修复个人中心问题
This commit is contained in:
@@ -5,6 +5,7 @@ import (
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"server/models"
|
||||
@@ -151,6 +152,75 @@ func NormalizeModuleCode(code string) string {
|
||||
return strings.ToLower(strings.TrimSpace(code))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- 模块权益鉴权
|
||||
|
||||
// moduleCodesCacheTTL 模块权益缓存时长。
|
||||
// 模块鉴权中间件会在每个业务请求上判断开通状态,直查数据库代价太高;
|
||||
// 60 秒延迟对「购买后立即可用」没有实际影响(前端刷新即可)。
|
||||
const moduleCodesCacheTTL = 60 * time.Second
|
||||
|
||||
type tenantModuleCodesItem struct {
|
||||
codes map[string]bool
|
||||
expireAt time.Time
|
||||
}
|
||||
|
||||
var (
|
||||
tenantModuleCodesMu sync.RWMutex
|
||||
tenantModuleCodesCache = map[uint64]tenantModuleCodesItem{}
|
||||
)
|
||||
|
||||
// GetTenantModuleCodesCached 带进程内缓存的模块权益查询(供请求级鉴权使用)
|
||||
func GetTenantModuleCodesCached(tid uint64) map[string]bool {
|
||||
now := time.Now()
|
||||
tenantModuleCodesMu.RLock()
|
||||
item, ok := tenantModuleCodesCache[tid]
|
||||
tenantModuleCodesMu.RUnlock()
|
||||
if ok && now.Before(item.expireAt) {
|
||||
return item.codes
|
||||
}
|
||||
|
||||
codes := GetTenantModuleCodes(tid)
|
||||
|
||||
tenantModuleCodesMu.Lock()
|
||||
// 顺带清理过期项,避免长期运行后内存堆积
|
||||
if len(tenantModuleCodesCache) > 5000 {
|
||||
for k, v := range tenantModuleCodesCache {
|
||||
if now.After(v.expireAt) {
|
||||
delete(tenantModuleCodesCache, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
tenantModuleCodesCache[tid] = tenantModuleCodesItem{codes: codes, expireAt: now.Add(moduleCodesCacheTTL)}
|
||||
tenantModuleCodesMu.Unlock()
|
||||
return codes
|
||||
}
|
||||
|
||||
// InvalidateTenantModuleCodes 清除某租户的模块权益缓存(套餐 / 订单变更后调用)
|
||||
func InvalidateTenantModuleCodes(tid uint64) {
|
||||
if tid == 0 {
|
||||
return
|
||||
}
|
||||
tenantModuleCodesMu.Lock()
|
||||
delete(tenantModuleCodesCache, tid)
|
||||
tenantModuleCodesMu.Unlock()
|
||||
}
|
||||
|
||||
// IsModuleEnabled 判断租户是否已开通指定功能模块。
|
||||
//
|
||||
// 口径与 /backend/modules/getTenantList 保持一致:租户没有任何模块权益时视为「全开」,
|
||||
// 避免历史租户或未配置套餐的环境被一刀切拦截。
|
||||
func IsModuleEnabled(tid uint64, moduleCode string) bool {
|
||||
code := NormalizeModuleCode(moduleCode)
|
||||
if code == "" || tid == 0 {
|
||||
return true
|
||||
}
|
||||
codes := GetTenantModuleCodesCached(tid)
|
||||
if len(codes) == 0 {
|
||||
return true
|
||||
}
|
||||
return codes[code]
|
||||
}
|
||||
|
||||
// DayStart 取时间所在自然日的 0 点(有效期按「日」计算,不精确到秒)
|
||||
func DayStart(t time.Time) time.Time {
|
||||
return time.Date(t.Year(), t.Month(), t.Day(), 0, 0, 0, 0, t.Location())
|
||||
@@ -277,9 +347,10 @@ func CheckTenantUserQuota(tid, uid uint64) (*TenantQuotaCheck, error) {
|
||||
result.Quota = quota
|
||||
|
||||
if uid > 0 {
|
||||
// uid 可能来自令牌(identity_id),查老表前先换算
|
||||
exist, err := models.Orm.QueryTable(new(models.SystemTenantUser)).
|
||||
Filter("tid", tid).
|
||||
Filter("uid", uid).
|
||||
Filter("uid", ResolveTenantUID(tid, uid)).
|
||||
Filter("delete_time__isnull", true).
|
||||
Count()
|
||||
if err == nil && exist > 0 {
|
||||
@@ -442,6 +513,8 @@ func ApplyPackageToTenant(tid, packageID uint64, syncQuota bool) (int, error) {
|
||||
Update(update); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
// 套餐变更后清掉模块权益缓存,避免「刚换套餐仍被拦/仍在放行」的延迟
|
||||
InvalidateTenantModuleCodes(tid)
|
||||
return newQuota, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user