2026-04-24 13:04:06 +08:00

177 lines
5.7 KiB
Go

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
}