Initial commit

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 13:57:10 +08:00
co-authored by Claude Opus 5
commit 8679200f41
1897 changed files with 257900 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
package sys_config
import (
"91porn-server/models/cache/sysconfdata"
)
// GetTotalWatchCount 读取系统配置 `totalWatchCount`,若未配置则返回默认值。
func GetTotalWatchCount() uint64 {
return sysconfdata.GetTotalWatchCount()
}
+192
View File
@@ -0,0 +1,192 @@
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
}