This commit is contained in:
2025-06-13 17:23:16 +08:00
parent 3150ba80bc
commit 1b72f51e4a
55 changed files with 3894 additions and 310 deletions

View File

@@ -0,0 +1,402 @@
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
//}