package sys_config import ( "fmt" "math/rand" "time" "91porn-server/app/appg" "91porn-server/app/proto" "91porn-server/common" "91porn-server/common/constant/redisconst" "91porn-server/common/log" "91porn-server/models/v/sysconfmod" "91porn-server/models/v/usermod" "github.com/vmihailenco/msgpack/v5" ) // mapUserPaymentStatusPopup 用户付费状态分层 → 需要拉取的配置项集合 var mapUserPaymentStatusPopup = map[string]map[sysconfmod.VCode]bool{ usermod.UserPaymentStatusPopupNewUnpay: { sysconfmod.VCodeUserPaymentStatusPopupNewUserPopup: true, sysconfmod.VCodeUserPaymentStatusPopupNewUserFloating: true, sysconfmod.VCodeUserPaymentStatusPopupNewUserPlayPage: true, sysconfmod.VCodeUserPaymentStatusPopupNewUserMeTab: true, sysconfmod.VCodeUserPaymentStatusPopupNewUserVipCardID: true, sysconfmod.VCodeUserPaymentStatusPopupNewUserCountdown: true, }, usermod.UserPaymentStatusPopupUnder7DayUnpay: { sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPopup: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayFloating: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPlayPage: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayMeTab: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayVipCardID: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayCountdown: true, }, usermod.UserPaymentStatusPopupUnder7DayUnpayNoCountdown: { sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPopupNoCountdown: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayFloatingNoCountdown: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPlayPageNoCountdown: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayMeTabNoCountdown: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayVipCardID: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayCountdown: true, }, usermod.UserPaymentStatusPopupOver7DayUnpay: { sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayPopup: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayFloating: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayPlayPage: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayMeTab: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayVipCardID: true, sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayCountdown: true, }, usermod.UserPaymentStatusPopupOver7DayNeedUpgrade: { sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradePopup: true, sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeFloating: true, sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradePlayPage: true, sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeMeTab: true, sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeVipCardID: true, sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeCountdown: true, }, usermod.UserPaymentStatusPopupMaxVIPLevel: { sysconfmod.VCodeUserPaymentStatusPopupMaxVIPLevel: true, }, } // SysConfUserPaymentStatusPopup 根据用户付费状态分层,返回对应的分层弹窗配置与分层标识。 // 结果按分层做 1-3 分钟随机时长的 Redis 缓存。 func SysConfUserPaymentStatusPopup(user *usermod.User) (res proto.PaymentStatusPopupConfig, userType string, err error) { if user == nil { return } userType = user.GetPaymentStatusPopup() userCreateTime := user.CreatedAt vCodes, ok := mapUserPaymentStatusPopup[userType] if !ok { return } // 缓存键使用原始分层标识:有倒计时/无倒计时两个分层的配置项不同,必须分开缓存, // 否则会互相覆盖,导致下发错误的图片源。 cacheKey := fmt.Sprintf("%s:%s", redisconst.UserPaymentStatusPopupCache, userType) // 读缓存 str, err := appg.Redis.Get(cacheKey) if err != nil { log.Warn(fmt.Sprintf("获取用户分层弹窗缓存数据异常:%v", err)) } if str != nil { if err = msgpack.Unmarshal([]byte(*str), &res); err == nil { // 缓存按分层(userType)共享、不含 uid,图片配置可共享;但 LastDiscountTime // 是 per-user 动态值(依赖当前用户注册时间),必须基于当前用户重算, // 否则会串用到同分层其他用户的时刻。 if !res.LastDiscountTime.IsZero() { res.LastDiscountTime = userCreateTime.AddDate(0, 0, countdownDaysOf(userType)).Local() } userType = normalizePaymentStatusPopupType(userType) return } log.Warn(fmt.Sprintf("解析用户分层弹窗缓存数据异常:%v", err)) } vCodesKey := make([]sysconfmod.VCode, 0, len(vCodes)) for vCode := range vCodes { vCodesKey = append(vCodesKey, vCode) } data, err := sysconfmod.BatchGetInfoByVCode(vCodesKey) if err != nil { log.Error("获取用户分层弹窗数据失败!", log.E(err)) res = proto.PaymentStatusPopupConfig{} // 兜底:返回空配置 } else { // 使用两层 else 是为了让兜底方案也能被缓存,避免故障时每次请求都访问数据库 for _, d := range data { switch d.VCode { case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserPopup), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPopup), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPopupNoCountdown), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayPopup), string(sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradePopup): res.Homepage = d.Value case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserFloating), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayFloating), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayFloatingNoCountdown), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayFloating), string(sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeFloating): res.HomepageFlot = d.Value case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserPlayPage), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPlayPage), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayPlayPageNoCountdown), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayPlayPage), string(sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradePlayPage): res.PlayPage = d.Value case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserMeTab), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayMeTab), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayMeTabNoCountdown), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayMeTab), string(sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeMeTab), string(sysconfmod.VCodeUserPaymentStatusPopupMaxVIPLevel): res.MeTab = d.Value case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserVipCardID), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayVipCardID), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayOver7dayVipCardID), string(sysconfmod.VCodeUserPaymentStatusPopupPayNeedUpgradeVipCardID): res.VipCard = d.Value case string(sysconfmod.VCodeUserPaymentStatusPopupNewUserCountdown), string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayCountdown): if d.VCode == string(sysconfmod.VCodeUserPaymentStatusPopupNewUserCountdown) && d.Value == "1" { res.LastDiscountTime = userCreateTime.AddDate(0, 0, 1).Local() } if d.VCode == string(sysconfmod.VCodeUserPaymentStatusPopupUnpayUnder7dayCountdown) && d.Value == "1" { res.LastDiscountTime = userCreateTime.AddDate(0, 0, 2).Local() } } } } common.Go(func() { // 加入缓存,随机 60-180 秒,避免缓存集中失效 random := rand.Intn(120) + 60 b, mErr := msgpack.Marshal(res) if mErr != nil { return } if sErr := appg.Redis.Set(cacheKey, b, time.Duration(random)*time.Second); sErr != nil { log.Warn(fmt.Sprintf("设置用户分层弹窗缓存数据异常:%v", sErr)) } }) userType = normalizePaymentStatusPopupType(userType) return } // normalizePaymentStatusPopupType 统一对外的分层标识:无倒计时仅作内部区分,对外并入 under_7_day_unpay。 // 前端通过 LastDiscountTime 是否为零值来判断是否展示倒计时。 func normalizePaymentStatusPopupType(userType string) string { if userType == usermod.UserPaymentStatusPopupUnder7DayUnpayNoCountdown { return usermod.UserPaymentStatusPopupUnder7DayUnpay } return userType } // countdownDaysOf 返回该分层倒计时天数(用于命中缓存时按当前用户重算 LastDiscountTime): // 新用户 1 天,7天内未付款(含无倒计时变体) 2 天,其余分层无倒计时。 // 需与生成路径 AddDate 的天数保持一致。 func countdownDaysOf(userType string) int { switch userType { case usermod.UserPaymentStatusPopupNewUnpay: return 1 case usermod.UserPaymentStatusPopupUnder7DayUnpay, usermod.UserPaymentStatusPopupUnder7DayUnpayNoCountdown: return 2 } return 0 }