package invite import ( "chatapp3-golang/internal/model" "context" "net/url" "sort" "time" ) // GetHome 返回邀请活动首页所需的配置、任务、月度进度和累计统计。 func (s *InviteService) GetHome(ctx context.Context, user AuthUser) (*HomeResponse, error) { bundle, err := s.loadBundle(ctx, user.SysOrigin) if err != nil { return nil, err } inviteCode, err := s.ensureInviteCode(ctx, user) if err != nil { return nil, err } // 首页需要同时返回当前月进度、累计统计、邀请码和任务配置。 now := time.Now() timezone := normalizeTimezone(bundle.Config.Timezone) monthKey := monthKeyAt(now, timezone) periodEndAt, secondsToReset := monthResetInfoAt(now, timezone) monthly, err := s.findMonthlyProgress(ctx, user.SysOrigin, user.UserID, monthKey) if err != nil { return nil, err } stats, err := s.loadStats(ctx, user.SysOrigin, user.UserID) if err != nil { return nil, err } claims, err := s.loadClaimedRuleIDs(ctx, user.SysOrigin, user.UserID, monthKey) if err != nil { return nil, err } inviterBindRule := firstRule(bundle.Rules, ruleTypeInviterBind) inviteeBindRule := firstRule(bundle.Rules, ruleTypeInviteeBind) inviteeRechargeRules := rulesByType(bundle.Rules, ruleTypeInviteeRecharge) validCountRules := rulesByType(bundle.Rules, ruleTypeInviterValidCount) totalRechargeRules := rulesByType(bundle.Rules, ruleTypeInviterTotalRecharge) resp := &HomeResponse{ CampaignEnabled: bundle.Config.Enabled, SysOrigin: user.SysOrigin, InviteCode: inviteCode, InviteLink: s.cfg.Invite.PublicBaseURL + "/public/h5/invite-campaign/landing?code=" + url.QueryEscape(inviteCode), ShareTitle: defaultIfBlank(bundle.Config.ShareTitle, defaultShareTitle), ShareDesc: defaultIfBlank(bundle.Config.ShareDesc, defaultShareDesc), DownloadURL: bundle.Config.DownloadURL, ServiceContact: bundle.Config.ServiceContact, Timezone: timezone, MonthKey: monthKey, ServerTimeMs: now.UnixMilli(), PeriodEndAtMs: periodEndAt.UnixMilli(), SecondsToReset: secondsToReset, ValidUserThreshold: effectiveValidThreshold(inviteeRechargeRules), InviterBindReward: rewardFromRule(inviterBindRule), InviteeBindReward: rewardFromRule(inviteeBindRule), InviteeRecharge: rewardsFromRules(inviteeRechargeRules), Stats: stats, } resp.MonthlyProgress.InviteCount = monthly.InviteCount resp.MonthlyProgress.ValidUserCount = monthly.ValidUserCount resp.MonthlyProgress.TotalRecharge = monthly.TotalRecharge resp.MonthlyProgress.RewardGoldCoins = monthly.RewardGoldCoins resp.ValidCountTasks = buildTaskViews(validCountRules, monthly.InviteCount, claims) resp.TotalRechargeTasks = buildTaskViews(totalRechargeRules, monthly.TotalRecharge, claims) return resp, nil } // buildTaskViews 根据规则、进度和领取状态生成任务展示列表。 func buildTaskViews(rules []model.InviteCampaignRewardRule, progress int64, claimed map[int64]bool) []TaskView { result := make([]TaskView, 0, len(rules)) for _, rule := range rules { if !rule.Enabled { continue } result = append(result, TaskView{ RuleID: rule.ID, RuleType: rule.RuleType, Threshold: rule.ThresholdValue, Progress: progress, GoldAmount: rule.GoldAmount, RewardGroupID: rule.RewardGroupID, Achieved: progress >= rule.ThresholdValue, Claimed: claimed[rule.ID], }) } return result } // firstRule 返回某类规则里的第一条规则。 func firstRule(rules []model.InviteCampaignRewardRule, ruleType string) *model.InviteCampaignRewardRule { for _, item := range rules { if item.RuleType == ruleType && item.Enabled { rule := item return &rule } } return nil } // rulesByType 返回某类规则的有序列表。 func rulesByType(rules []model.InviteCampaignRewardRule, ruleType string) []model.InviteCampaignRewardRule { result := make([]model.InviteCampaignRewardRule, 0) for _, item := range rules { if item.RuleType == ruleType && item.Enabled { result = append(result, item) } } sort.SliceStable(result, func(i, j int) bool { if result[i].Sort == result[j].Sort { if result[i].ThresholdValue == result[j].ThresholdValue { return result[i].ID < result[j].ID } return result[i].ThresholdValue < result[j].ThresholdValue } return result[i].Sort < result[j].Sort }) return result } // findRuleByID 按规则 ID 从规则列表中查找规则。 func findRuleByID(rules []model.InviteCampaignRewardRule, ruleID int64) *model.InviteCampaignRewardRule { for _, item := range rules { if item.ID == ruleID { rule := item return &rule } } return nil } // rewardFromRule 把规则转换成对外奖励视图。 func rewardFromRule(rule *model.InviteCampaignRewardRule) RewardView { if rule == nil { return RewardView{} } return RewardView{ RuleID: rule.ID, RuleType: rule.RuleType, Threshold: rule.ThresholdValue, GoldAmount: rule.GoldAmount, RewardGroupID: rule.RewardGroupID, } } // rewardsFromRules 把规则列表转换成奖励视图列表。 func rewardsFromRules(rules []model.InviteCampaignRewardRule) []RewardView { result := make([]RewardView, 0, len(rules)) for _, item := range rules { result = append(result, rewardFromRule(&item)) } return result } // effectiveValidThreshold 计算“有效用户”判定使用的最低充值门槛。 func effectiveValidThreshold(rules []model.InviteCampaignRewardRule) int64 { var threshold int64 for _, rule := range rules { if !rule.Enabled || rule.ThresholdValue <= 0 { continue } if threshold == 0 || rule.ThresholdValue < threshold { threshold = rule.ThresholdValue } } if threshold == 0 { return defaultValidRechargeCoins } return threshold }