403 lines
9.8 KiB
Go
403 lines
9.8 KiB
Go
package mid
|
||
|
||
//
|
||
//import (
|
||
// "context"
|
||
// "log"
|
||
// "net/http"
|
||
// "sync"
|
||
// "time"
|
||
//
|
||
// "management/internal/erpserver/model/dto"
|
||
// v1 "management/internal/erpserver/service/v1"
|
||
// "management/internal/pkg/know"
|
||
// "management/internal/pkg/session"
|
||
//)
|
||
//
|
||
//var publicRoutes = map[string]bool{
|
||
// "/home.html": true,
|
||
// "/dashboard": true,
|
||
// "/system/menus": true,
|
||
// "/upload/img": true,
|
||
// "/upload/file": true,
|
||
// "/upload/multi_files": true,
|
||
// "/pear.json": true,
|
||
// "/logout": true,
|
||
//}
|
||
//
|
||
//// MenuCacheItem 菜单缓存项
|
||
//type MenuCacheItem struct {
|
||
// Data map[string]*dto.OwnerMenuDto
|
||
// ExpireAt time.Time
|
||
// LoadTime time.Time
|
||
// Version int64
|
||
//}
|
||
//
|
||
//// IsExpired 检查是否过期
|
||
//func (item *MenuCacheItem) IsExpired() bool {
|
||
// return time.Now().After(item.ExpireAt)
|
||
//}
|
||
//
|
||
//// MenuCache 内存缓存管理器
|
||
//type MenuCache struct {
|
||
// mu sync.RWMutex
|
||
// cache map[int32]*MenuCacheItem // roleID -> MenuCacheItem
|
||
// maxSize int // 最大缓存条目数
|
||
// ttl time.Duration // 缓存TTL
|
||
// refreshTTL time.Duration // 刷新TTL (提前刷新时间)
|
||
// stats CacheStats // 缓存统计
|
||
// cleanupTick *time.Ticker // 清理定时器
|
||
// stopCh chan struct{} // 停止信号
|
||
//}
|
||
//
|
||
//// CacheStats 缓存统计信息
|
||
//type CacheStats struct {
|
||
// mu sync.RWMutex
|
||
// Hits int64
|
||
// Misses int64
|
||
// Evictions int64
|
||
// RefreshCount int64
|
||
//}
|
||
//
|
||
//// NewMenuCache 创建新的菜单缓存
|
||
//func NewMenuCache(maxSize int, ttl time.Duration) *MenuCache {
|
||
// cache := &MenuCache{
|
||
// cache: make(map[int32]*MenuCacheItem),
|
||
// maxSize: maxSize,
|
||
// ttl: ttl,
|
||
// refreshTTL: ttl - time.Duration(float64(ttl)*0.1), // 提前10%刷新
|
||
// stopCh: make(chan struct{}),
|
||
// cleanupTick: time.NewTicker(time.Minute * 5), // 每5分钟清理一次
|
||
// }
|
||
//
|
||
// // 启动后台清理协程
|
||
// go cache.cleanup()
|
||
//
|
||
// return cache
|
||
//}
|
||
//
|
||
//// Get 获取缓存数据
|
||
//func (mc *MenuCache) Get(roleID int32) (map[string]*dto.OwnerMenuDto, bool) {
|
||
// mc.mu.RLock()
|
||
// item, exists := mc.cache[roleID]
|
||
// mc.mu.RUnlock()
|
||
//
|
||
// if !exists {
|
||
// mc.recordMiss()
|
||
// return nil, false
|
||
// }
|
||
//
|
||
// // 检查是否过期
|
||
// if item.IsExpired() {
|
||
// mc.recordMiss()
|
||
// // 异步删除过期项
|
||
// go func() {
|
||
// mc.mu.Lock()
|
||
// delete(mc.cache, roleID)
|
||
// mc.mu.Unlock()
|
||
// }()
|
||
// return nil, false
|
||
// }
|
||
//
|
||
// mc.recordHit()
|
||
// return item.Data, true
|
||
//}
|
||
//
|
||
//// Set 设置缓存数据
|
||
//func (mc *MenuCache) Set(roleID int32, data map[string]*dto.OwnerMenuDto, version int64) {
|
||
// mc.mu.Lock()
|
||
// defer mc.mu.Unlock()
|
||
//
|
||
// // 如果缓存已满,执行LRU淘汰
|
||
// if len(mc.cache) >= mc.maxSize {
|
||
// mc.evictLRU()
|
||
// }
|
||
//
|
||
// item := &MenuCacheItem{
|
||
// Data: data,
|
||
// ExpireAt: time.Now().Add(mc.ttl),
|
||
// LoadTime: time.Now(),
|
||
// Version: version,
|
||
// }
|
||
//
|
||
// mc.cache[roleID] = item
|
||
//}
|
||
//
|
||
//// NeedRefresh 检查是否需要提前刷新
|
||
//func (mc *MenuCache) NeedRefresh(roleID int32) bool {
|
||
// mc.mu.RLock()
|
||
// item, exists := mc.cache[roleID]
|
||
// mc.mu.RUnlock()
|
||
//
|
||
// if !exists {
|
||
// return true
|
||
// }
|
||
//
|
||
// // 提前刷新策略:在过期前10%时间开始刷新
|
||
// refreshTime := item.LoadTime.Add(mc.refreshTTL)
|
||
// return time.Now().After(refreshTime)
|
||
//}
|
||
//
|
||
//// evictLRU 淘汰最久未使用的缓存项
|
||
//func (mc *MenuCache) evictLRU() {
|
||
// var oldestRoleID int32
|
||
// var oldestTime time.Time = time.Now()
|
||
//
|
||
// for roleID, item := range mc.cache {
|
||
// if item.LoadTime.Before(oldestTime) {
|
||
// oldestTime = item.LoadTime
|
||
// oldestRoleID = roleID
|
||
// }
|
||
// }
|
||
//
|
||
// if oldestRoleID != 0 {
|
||
// delete(mc.cache, oldestRoleID)
|
||
// mc.stats.mu.Lock()
|
||
// mc.stats.Evictions++
|
||
// mc.stats.mu.Unlock()
|
||
// }
|
||
//}
|
||
//
|
||
//// cleanup 后台清理过期缓存
|
||
//func (mc *MenuCache) cleanup() {
|
||
// for {
|
||
// select {
|
||
// case <-mc.cleanupTick.C:
|
||
// mc.cleanupExpired()
|
||
// case <-mc.stopCh:
|
||
// mc.cleanupTick.Stop()
|
||
// return
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//// cleanupExpired 清理过期缓存
|
||
//func (mc *MenuCache) cleanupExpired() {
|
||
// mc.mu.Lock()
|
||
// defer mc.mu.Unlock()
|
||
//
|
||
// now := time.Now()
|
||
// for roleID, item := range mc.cache {
|
||
// if now.After(item.ExpireAt) {
|
||
// delete(mc.cache, roleID)
|
||
// }
|
||
// }
|
||
//}
|
||
//
|
||
//// GetStats 获取缓存统计信息
|
||
//func (mc *MenuCache) GetStats() CacheStats {
|
||
// mc.stats.mu.RLock()
|
||
// defer mc.stats.mu.RUnlock()
|
||
// return mc.stats
|
||
//}
|
||
//
|
||
//// recordHit 记录缓存命中
|
||
//func (mc *MenuCache) recordHit() {
|
||
// mc.stats.mu.Lock()
|
||
// mc.stats.Hits++
|
||
// mc.stats.mu.Unlock()
|
||
//}
|
||
//
|
||
//// recordMiss 记录缓存未命中
|
||
//func (mc *MenuCache) recordMiss() {
|
||
// mc.stats.mu.Lock()
|
||
// mc.stats.Misses++
|
||
// mc.stats.mu.Unlock()
|
||
//}
|
||
//
|
||
//// Close 关闭缓存
|
||
//func (mc *MenuCache) Close() {
|
||
// close(mc.stopCh)
|
||
//}
|
||
//
|
||
//// CachedMenuService 带缓存的菜单服务包装器
|
||
//type CachedMenuService struct {
|
||
// menuService v1.MenuService
|
||
// cache *MenuCache
|
||
// mu sync.RWMutex
|
||
// refreshing map[int32]bool // 正在刷新的roleID
|
||
//}
|
||
//
|
||
//// NewCachedMenuService 创建带缓存的菜单服务
|
||
//func NewCachedMenuService(menuService v1.MenuService, cache *MenuCache) *CachedMenuService {
|
||
// return &CachedMenuService{
|
||
// menuService: menuService,
|
||
// cache: cache,
|
||
// refreshing: make(map[int32]bool),
|
||
// }
|
||
//}
|
||
//
|
||
//// ListByRoleIDToMap 获取菜单数据(带缓存)
|
||
//func (cms *CachedMenuService) ListByRoleIDToMap(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) {
|
||
// // 先尝试从缓存获取
|
||
// if data, hit := cms.cache.Get(roleID); hit {
|
||
// // 检查是否需要异步刷新
|
||
// if cms.cache.NeedRefresh(roleID) {
|
||
// go cms.asyncRefresh(ctx, roleID)
|
||
// }
|
||
// return data, nil
|
||
// }
|
||
//
|
||
// // 缓存未命中,同步获取数据
|
||
// return cms.loadAndCache(ctx, roleID)
|
||
//}
|
||
//
|
||
//// loadAndCache 加载数据并缓存
|
||
//func (cms *CachedMenuService) loadAndCache(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) {
|
||
// // 防止并发重复加载
|
||
// cms.mu.Lock()
|
||
// if cms.refreshing[roleID] {
|
||
// cms.mu.Unlock()
|
||
// // 如果正在加载,等待一小段时间后重试缓存
|
||
// time.Sleep(time.Millisecond * 10)
|
||
// if data, hit := cms.cache.Get(roleID); hit {
|
||
// return data, nil
|
||
// }
|
||
// // 重试失败,继续执行加载逻辑
|
||
// cms.mu.Lock()
|
||
// }
|
||
// cms.refreshing[roleID] = true
|
||
// cms.mu.Unlock()
|
||
//
|
||
// defer func() {
|
||
// cms.mu.Lock()
|
||
// delete(cms.refreshing, roleID)
|
||
// cms.mu.Unlock()
|
||
// }()
|
||
//
|
||
// // 从原始服务获取数据
|
||
// data, err := cms.menuService.ListByRoleIDToMap(ctx, roleID)
|
||
// if err != nil {
|
||
// return nil, err
|
||
// }
|
||
//
|
||
// // 缓存数据
|
||
// version := time.Now().UnixNano()
|
||
// cms.cache.Set(roleID, data, version)
|
||
//
|
||
// return data, nil
|
||
//}
|
||
//
|
||
//// asyncRefresh 异步刷新缓存
|
||
//func (cms *CachedMenuService) asyncRefresh(ctx context.Context, roleID int32) {
|
||
// // 使用背景上下文,避免原请求取消影响刷新
|
||
// bgCtx := context.Background()
|
||
//
|
||
// cms.mu.RLock()
|
||
// if cms.refreshing[roleID] {
|
||
// cms.mu.RUnlock()
|
||
// return
|
||
// }
|
||
// cms.mu.RUnlock()
|
||
//
|
||
// _, err := cms.loadAndCache(bgCtx, roleID)
|
||
// if err != nil {
|
||
// log.Printf("async refresh menu cache failed for roleID %d: %v", roleID, err)
|
||
// }
|
||
//
|
||
// cms.cache.stats.mu.Lock()
|
||
// cms.cache.stats.RefreshCount++
|
||
// cms.cache.stats.mu.Unlock()
|
||
//}
|
||
//
|
||
//// 全局缓存实例
|
||
//var (
|
||
// menuCache *MenuCache
|
||
// cachedMenuSvc *CachedMenuService
|
||
// cacheInitOnce sync.Once
|
||
//)
|
||
//
|
||
//// InitMenuCache 初始化菜单缓存(在应用启动时调用)
|
||
//func InitMenuCache(menuService v1.MenuService) {
|
||
// cacheInitOnce.Do(func() {
|
||
// // 配置参数:最大1000个角色的缓存,TTL 10分钟
|
||
// menuCache = NewMenuCache(1000, time.Minute*10)
|
||
// cachedMenuSvc = NewCachedMenuService(menuService, menuCache)
|
||
// })
|
||
//}
|
||
//
|
||
//// GetCachedMenuService 获取缓存菜单服务实例
|
||
//func GetCachedMenuService() *CachedMenuService {
|
||
// return cachedMenuSvc
|
||
//}
|
||
//
|
||
//// Authorize 修改后的授权中间件
|
||
//func Authorize(
|
||
// sess session.Manager,
|
||
// menuService v1.MenuService,
|
||
//) func(http.Handler) http.Handler {
|
||
// // 初始化缓存
|
||
// InitMenuCache(menuService)
|
||
//
|
||
// return func(next http.Handler) http.Handler {
|
||
// return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
// ctx := r.Context()
|
||
// path := r.URL.Path
|
||
//
|
||
// // 登陆检查
|
||
// n := time.Now()
|
||
// user, err := sess.GetUser(ctx, know.StoreName)
|
||
// if err != nil || user.ID == 0 {
|
||
// http.Redirect(w, r, "/", http.StatusFound)
|
||
// return
|
||
// }
|
||
//
|
||
// log.Printf("scs get user: %s", time.Since(n).String())
|
||
//
|
||
// // 公共路由放行
|
||
// if publicRoutes[path] {
|
||
// ctx = setUser(ctx, user)
|
||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||
// return
|
||
// }
|
||
//
|
||
// n1 := time.Now()
|
||
// // 权限检查 - 使用缓存服务
|
||
// menus, err := GetCachedMenuService().ListByRoleIDToMap(ctx, user.RoleID)
|
||
// if err != nil || !hasPermission(menus, path) {
|
||
// http.Error(w, "Forbidden", http.StatusForbidden)
|
||
// return
|
||
// }
|
||
//
|
||
// log.Printf("listByRoleIDToMap (cached): %s", time.Since(n1).String())
|
||
//
|
||
// n2 := time.Now()
|
||
// cur := getCurrentMenus(menus, path)
|
||
// log.Printf("getCurrentMenus: %s", time.Since(n2).String())
|
||
//
|
||
// ctx = setUser(ctx, user)
|
||
// ctx = setCurMenus(ctx, cur)
|
||
//
|
||
// next.ServeHTTP(w, r.WithContext(ctx))
|
||
// })
|
||
// }
|
||
//}
|
||
//
|
||
//func hasPermission(menus map[string]*dto.OwnerMenuDto, path string) bool {
|
||
// _, ok := menus[path]
|
||
// return ok
|
||
//}
|
||
//
|
||
//func getCurrentMenus(data map[string]*dto.OwnerMenuDto, path string) []dto.OwnerMenuDto {
|
||
// var res []dto.OwnerMenuDto
|
||
//
|
||
// menu, ok := data[path]
|
||
// if !ok {
|
||
// return res
|
||
// }
|
||
//
|
||
// for _, item := range data {
|
||
// if menu.IsList {
|
||
// if item.ParentID == menu.ID || item.ID == menu.ID {
|
||
// res = append(res, *item)
|
||
// }
|
||
// } else {
|
||
// if item.ParentID == menu.ParentID {
|
||
// res = append(res, *item)
|
||
// }
|
||
// }
|
||
// }
|
||
//
|
||
// return res
|
||
//}
|