2026-05-09 21:47:33 +08:00

225 lines
7.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 ""
}