重构数据权限模型并清理旧RBAC代码

核心变更:
- 数据权限过滤从基于账号层级改为基于用户类型的多策略过滤
- 移除 AccountStore 中的 GetSubordinateIDs 等旧方法
- 重构认证中间件,支持 enterprise_id 和 customer_id
- 更新 GORM Callback,根据用户类型自动选择过滤策略(代理/企业/个人客户)
- 更新所有集成测试以适配新的 API 签名
- 添加功能总结文档和 OpenSpec 归档

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-01-10 15:08:11 +08:00
parent 9c6d4a3bd4
commit 743db126f7
26 changed files with 1292 additions and 322 deletions

View File

@@ -2,13 +2,10 @@ package postgres
import (
"context"
"time"
"github.com/break/junhong_cmp_fiber/internal/store"
"github.com/break/junhong_cmp_fiber/internal/model"
"github.com/break/junhong_cmp_fiber/pkg/constants"
"github.com/bytedance/sonic"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
)
@@ -133,67 +130,3 @@ func (s *AccountStore) List(ctx context.Context, opts *store.QueryOptions, filte
return accounts, total, nil
}
// GetSubordinateIDs 获取账号的所有可见账号 ID包含自己
// 废弃说明:账号层级关系已改为通过 Shop 表维护
// 新的数据权限过滤应该基于 ShopID而非账号的 ParentID
// 使用 Redis 缓存优化性能,缓存 30 分钟
//
// 对于代理账号:查询该账号所属店铺及其下级店铺的所有账号
// 对于平台用户和超级管理员:返回空(在上层跳过过滤)
func (s *AccountStore) GetSubordinateIDs(ctx context.Context, accountID uint) ([]uint, error) {
// 1. 尝试从 Redis 缓存读取
cacheKey := constants.RedisAccountSubordinatesKey(accountID)
cached, err := s.redis.Get(ctx, cacheKey).Result()
if err == nil {
var ids []uint
if err := sonic.Unmarshal([]byte(cached), &ids); err == nil {
return ids, nil
}
}
// 2. 查询当前账号
account, err := s.GetByID(ctx, accountID)
if err != nil {
return nil, err
}
// 3. 如果是代理账号,需要查询该店铺及下级店铺的所有账号
var ids []uint
if account.UserType == constants.UserTypeAgent && account.ShopID != nil {
// 注意:这里需要 ShopStore 来查询店铺的下级
// 但为了避免循环依赖,这个逻辑应该在 Service 层处理
// Store 层只提供基础的数据访问能力
// 暂时返回只包含自己的列表
ids = []uint{accountID}
} else {
// 平台用户和超级管理员返回空列表(在 Service 层跳过过滤)
ids = []uint{}
}
// 4. 写入 Redis 缓存30 分钟过期)
data, _ := sonic.Marshal(ids)
s.redis.Set(ctx, cacheKey, data, 30*time.Minute)
return ids, nil
}
// ClearSubordinatesCache 清除指定账号的下级 ID 缓存
func (s *AccountStore) ClearSubordinatesCache(ctx context.Context, accountID uint) error {
cacheKey := constants.RedisAccountSubordinatesKey(accountID)
return s.redis.Del(ctx, cacheKey).Err()
}
// ClearSubordinatesCacheForParents 递归清除所有上级账号的缓存
// 废弃说明:账号层级关系已改为通过 Shop 表维护
// 新版本应该清除店铺层级的缓存,而非账号层级
func (s *AccountStore) ClearSubordinatesCacheForParents(ctx context.Context, accountID uint) error {
// 清除当前账号的缓存
if err := s.ClearSubordinatesCache(ctx, accountID); err != nil {
return err
}
// TODO: 应该清除该账号所属店铺及上级店铺的下级缓存
// 但这需要访问 ShopStore为了避免循环依赖应在 Service 层处理
return nil
}