package agencyopening 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"` StartMS int64 `json:"start_ms"` EndMS int64 `json:"end_ms"` MinHostCount int32 `json:"min_host_count"` MaxAgencyAgeDays int32 `json:"max_agency_age_days"` Rewards []rewardDTO `json:"rewards"` } 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"` Title string `json:"title"` Status string `json:"status"` StartMS int64 `json:"start_ms"` EndMS int64 `json:"end_ms"` MinHostCount int32 `json:"min_host_count"` MaxAgencyAgeDays int32 `json:"max_agency_age_days"` 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 rewardDTO struct { RankNo int32 `json:"rank_no"` TierNo int32 `json:"tier_no,omitempty"` ThresholdCoinSpent int64 `json:"threshold_coin_spent"` RewardCoinAmount int64 `json:"reward_coin_amount"` } type applicationDTO struct { ApplicationID string `json:"application_id"` CycleID string `json:"cycle_id"` AgencyID int64 `json:"agency_id,string"` AgencyOwnerUserID int64 `json:"agency_owner_user_id,string"` AgencyName string `json:"agency_name"` HostCount int32 `json:"host_count"` AgencyCreatedAtMS int64 `json:"agency_created_at_ms"` Status string `json:"status"` ScoreCoinAmount int64 `json:"score_coin_amount"` RewardRankNo int32 `json:"reward_rank_no"` RewardThresholdCoin int64 `json:"reward_threshold_coin_spent"` RewardCoinAmount int64 `json:"reward_coin_amount"` WalletCommandID string `json:"wallet_command_id"` WalletTransactionID string `json:"wallet_transaction_id"` FailureReason string `json:"failure_reason"` AppliedAtMS int64 `json:"applied_at_ms"` ApprovedAtMS int64 `json:"approved_at_ms"` ScoreStartMS int64 `json:"score_start_ms"` ScoreEndMS int64 `json:"score_end_ms"` ReviewedByAdminID int64 `json:"reviewed_by_admin_id"` GrantedAtMS int64 `json:"granted_at_ms"` UpdatedAtMS int64 `json:"updated_at_ms"` } func (h *Handler) ListCycles(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.activity.ListAgencyOpeningCycles(c.Request.Context(), &activityv1.ListAgencyOpeningCyclesRequest{ Meta: h.meta(c), Status: strings.TrimSpace(options.Status), StartMs: parseInt64Query(c, "start_ms"), EndMs: parseInt64Query(c, "end_ms"), 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.CreateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{ 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-agency-opening-cycle", "agency_opening_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.GetAgencyOpeningCycle(c.Request.Context(), &activityv1.GetAgencyOpeningCycleRequest{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.UpdateAgencyOpeningCycle(c.Request.Context(), &activityv1.UpsertAgencyOpeningCycleRequest{ 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-agency-opening-cycle", "agency_opening_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.SetAgencyOpeningCycleStatus(c.Request.Context(), &activityv1.SetAgencyOpeningCycleStatusRequest{ 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-agency-opening-cycle-status", "agency_opening_cycles", item.CycleID, "success", "") response.OK(c, item) } func (h *Handler) ListApplications(c *gin.Context) { options := shared.ListOptions(c) resp, err := h.activity.ListAgencyOpeningApplications(c.Request.Context(), &activityv1.ListAgencyOpeningApplicationsRequest{ Meta: h.meta(c), CycleId: strings.TrimSpace(c.Query("cycle_id")), Status: strings.TrimSpace(options.Status), AgencyId: parseInt64Query(c, "agency_id"), AgencyOwnerUserId: parseInt64Query(c, "agency_owner_user_id"), Page: int32(options.Page), PageSize: int32(options.PageSize), }) if err != nil { response.ServerError(c, "获取代理开业申请记录失败") return } items := make([]applicationDTO, 0, len(resp.GetApplications())) for _, application := range resp.GetApplications() { items = append(items, applicationFromProto(application)) } response.OK(c, response.Page{Items: items, Page: options.Page, PageSize: options.PageSize, Total: resp.GetTotal()}) } func (h *Handler) ApproveApplication(c *gin.Context) { applicationID := strings.TrimSpace(c.Param("application_id")) resp, err := h.activity.ReviewAgencyOpeningApplication(c.Request.Context(), &activityv1.ReviewAgencyOpeningApplicationRequest{ Meta: h.meta(c), ApplicationId: applicationID, Action: "approve", OperatorAdminId: int64(middleware.CurrentUserID(c)), }) if err != nil { response.BadRequest(c, err.Error()) return } item := applicationFromProto(resp.GetApplication()) shared.OperationLogWithResourceID(c, h.audit, "approve-agency-opening-application", "agency_opening_applications", item.ApplicationID, "success", "") response.OK(c, item) } 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.AgencyOpeningCycle { rewards := make([]*activityv1.AgencyOpeningReward, 0, len(r.Rewards)) for _, reward := range r.Rewards { rankNo := reward.RankNo if rankNo <= 0 { rankNo = reward.TierNo } rewards = append(rewards, &activityv1.AgencyOpeningReward{ RankNo: rankNo, ThresholdCoinSpent: reward.ThresholdCoinSpent, RewardCoinAmount: reward.RewardCoinAmount, }) } return &activityv1.AgencyOpeningCycle{ CycleId: strings.TrimSpace(r.CycleID), Title: strings.TrimSpace(r.Title), Status: strings.TrimSpace(r.Status), StartMs: r.StartMS, EndMs: r.EndMS, MinHostCount: r.MinHostCount, MaxAgencyAgeDays: r.MaxAgencyAgeDays, Rewards: rewards, } } func cycleFromProto(cycle *activityv1.AgencyOpeningCycle) cycleDTO { if cycle == nil { return cycleDTO{Rewards: []rewardDTO{}} } rewards := make([]rewardDTO, 0, len(cycle.GetRewards())) for _, reward := range cycle.GetRewards() { rewards = append(rewards, rewardDTO{ RankNo: reward.GetRankNo(), TierNo: reward.GetRankNo(), ThresholdCoinSpent: reward.GetThresholdCoinSpent(), RewardCoinAmount: reward.GetRewardCoinAmount(), }) } return cycleDTO{ AppCode: cycle.GetAppCode(), CycleID: cycle.GetCycleId(), ActivityCode: cycle.GetActivityCode(), Title: cycle.GetTitle(), Status: cycle.GetStatus(), StartMS: cycle.GetStartMs(), EndMS: cycle.GetEndMs(), MinHostCount: cycle.GetMinHostCount(), MaxAgencyAgeDays: cycle.GetMaxAgencyAgeDays(), Rewards: rewards, CreatedByAdminID: cycle.GetCreatedByAdminId(), UpdatedByAdminID: cycle.GetUpdatedByAdminId(), SettledAtMS: cycle.GetSettledAtMs(), CreatedAtMS: cycle.GetCreatedAtMs(), UpdatedAtMS: cycle.GetUpdatedAtMs(), } } func applicationFromProto(item *activityv1.AgencyOpeningApplication) applicationDTO { if item == nil { return applicationDTO{} } return applicationDTO{ ApplicationID: item.GetApplicationId(), CycleID: item.GetCycleId(), AgencyID: item.GetAgencyId(), AgencyOwnerUserID: item.GetAgencyOwnerUserId(), AgencyName: item.GetAgencyName(), HostCount: item.GetHostCount(), AgencyCreatedAtMS: item.GetAgencyCreatedAtMs(), Status: item.GetStatus(), ScoreCoinAmount: item.GetScoreCoinAmount(), RewardRankNo: item.GetRewardRankNo(), RewardThresholdCoin: item.GetRewardThresholdCoinSpent(), RewardCoinAmount: item.GetRewardCoinAmount(), WalletCommandID: item.GetWalletCommandId(), WalletTransactionID: item.GetWalletTransactionId(), FailureReason: item.GetFailureReason(), AppliedAtMS: item.GetAppliedAtMs(), ApprovedAtMS: item.GetApprovedAtMs(), ScoreStartMS: item.GetScoreStartMs(), ScoreEndMS: item.GetScoreEndMs(), ReviewedByAdminID: item.GetReviewedByAdminId(), GrantedAtMS: item.GetGrantedAtMs(), UpdatedAtMS: item.GetUpdatedAtMs(), } } func parseInt64Query(c *gin.Context, key string) int64 { value, _ := strconv.ParseInt(strings.TrimSpace(c.Query(key)), 10, 64) return value }