Files
rootandClaude Opus 5 8679200f41 Initial commit
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-15 13:57:10 +08:00

193 lines
8.7 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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
}