2026-06-12 12:08:01 +08:00

351 lines
12 KiB
Go

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
}