# 旧 RBAC 系统清理 - 完成总结 ## 概览 本次清理工作完成了从旧的基于账号层级(`parent_id`)的数据权限模型到新的基于店铺层级、企业ID和客户ID的数据权限模型的迁移。 **完成时间**: 2026-01-10 **提案ID**: remove-legacy-rbac-cleanup **依赖提案**: - add-user-organization-model ✅ - add-role-permission-system ✅ - add-personal-customer-wechat ✅ --- ## 核心变更 ### 1. Account Store 清理 **文件**: `internal/store/postgres/account_store.go` **移除的方法**: - `GetSubordinateIDs(ctx, accountID)` - 基于 `parent_id` 的递归查询 - `ClearSubordinatesCache(ctx, accountID)` - 清除账号下级缓存 - `ClearSubordinatesCacheForParents(ctx, accountID)` - 递归清除上级缓存 **保留的方法**: - `GetByShopID(ctx, shopID)` - 根据店铺 ID 查询账号列表 - `GetByEnterpriseID(ctx, enterpriseID)` - 根据企业 ID 查询账号列表 **移除的依赖**: - 移除了 `time`、`constants`、`sonic` 包的导入(不再需要) --- ### 2. 数据权限过滤重构 **文件**: `pkg/gorm/callback.go` **核心变更**: 从基于账号层级的过滤改为基于用户类型的多策略过滤。 #### 旧的过滤逻辑 ```go // 查询账号的所有下级ID subordinateIDs := accountStore.GetSubordinateIDs(ctx, userID) // 过滤: creator IN (下级账号ID) tx.Where("creator IN ?", subordinateIDs) ``` #### 新的过滤逻辑 根据用户类型自动选择合适的过滤策略: 1. **超级管理员和平台用户**: 跳过过滤,查看所有数据 2. **代理用户**: 基于店铺层级过滤 ```go subordinateShopIDs := shopStore.GetSubordinateShopIDs(ctx, shopID) tx.Where("shop_id IN ?", subordinateShopIDs) ``` 3. **企业用户**: 基于企业ID过滤 ```go tx.Where("enterprise_id = ?", enterpriseID) ``` 4. **个人客户**: 基于客户ID或创建人过滤 ```go tx.Where("customer_id = ?", customerID) // 或降级为 tx.Where("creator = ?", userID) ``` **接口变更**: ```go // 旧接口 type AccountStoreInterface interface { GetSubordinateIDs(ctx, accountID) ([]uint, error) } func RegisterDataPermissionCallback(db, accountStore) // 新接口 type ShopStoreInterface interface { GetSubordinateShopIDs(ctx, shopID) ([]uint, error) } func RegisterDataPermissionCallback(db, shopStore) ``` --- ### 3. 认证中间件增强 **文件**: `pkg/middleware/auth.go` **新增字段支持**: ```go // 旧的用户上下文 - userID - userType - shopID // 新的用户上下文 type UserContextInfo struct { UserID uint // 用户ID UserType int // 用户类型 ShopID uint // 店铺ID(代理用户) EnterpriseID uint // 企业ID(企业用户) CustomerID uint // 客户ID(个人客户) } ``` **API 变更**: ```go // 旧API func SetUserContext(ctx, userID, userType, shopID) context.Context func SetUserToFiberContext(c, userID, userType, shopID) // 新API func SetUserContext(ctx, info *UserContextInfo) context.Context func SetUserToFiberContext(c, info *UserContextInfo) // 辅助函数(用于测试和兼容性) func NewSimpleUserContext(userID, userType, shopID) *UserContextInfo ``` **新增辅助函数**: ```go func GetEnterpriseIDFromContext(ctx) uint func GetCustomerIDFromContext(ctx) uint ``` --- ### 4. 常量清理 **文件**: `pkg/constants/constants.go` 和 `pkg/constants/redis.go` **新增常量**: ```go // Context 键 const ( ContextKeyEnterpriseID = "enterprise_id" ContextKeyCustomerID = "customer_id" ) // 用户类型 const ( UserTypePersonalCustomer = 5 // 个人客户(C端用户) ) ``` **移除常量**: ```go // Redis 键生成函数(已废弃) func RedisAccountSubordinatesKey(accountID) string ``` --- ### 5. Bootstrap 初始化调整 **文件**: `internal/bootstrap/stores.go` 和 `internal/bootstrap/bootstrap.go` **变更内容**: ```go // 在 stores 结构体中添加 Shop type stores struct { Account *postgres.AccountStore Shop *postgres.ShopStore // 新增 Role *postgres.RoleStore // ... } // 初始化 Shop Store Shop: postgres.NewShopStore(deps.DB, deps.Redis) // 数据权限回调改为使用 ShopStore pkgGorm.RegisterDataPermissionCallback(deps.DB, stores.Shop) ``` --- ## 架构改进 ### 数据权限过滤逻辑对比 | 维度 | 旧设计(基于账号层级) | 新设计(基于用户类型) | |------|----------------------|---------------------| | **核心依赖** | Account.parent_id | Shop.parent_id + Enterprise.id + Customer.id | | **递归查询** | 账号的下级账号 | 店铺的下级店铺 | | **适用范围** | 仅代理账号 | 代理、企业、个人客户 | | **过滤字段** | creator | shop_id / enterprise_id / customer_id / creator | | **可扩展性** | 低(单一策略) | 高(多策略,根据用户类型) | | **缓存键** | account:subordinates:{id} | shop:subordinates:{id} | ### 新的数据权限模型优势 1. **更清晰的职责分离**: 账号不再承担组织结构的职责,组织结构完全由 Shop 和 Enterprise 维护 2. **更灵活的过滤策略**: 根据用户类型自动选择合适的过滤字段 3. **更好的扩展性**: 新增用户类型时只需添加对应的过滤逻辑 4. **更符合业务模型**: B端(代理/企业)和C端(个人客户)使用不同的过滤策略 --- ## 破坏性变更 ### API 签名变更 以下 API 的签名已变更,需要调用方更新: #### 1. pkg/middleware 包 ```go // ❌ 旧API(已移除) middleware.SetUserContext(ctx, userID, userType, shopID) middleware.SetUserToFiberContext(c, userID, userType, shopID) // ✅ 新API info := &middleware.UserContextInfo{ UserID: userID, UserType: userType, ShopID: shopID, EnterpriseID: enterpriseID, CustomerID: customerID, } middleware.SetUserContext(ctx, info) middleware.SetUserToFiberContext(c, info) // ✅ 兼容性辅助函数(仅用于基本场景) info := middleware.NewSimpleUserContext(userID, userType, shopID) middleware.SetUserContext(ctx, info) ``` #### 2. AuthConfig 配置 ```go // ❌ 旧API type AuthConfig struct { TokenValidator func(token string) (userID uint, userType int, shopID uint, err error) } // ✅ 新API type AuthConfig struct { TokenValidator func(token string) (*middleware.UserContextInfo, error) } ``` #### 3. GORM Callback 注册 ```go // ❌ 旧API gorm.RegisterDataPermissionCallback(db, accountStore) // ✅ 新API gorm.RegisterDataPermissionCallback(db, shopStore) ``` --- ## 迁移指南 ### 对于业务代码 如果你的代码直接调用了以下方法,需要迁移: #### 1. AccountStore 方法调用 ```go // ❌ 旧代码 ids, err := accountStore.GetSubordinateIDs(ctx, userID) accountStore.ClearSubordinatesCache(ctx, userID) // ✅ 新代码 // 不再需要查询账号下级,数据权限过滤会自动处理 // 如果需要查询店铺下级: shopIDs, err := shopStore.GetSubordinateShopIDs(ctx, shopID) ``` #### 2. 认证中间件配置 ```go // ❌ 旧代码 auth.Auth(auth.AuthConfig{ TokenValidator: func(token string) (uint, int, uint, error) { // 解析 token return userID, userType, shopID, nil }, }) // ✅ 新代码 auth.Auth(auth.AuthConfig{ TokenValidator: func(token string) (*middleware.UserContextInfo, error) { // 解析 token return &middleware.UserContextInfo{ UserID: userID, UserType: userType, ShopID: shopID, EnterpriseID: enterpriseID, CustomerID: customerID, }, nil }, }) ``` #### 3. 设置用户上下文 ```go // ❌ 旧代码 middleware.SetUserToFiberContext(c, userID, userType, shopID) // ✅ 新代码 info := &middleware.UserContextInfo{ UserID: userID, UserType: userType, ShopID: shopID, } middleware.SetUserToFiberContext(c, info) // ✅ 或者使用辅助函数(仅适用于简单场景) info := middleware.NewSimpleUserContext(userID, userType, shopID) middleware.SetUserToFiberContext(c, info) ``` --- ## 测试影响 ### 需要更新的测试 由于 API 签名变更,以下测试需要更新: 1. **认证中间件测试** (`pkg/middleware/auth_test.go`) 2. **GORM Callback 测试** (`pkg/gorm/callback_test.go`) 3. **业务集成测试** (`tests/integration/admin/*_test.go`) 4. **权限过滤测试** (`tests/integration/admin/permission_platform_filter_test.go`) ### 测试迁移模式 ```go // ❌ 旧测试代码 ctx := middleware.SetUserContext(context.Background(), 1, constants.UserTypeAgent, 100) // ✅ 新测试代码 ctx := middleware.SetUserContext(context.Background(), middleware.NewSimpleUserContext(1, constants.UserTypeAgent, 100)) ``` --- ## 遗留问题 ### 1. 企业用户和个人客户的 Token 生成 **问题**: 当前 Token 验证器需要返回 `enterprise_id` 和 `customer_id`,但现有的 Token 生成逻辑可能还没有包含这些字段。 **影响**: 企业用户和个人客户的数据权限过滤可能无法正常工作。 **解决方案**: 1. 更新 JWT Token 的 Claims 结构,添加 `enterprise_id` 和 `customer_id` 字段 2. 在登录时根据用户类型生成包含对应字段的 Token ### 2. 测试兼容性 **问题**: 大量测试代码需要更新以适配新的 API 签名。 **影响**: 测试编译失败。 **解决方案**: 1. 批量更新测试代码,使用 `middleware.NewSimpleUserContext` 辅助函数 2. 为需要完整上下文的测试创建完整的 `UserContextInfo` 实例 --- ## 验收清单 - [x] 0.1 确认 add-user-organization-model 提案已完成 - [x] 0.2 确认 add-role-permission-system 提案已完成 - [x] 0.3 确认 add-personal-customer-wechat 提案已完成 - [x] 1.1 移除 `GetSubordinateIDs` 方法 - [x] 1.2 移除相关的 Redis 缓存逻辑 - [x] 1.3 更新 `account_store.go` 中所有引用 `parent_id` 的代码 - [x] 1.4 添加新的查询方法:`GetByShopID`、`GetByEnterpriseID` - [x] 2.1 创建/更新数据权限过滤逻辑 - [x] 2.1.1 改为从 context 获取 shop_id(而非 user_id) - [x] 2.1.2 调用 `shop_store.GetSubordinateShopIDs` 获取下级店铺 - [x] 2.1.3 生成 `WHERE shop_id IN (...)` 过滤条件 - [x] 2.2 更新 Store 层的 List 方法 - [x] 2.3 处理企业账号的过滤逻辑 - [x] 2.4 处理平台用户跳过过滤的逻辑 - [x] 3.1 更新认证中间件 - [x] 3.1.1 在 context 中设置 enterprise_id 和 customer_id - [x] 3.1.2 根据用户类型设置数据权限过滤标记 - [x] 4.1 权限校验中间件(无需修改) - [x] 5.1 移除旧的 Redis key 常量 - [x] 5.2 确保所有代码使用新定义的用户类型常量 - [x] 5.3 确保所有代码使用新定义的角色类型常量 - [x] 6.1 日志和埋点更新(无需额外修改,context 已包含完整信息) - [ ] 7.x 测试更新(待完成) - [x] 8.1 创建清理总结文档 --- ## 性能影响 ### 缓存使用 - **旧系统**: `account:subordinates:{account_id}`(缓存账号下级) - **新系统**: `shop:subordinates:{shop_id}`(缓存店铺下级) ### 查询性能 - **代理用户**: 查询性能保持不变,从递归查询账号改为递归查询店铺 - **企业用户**: 查询性能提升,直接根据 `enterprise_id` 过滤 - **个人客户**: 查询性能提升,直接根据 `customer_id` 或 `creator` 过滤 --- ## 后续工作 1. **完成测试修复**: 批量更新测试代码以适配新的 API 签名 2. **Token 生成逻辑更新**: 在 JWT Token 中添加 `enterprise_id` 和 `customer_id` 字段 3. **监控和日志**: 验证新的数据权限过滤逻辑是否正常工作 4. **性能测试**: 验证新的过滤逻辑性能是否符合预期 --- ## 参考文档 - [提案文档](../../openspec/changes/archive/remove-legacy-rbac-cleanup/proposal.md) - [用户组织模型](../add-user-organization-model/功能总结.md) - [角色权限体系](../add-role-permission-system/功能总结.md)