2026-06-18 14:19:47 +08:00

325 lines
12 KiB
Go

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
}