package dailytask import ( "strings" "time" "hyapp-admin-server/internal/appctx" "hyapp-admin-server/internal/integration/activityclient" "hyapp-admin-server/internal/middleware" "hyapp-admin-server/internal/modules/shared" "hyapp-admin-server/internal/response" activityv1 "hyapp.local/api/proto/activity/v1" "github.com/gin-gonic/gin" ) type Handler struct { activity activityclient.Client audit shared.OperationLogger } func New(activity activityclient.Client, audit shared.OperationLogger) *Handler { return &Handler{activity: activity, audit: audit} } type taskRequest struct { TaskType string `json:"task_type"` Category string `json:"category"` MetricType string `json:"metric_type"` Title string `json:"title"` Description string `json:"description"` TargetValue int64 `json:"target_value"` TargetUnit string `json:"target_unit"` RewardCoinAmount int64 `json:"reward_coin_amount"` Status string `json:"status"` SortOrder int32 `json:"sort_order"` EffectiveFromMS int64 `json:"effective_from_ms"` EffectiveToMS int64 `json:"effective_to_ms"` } type statusRequest struct { Status string `json:"status"` } type taskDTO struct { TaskID string `json:"task_id"` TaskType string `json:"task_type"` Category string `json:"category"` MetricType string `json:"metric_type"` Title string `json:"title"` Description string `json:"description"` TargetValue int64 `json:"target_value"` TargetUnit string `json:"target_unit"` RewardCoinAmount int64 `json:"reward_coin_amount"` Status string `json:"status"` SortOrder int32 `json:"sort_order"` Version int64 `json:"version"` EffectiveFromMS int64 `json:"effective_from_ms"` EffectiveToMS int64 `json:"effective_to_ms"` CreatedByAdminID int64 `json:"created_by_admin_id"` UpdatedByAdminID int64 `json:"updated_by_admin_id"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } // ListTaskDefinitions 返回活动管理 > 每日任务页的 daily/exclusive 配置列表。 func (h *Handler) ListTaskDefinitions(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.activity.ListTaskDefinitions(c.Request.Context(), &activityv1.ListTaskDefinitionsRequest{ Meta: h.meta(c), TaskType: strings.TrimSpace(firstQuery(c, "task_type", "taskType")), Category: strings.TrimSpace(c.Query("category")), Status: options.Status, Keyword: options.Keyword, Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取任务列表失败") return } items := make([]taskDTO, 0, len(resp.GetTasks())) for _, item := range resp.GetTasks() { items = append(items, taskFromProto(item)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } // CreateTaskDefinition 创建任务配置;active 任务会立即被 App 查询看到。 func (h *Handler) CreateTaskDefinition(c *gin.Context) { var req taskRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "任务参数不正确") return } resp, err := h.activity.UpsertTaskDefinition(c.Request.Context(), req.toProto(c, "", h.meta(c))) if err != nil { response.BadRequest(c, err.Error()) return } item := taskFromProto(resp.GetTask()) h.auditLog(c, "create-daily-task", item.TaskID, item.Title) response.Created(c, item) } // UpdateTaskDefinition 编辑任务配置;关键字段变更由 activity-service 生成新版本。 func (h *Handler) UpdateTaskDefinition(c *gin.Context) { taskID := strings.TrimSpace(c.Param("task_id")) if taskID == "" { response.BadRequest(c, "ID 参数不正确") return } var req taskRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "任务参数不正确") return } resp, err := h.activity.UpsertTaskDefinition(c.Request.Context(), req.toProto(c, taskID, h.meta(c))) if err != nil { response.BadRequest(c, err.Error()) return } item := taskFromProto(resp.GetTask()) h.auditLog(c, "update-daily-task", item.TaskID, item.Title) response.OK(c, item) } // SetTaskDefinitionStatus 只做 draft/active/paused/archived 状态流转,不物理删除任务。 func (h *Handler) SetTaskDefinitionStatus(c *gin.Context) { taskID := strings.TrimSpace(c.Param("task_id")) if taskID == "" { response.BadRequest(c, "ID 参数不正确") return } var req statusRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "状态参数不正确") return } resp, err := h.activity.SetTaskDefinitionStatus(c.Request.Context(), &activityv1.SetTaskDefinitionStatusRequest{ Meta: h.meta(c), TaskId: taskID, Status: strings.TrimSpace(req.Status), OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := taskFromProto(resp.GetTask()) h.auditLog(c, "set-daily-task-status", item.TaskID, item.Status) response.OK(c, item) } func (r taskRequest) toProto(c *gin.Context, taskID string, meta *activityv1.RequestMeta) *activityv1.UpsertTaskDefinitionRequest { return &activityv1.UpsertTaskDefinitionRequest{ Meta: meta, TaskId: taskID, TaskType: strings.TrimSpace(r.TaskType), Category: strings.TrimSpace(r.Category), MetricType: strings.TrimSpace(r.MetricType), Title: strings.TrimSpace(r.Title), Description: strings.TrimSpace(r.Description), TargetValue: r.TargetValue, TargetUnit: strings.TrimSpace(r.TargetUnit), RewardCoinAmount: r.RewardCoinAmount, Status: strings.TrimSpace(r.Status), SortOrder: r.SortOrder, EffectiveFromMs: r.EffectiveFromMS, EffectiveToMs: r.EffectiveToMS, OperatorAdminId: int64(middleware.CurrentUserID(c)), } } func (h *Handler) meta(c *gin.Context) *activityv1.RequestMeta { return &activityv1.RequestMeta{ RequestId: middleware.CurrentRequestID(c), Caller: "admin-server", AppCode: appctx.FromContext(c.Request.Context()), SentAtMs: time.Now().UnixMilli(), } } func (h *Handler) auditLog(c *gin.Context, action string, taskID string, detail string) { if h.audit == nil { return } shared.OperationLogWithResourceID(c, h.audit, action, "task_definitions", taskID, "success", detail) } func taskFromProto(item *activityv1.TaskDefinition) taskDTO { if item == nil { return taskDTO{} } return taskDTO{ TaskID: item.GetTaskId(), TaskType: item.GetTaskType(), Category: item.GetCategory(), MetricType: item.GetMetricType(), Title: item.GetTitle(), Description: item.GetDescription(), TargetValue: item.GetTargetValue(), TargetUnit: item.GetTargetUnit(), RewardCoinAmount: item.GetRewardCoinAmount(), Status: item.GetStatus(), SortOrder: item.GetSortOrder(), Version: item.GetVersion(), EffectiveFromMS: item.GetEffectiveFromMs(), EffectiveToMS: item.GetEffectiveToMs(), CreatedByAdminID: item.GetCreatedByAdminId(), UpdatedByAdminID: item.GetUpdatedByAdminId(), CreatedAtMS: item.GetCreatedAtMs(), UpdatedAtMS: item.GetUpdatedAtMs(), } } func firstQuery(c *gin.Context, keys ...string) string { for _, key := range keys { if value := c.Query(key); value != "" { return value } } return "" }