projectx/internal/pkg/mid/authorize_v3.go
2025-06-13 17:23:16 +08:00

538 lines
14 KiB
Go
Raw 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 mid
//import (
// "context"
// "encoding/json"
// "errors"
// "fmt"
// "log"
// "net/http"
// "strconv"
// "sync"
// "time"
//
// "management/internal/erpserver/model/dto"
// v1 "management/internal/erpserver/service/v1"
// "management/internal/pkg/know"
// "management/internal/pkg/session"
//
// "github.com/allegro/bigcache/v3"
//)
//
//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,
//}
//
//// MenuCacheEntry 菜单缓存条目
//type MenuCacheEntry struct {
// Data map[string]*dto.OwnerMenuDto `json:"data"`
// Timestamp int64 `json:"timestamp"`
// Version int64 `json:"version"`
//}
//
//// CacheStats 缓存统计信息
//type CacheStats struct {
// mu sync.RWMutex
// Hits int64
// Misses int64
// Errors int64
// RefreshCount int64
// AsyncRefreshHits int64
// LastRefreshTime time.Time
//}
//
//// GetHitRate 获取命中率
//func (cs *CacheStats) GetHitRate() float64 {
// cs.mu.RLock()
// defer cs.mu.RUnlock()
//
// total := cs.Hits + cs.Misses
// if total == 0 {
// return 0
// }
// return float64(cs.Hits) / float64(total) * 100
//}
//
//// MenuCacheManager BigCache菜单缓存管理器
//type MenuCacheManager struct {
// cache *bigcache.BigCache
// refreshTTL time.Duration
// stats *CacheStats
// mu sync.RWMutex
// refreshing map[int64]bool
// stopCh chan struct{}
// monitorTicker *time.Ticker
//}
//
//// NewMenuCacheManager 创建菜单缓存管理器
//func NewMenuCacheManager(ttl time.Duration) (*MenuCacheManager, error) {
// config := bigcache.DefaultConfig(ttl)
//
// // 生产环境优化配置
// config.Shards = 256 // 分片数,减少锁竞争
// config.MaxEntriesInWindow = 10000 // 窗口内最大条目数
// config.MaxEntrySize = 1024 * 50 // 最大条目50KB
// config.HardMaxCacheSize = 512 // 最大缓存512MB
// config.StatsEnabled = true // 启用统计
// config.Verbose = false // 关闭详细日志
// config.CleanWindow = 5 * time.Minute // 清理窗口
//
// ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
// defer cancel()
//
// cache, err := bigcache.New(ctx, config)
// if err != nil {
// return nil, fmt.Errorf("failed to create BigCache: %w", err)
// }
//
// manager := &MenuCacheManager{
// cache: cache,
// refreshTTL: time.Duration(float64(ttl) * 0.8), // 80%时间后开始刷新
// stats: &CacheStats{},
// refreshing: make(map[int64]bool),
// stopCh: make(chan struct{}),
// monitorTicker: time.NewTicker(time.Minute), // 每分钟监控一次
// }
//
// // 启动监控协程
// go manager.monitor()
//
// return manager, nil
//}
//
//// Get 获取缓存数据
//func (mcm *MenuCacheManager) Get(roleID int32) (map[string]*dto.OwnerMenuDto, bool, bool) {
// key := mcm.makeKey(roleID)
//
// data, err := mcm.cache.Get(key)
// if err != nil {
// if !errors.Is(err, bigcache.ErrEntryNotFound) {
// mcm.recordError()
// log.Printf("BigCache get error for roleID %d: %v", roleID, err)
// }
// mcm.recordMiss()
// return nil, false, false
// }
//
// // 反序列化
// var entry MenuCacheEntry
// if err := json.Unmarshal(data, &entry); err != nil {
// mcm.recordError()
// log.Printf("Failed to unmarshal cache entry for roleID %d: %v", roleID, err)
// mcm.recordMiss()
// return nil, false, false
// }
//
// mcm.recordHit()
//
// // 检查是否需要刷新
// needRefresh := time.Since(time.Unix(entry.Timestamp, 0)) > mcm.refreshTTL
//
// return entry.Data, true, needRefresh
//}
//
//// Set 设置缓存数据
//func (mcm *MenuCacheManager) Set(roleID int32, data map[string]*dto.OwnerMenuDto) error {
// key := mcm.makeKey(roleID)
//
// entry := MenuCacheEntry{
// Data: data,
// Timestamp: time.Now().Unix(),
// Version: time.Now().UnixNano(),
// }
//
// // 序列化
// entryData, err := json.Marshal(entry)
// if err != nil {
// mcm.recordError()
// return fmt.Errorf("failed to marshal cache entry: %w", err)
// }
//
// // 存储到BigCache
// err = mcm.cache.Set(key, entryData)
// if err != nil {
// mcm.recordError()
// return fmt.Errorf("failed to set cache: %w", err)
// }
//
// return nil
//}
//
//// Delete 删除缓存数据
//func (mcm *MenuCacheManager) Delete(roleID int32) error {
// key := mcm.makeKey(roleID)
// err := mcm.cache.Delete(key)
// if err != nil && !errors.Is(err, bigcache.ErrEntryNotFound) {
// mcm.recordError()
// return fmt.Errorf("failed to delete cache: %w", err)
// }
// return nil
//}
//
//// makeKey 生成缓存key
//func (mcm *MenuCacheManager) makeKey(roleID int32) string {
// return "menu:role:" + strconv.Itoa(int(roleID))
//}
//
//// GetStats 获取统计信息
//func (mcm *MenuCacheManager) GetStats() *CacheStats {
// mcm.stats.mu.RLock()
// defer mcm.stats.mu.RUnlock()
//
// // 复制统计数据
// stats := *mcm.stats
// return &stats
//}
//
//// GetBigCacheStats 获取BigCache原生统计
//func (mcm *MenuCacheManager) GetBigCacheStats() bigcache.Stats {
// return mcm.cache.Stats()
//}
//
//// recordHit 记录命中
//func (mcm *MenuCacheManager) recordHit() {
// mcm.stats.mu.Lock()
// mcm.stats.Hits++
// mcm.stats.mu.Unlock()
//}
//
//// recordMiss 记录未命中
//func (mcm *MenuCacheManager) recordMiss() {
// mcm.stats.mu.Lock()
// mcm.stats.Misses++
// mcm.stats.mu.Unlock()
//}
//
//// recordError 记录错误
//func (mcm *MenuCacheManager) recordError() {
// mcm.stats.mu.Lock()
// mcm.stats.Errors++
// mcm.stats.mu.Unlock()
//}
//
//// recordRefresh 记录刷新
//func (mcm *MenuCacheManager) recordRefresh() {
// mcm.stats.mu.Lock()
// mcm.stats.RefreshCount++
// mcm.stats.LastRefreshTime = time.Now()
// mcm.stats.mu.Unlock()
//}
//
//// recordAsyncRefreshHit 记录异步刷新命中
//func (mcm *MenuCacheManager) recordAsyncRefreshHit() {
// mcm.stats.mu.Lock()
// mcm.stats.AsyncRefreshHits++
// mcm.stats.mu.Unlock()
//}
//
//// monitor 监控协程
//func (mcm *MenuCacheManager) monitor() {
// for {
// select {
// case <-mcm.monitorTicker.C:
// mcm.logStats()
// case <-mcm.stopCh:
// mcm.monitorTicker.Stop()
// return
// }
// }
//}
//
//// logStats 记录统计信息
//func (mcm *MenuCacheManager) logStats() {
// stats := mcm.GetStats()
// bigCacheStats := mcm.GetBigCacheStats()
//
// log.Printf("MenuCache Stats - Hits: %d, Misses: %d, Errors: %d, HitRate: %.2f%%, "+
// "RefreshCount: %d, AsyncRefreshHits: %d, BigCache Hits: %d, BigCache Misses: %d",
// stats.Hits, stats.Misses, stats.Errors, stats.GetHitRate(),
// stats.RefreshCount, stats.AsyncRefreshHits,
// bigCacheStats.Hits, bigCacheStats.Misses)
//}
//
//// Close 关闭缓存管理器
//func (mcm *MenuCacheManager) Close() error {
// close(mcm.stopCh)
// return mcm.cache.Close()
//}
//
//// CachedMenuService 带BigCache的菜单服务
//type CachedMenuService struct {
// menuService v1.MenuService
// cache *MenuCacheManager
// mu sync.RWMutex
// refreshing map[int32]bool
//}
//
//// NewCachedMenuService 创建带缓存的菜单服务
//func NewCachedMenuService(menuService v1.MenuService, cache *MenuCacheManager) *CachedMenuService {
// return &CachedMenuService{
// menuService: menuService,
// cache: cache,
// refreshing: make(map[int32]bool),
// }
//}
//
//// ListByRoleIDToMap 获取菜单数据带BigCache缓存
//func (cms *CachedMenuService) ListByRoleIDToMap(ctx context.Context, roleID int32) (map[string]*dto.OwnerMenuDto, error) {
// // 尝试从缓存获取
// data, hit, needRefresh := cms.cache.Get(roleID)
// if hit {
// // 如果需要刷新且当前没有在刷新中,启动异步刷新
// if needRefresh && !cms.isRefreshing(roleID) {
// go cms.asyncRefresh(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 * 5)
// 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
// }
//
// // 缓存数据
// if cacheErr := cms.cache.Set(roleID, data); cacheErr != nil {
// log.Printf("Failed to cache menu data for roleID %d: %v", roleID, cacheErr)
// // 缓存失败不影响业务逻辑,继续返回数据
// }
//
// return data, nil
//}
//
//// asyncRefresh 异步刷新缓存
//func (cms *CachedMenuService) asyncRefresh(roleID int32) {
// // 检查是否已在刷新中
// if cms.isRefreshing(roleID) {
// return
// }
//
// // 使用背景上下文
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
// defer cancel()
//
// _, err := cms.loadAndCache(ctx, roleID)
// if err != nil {
// log.Printf("Async refresh menu cache failed for roleID %d: %v", roleID, err)
// } else {
// cms.cache.recordRefresh()
// cms.cache.recordAsyncRefreshHit()
// }
//}
//
//// isRefreshing 检查是否正在刷新
//func (cms *CachedMenuService) isRefreshing(roleID int32) bool {
// cms.mu.RLock()
// refreshing := cms.refreshing[roleID]
// cms.mu.RUnlock()
// return refreshing
//}
//
//// InvalidateRole 使指定角色缓存失效
//func (cms *CachedMenuService) InvalidateRole(roleID int32) error {
// return cms.cache.Delete(roleID)
//}
//
//// GetCacheStats 获取缓存统计
//func (cms *CachedMenuService) GetCacheStats() *CacheStats {
// return cms.cache.GetStats()
//}
//
//// 全局实例
//var (
// menuCacheManager *MenuCacheManager
// cachedMenuSvc *CachedMenuService
// cacheInitOnce sync.Once
// cacheInitErr error
//)
//
//// InitMenuCache 初始化菜单缓存
//func InitMenuCache(menuService v1.MenuService) error {
// cacheInitOnce.Do(func() {
// // 缓存TTL设置为15分钟
// manager, err := NewMenuCacheManager(15 * time.Minute)
// if err != nil {
// cacheInitErr = fmt.Errorf("failed to initialize menu cache: %w", err)
// return
// }
//
// menuCacheManager = manager
// cachedMenuSvc = NewCachedMenuService(menuService, manager)
//
// log.Println("MenuCache initialized successfully with BigCache")
// })
//
// return cacheInitErr
//}
//
//// GetCachedMenuService 获取缓存菜单服务
//func GetCachedMenuService() *CachedMenuService {
// return cachedMenuSvc
//}
//
//// GetMenuCacheManager 获取缓存管理器
//func GetMenuCacheManager() *MenuCacheManager {
// return menuCacheManager
//}
//
//// CloseMenuCache 关闭菜单缓存
//func CloseMenuCache() error {
// if menuCacheManager != nil {
// return menuCacheManager.Close()
// }
// return nil
//}
//
//// Authorize 修改后的授权中间件
//func Authorize(
// sess session.Manager,
// menuService v1.MenuService,
//) func(http.Handler) http.Handler {
// // 初始化缓存
// if err := InitMenuCache(menuService); err != nil {
// log.Printf("Failed to initialize menu cache: %v", err)
// // 缓存初始化失败,降级到原始服务
// 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 := menuService.ListByRoleIDToMap(ctx, user.RoleID)
// if err != nil || !hasPermission(menus, path) {
// http.Error(w, "Forbidden", http.StatusForbidden)
// return
// }
// log.Printf("listByRoleIDToMap (fallback): %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))
// })
// }
// }
//
// 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()
// // 权限检查 - 使用BigCache缓存服务
// menus, err := GetCachedMenuService().ListByRoleIDToMap(ctx, user.RoleID)
// if err != nil || !hasPermission(menus, path) {
// http.Error(w, "Forbidden", http.StatusForbidden)
// return
// }
// log.Printf("listByRoleIDToMap (BigCache): %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
//}