package weeklystar import ( "strconv" "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 cycleRequest struct { CycleID string `json:"cycle_id"` Title string `json:"title"` Status string `json:"status"` RegionID int64 `json:"region_id"` StartMS int64 `json:"start_ms"` EndMS int64 `json:"end_ms"` GiftIDs []string `json:"gift_ids"` Gifts []giftDTO `json:"gifts"` Rewards []rewardDTO `json:"rewards"` Top1ResourceGroupID int64 `json:"top1_resource_group_id"` Top2ResourceGroupID int64 `json:"top2_resource_group_id"` Top3ResourceGroupID int64 `json:"top3_resource_group_id"` } type statusRequest struct { Status string `json:"status"` } type cycleDTO struct { AppCode string `json:"app_code,omitempty"` CycleID string `json:"cycle_id"` ActivityCode string `json:"activity_code"` RegionID int64 `json:"region_id"` Title string `json:"title"` Status string `json:"status"` StartMS int64 `json:"start_ms"` EndMS int64 `json:"end_ms"` Gifts []giftDTO `json:"gifts"` Rewards []rewardDTO `json:"rewards"` CreatedByAdminID int64 `json:"created_by_admin_id"` UpdatedByAdminID int64 `json:"updated_by_admin_id"` SettledAtMS int64 `json:"settled_at_ms"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } type giftDTO struct { GiftID string `json:"gift_id"` SortOrder int32 `json:"sort_order"` } type rewardDTO struct { RankNo int32 `json:"rank_no"` ResourceGroupID int64 `json:"resource_group_id"` } type leaderboardEntryDTO struct { RankNo int32 `json:"rank_no"` UserID int64 `json:"user_id,string"` Score int64 `json:"score"` FirstScoredAtMS int64 `json:"first_scored_at_ms"` LastScoredAtMS int64 `json:"last_scored_at_ms"` DisplayUserID string `json:"display_user_id,omitempty"` Username string `json:"username,omitempty"` Avatar string `json:"avatar,omitempty"` } type settlementDTO struct { SettlementID string `json:"settlement_id"` CycleID string `json:"cycle_id"` RankNo int32 `json:"rank_no"` UserID int64 `json:"user_id,string"` Score int64 `json:"score"` ResourceGroupID int64 `json:"resource_group_id"` WalletCommandID string `json:"wallet_command_id"` WalletGrantID string `json:"wallet_grant_id"` Status string `json:"status"` FailureReason string `json:"failure_reason"` AttemptCount int32 `json:"attempt_count"` CreatedAtMS int64 `json:"created_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } func (h *Handler) ListCycles(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.activity.ListWeeklyStarCycles(c.Request.Context(), &activityv1.ListWeeklyStarCyclesRequest{ Meta: h.meta(c), Status: strings.TrimSpace(options.Status), StartMs: parseInt64Query(c, "start_ms"), EndMs: parseInt64Query(c, "end_ms"), RegionId: parseInt64Query(c, "region_id"), Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取周星周期失败") return } items := make([]cycleDTO, 0, len(resp.GetCycles())) for _, cycle := range resp.GetCycles() { items = append(items, cycleFromProto(cycle)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) CreateCycle(c *gin.Context) { var req cycleRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "周星周期参数不正确") return } resp, err := h.activity.CreateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{ Meta: h.meta(c), Cycle: req.toProto(), OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := cycleFromProto(resp.GetCycle()) shared.OperationLogWithResourceID(c, h.audit, "create-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "") response.OK(c, item) } func (h *Handler) GetCycle(c *gin.Context) { cycleID := strings.TrimSpace(c.Param("cycle_id")) resp, err := h.activity.GetWeeklyStarCycle(c.Request.Context(), &activityv1.GetWeeklyStarCycleRequest{Meta: h.meta(c), CycleId: cycleID}) if err != nil { response.ServerError(c, "获取周星周期详情失败") return } response.OK(c, cycleFromProto(resp.GetCycle())) } func (h *Handler) UpdateCycle(c *gin.Context) { var req cycleRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "周星周期参数不正确") return } req.CycleID = strings.TrimSpace(c.Param("cycle_id")) resp, err := h.activity.UpdateWeeklyStarCycle(c.Request.Context(), &activityv1.UpsertWeeklyStarCycleRequest{ Meta: h.meta(c), Cycle: req.toProto(), OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := cycleFromProto(resp.GetCycle()) shared.OperationLogWithResourceID(c, h.audit, "update-weekly-star-cycle", "weekly_star_cycles", item.CycleID, "success", "") response.OK(c, item) } func (h *Handler) SetCycleStatus(c *gin.Context) { var req statusRequest if err := c.ShouldBindJSON(&req); err != nil { response.BadRequest(c, "周星周期状态参数不正确") return } cycleID := strings.TrimSpace(c.Param("cycle_id")) resp, err := h.activity.SetWeeklyStarCycleStatus(c.Request.Context(), &activityv1.SetWeeklyStarCycleStatusRequest{ Meta: h.meta(c), CycleId: cycleID, Status: strings.TrimSpace(req.Status), OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := cycleFromProto(resp.GetCycle()) shared.OperationLogWithResourceID(c, h.audit, "set-weekly-star-cycle-status", "weekly_star_cycles", item.CycleID, "success", "") response.OK(c, item) } func (h *Handler) ListLeaderboard(c *gin.Context) { resp, err := h.activity.ListWeeklyStarLeaderboard(c.Request.Context(), &activityv1.ListWeeklyStarLeaderboardRequest{ Meta: h.meta(c), CycleId: strings.TrimSpace(c.Param("cycle_id")), PageSize: int32(parseIntQuery(c, "page_size", 50)), PageToken: strings.TrimSpace(c.Query("page_token")), }) if err != nil { response.ServerError(c, "获取周星排行榜失败") return } items := make([]leaderboardEntryDTO, 0, len(resp.GetEntries())) for _, entry := range resp.GetEntries() { items = append(items, leaderboardEntryFromProto(entry)) } response.OK(c, gin.H{"cycle": cycleFromProto(resp.GetCycle()), "items": items, "total": resp.GetTotal(), "next_page_token": resp.GetNextPageToken()}) } func (h *Handler) ListSettlements(c *gin.Context) { resp, err := h.activity.ListWeeklyStarSettlements(c.Request.Context(), &activityv1.ListWeeklyStarSettlementsRequest{ Meta: h.meta(c), CycleId: strings.TrimSpace(c.Param("cycle_id")), }) if err != nil { response.ServerError(c, "获取周星结算记录失败") return } items := make([]settlementDTO, 0, len(resp.GetSettlements())) for _, settlement := range resp.GetSettlements() { items = append(items, settlementFromProto(settlement)) } response.OK(c, items) } 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 (r cycleRequest) toProto() *activityv1.WeeklyStarCycle { gifts := make([]*activityv1.WeeklyStarGift, 0, 3) if len(r.Gifts) > 0 { for _, gift := range r.Gifts { gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(gift.GiftID), SortOrder: gift.SortOrder}) } } else { for index, giftID := range r.GiftIDs { gifts = append(gifts, &activityv1.WeeklyStarGift{GiftId: strings.TrimSpace(giftID), SortOrder: int32(index + 1)}) } } rewards := make([]*activityv1.WeeklyStarReward, 0, 3) if len(r.Rewards) > 0 { for _, reward := range r.Rewards { rewards = append(rewards, &activityv1.WeeklyStarReward{RankNo: reward.RankNo, ResourceGroupId: reward.ResourceGroupID}) } } else { rewards = append(rewards, &activityv1.WeeklyStarReward{RankNo: 1, ResourceGroupId: r.Top1ResourceGroupID}, &activityv1.WeeklyStarReward{RankNo: 2, ResourceGroupId: r.Top2ResourceGroupID}, &activityv1.WeeklyStarReward{RankNo: 3, ResourceGroupId: r.Top3ResourceGroupID}, ) } return &activityv1.WeeklyStarCycle{ CycleId: strings.TrimSpace(r.CycleID), RegionId: r.RegionID, Title: strings.TrimSpace(r.Title), Status: strings.TrimSpace(r.Status), StartMs: r.StartMS, EndMs: r.EndMS, Gifts: gifts, Rewards: rewards, } } func cycleFromProto(cycle *activityv1.WeeklyStarCycle) cycleDTO { if cycle == nil { return cycleDTO{Gifts: []giftDTO{}, Rewards: []rewardDTO{}} } gifts := make([]giftDTO, 0, len(cycle.GetGifts())) for _, gift := range cycle.GetGifts() { gifts = append(gifts, giftDTO{GiftID: gift.GetGiftId(), SortOrder: gift.GetSortOrder()}) } rewards := make([]rewardDTO, 0, len(cycle.GetRewards())) for _, reward := range cycle.GetRewards() { rewards = append(rewards, rewardDTO{RankNo: reward.GetRankNo(), ResourceGroupID: reward.GetResourceGroupId()}) } return cycleDTO{ AppCode: cycle.GetAppCode(), CycleID: cycle.GetCycleId(), ActivityCode: cycle.GetActivityCode(), RegionID: cycle.GetRegionId(), Title: cycle.GetTitle(), Status: cycle.GetStatus(), StartMS: cycle.GetStartMs(), EndMS: cycle.GetEndMs(), Gifts: gifts, Rewards: rewards, CreatedByAdminID: cycle.GetCreatedByAdminId(), UpdatedByAdminID: cycle.GetUpdatedByAdminId(), SettledAtMS: cycle.GetSettledAtMs(), CreatedAtMS: cycle.GetCreatedAtMs(), UpdatedAtMS: cycle.GetUpdatedAtMs(), } } func leaderboardEntryFromProto(entry *activityv1.WeeklyStarLeaderboardEntry) leaderboardEntryDTO { if entry == nil { return leaderboardEntryDTO{} } return leaderboardEntryDTO{ RankNo: entry.GetRankNo(), UserID: entry.GetUserId(), Score: entry.GetScore(), FirstScoredAtMS: entry.GetFirstScoredAtMs(), LastScoredAtMS: entry.GetLastScoredAtMs(), } } func settlementFromProto(settlement *activityv1.WeeklyStarSettlement) settlementDTO { if settlement == nil { return settlementDTO{} } return settlementDTO{ SettlementID: settlement.GetSettlementId(), CycleID: settlement.GetCycleId(), RankNo: settlement.GetRankNo(), UserID: settlement.GetUserId(), Score: settlement.GetScore(), ResourceGroupID: settlement.GetResourceGroupId(), WalletCommandID: settlement.GetWalletCommandId(), WalletGrantID: settlement.GetWalletGrantId(), Status: settlement.GetStatus(), FailureReason: settlement.GetFailureReason(), AttemptCount: settlement.GetAttemptCount(), CreatedAtMS: settlement.GetCreatedAtMs(), UpdatedAtMS: settlement.GetUpdatedAtMs(), } } func parseInt64Query(c *gin.Context, key string) int64 { value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64) return value } func parseIntQuery(c *gin.Context, key string, fallback int) int { value, err := strconv.Atoi(strings.TrimSpace(c.Query(key))) if err != nil || value <= 0 { return fallback } return value }