feat: 实现 RBAC 权限系统和数据权限控制 (004-rbac-data-permission)
主要功能: - 实现完整的 RBAC 权限系统(账号、角色、权限的多对多关联) - 基于 owner_id + shop_id 的自动数据权限过滤 - 使用 PostgreSQL WITH RECURSIVE 查询下级账号 - Redis 缓存优化下级账号查询性能(30分钟过期) - 支持多租户数据隔离和层级权限管理 技术实现: - 新增 Account、Role、Permission 模型及关联关系表 - 实现 GORM Scopes 自动应用数据权限过滤 - 添加数据库迁移脚本(000002_rbac_data_permission、000003_add_owner_id_shop_id) - 完善错误码定义(1010-1027 为 RBAC 相关错误) - 重构 main.go 采用函数拆分提高可读性 测试覆盖: - 添加 Account、Role、Permission 的集成测试 - 添加数据权限过滤的单元测试和集成测试 - 添加下级账号查询和缓存的单元测试 - 添加 API 回归测试确保向后兼容 文档更新: - 更新 README.md 添加 RBAC 功能说明 - 更新 CLAUDE.md 添加技术栈和开发原则 - 添加 docs/004-rbac-data-permission/ 功能总结和使用指南 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,26 @@ const (
|
||||
CodeTooManyRequests = 1008 // 请求过多
|
||||
CodeRequestTooLarge = 1009 // 请求体过大
|
||||
|
||||
// RBAC 相关错误 (1010-1099)
|
||||
CodeAccountNotFound = 1010 // 账号不存在
|
||||
CodeAccountDisabled = 1011 // 账号已禁用
|
||||
CodeAccountDeleted = 1012 // 账号已删除
|
||||
CodeUsernameExists = 1013 // 用户名已存在
|
||||
CodePhoneExists = 1014 // 手机号已存在
|
||||
CodeInvalidPassword = 1015 // 密码格式不正确
|
||||
CodePasswordTooWeak = 1016 // 密码强度不足
|
||||
CodeParentIDRequired = 1017 // 非 root 用户必须提供上级账号
|
||||
CodeInvalidParentID = 1018 // 上级账号不存在或无效
|
||||
CodeCannotModifyParent = 1019 // 禁止修改上级账号
|
||||
CodeCannotModifyUserType = 1020 // 禁止修改用户类型
|
||||
CodeRoleNotFound = 1021 // 角色不存在
|
||||
CodeRoleNameExists = 1022 // 角色名称已存在
|
||||
CodePermissionNotFound = 1023 // 权限不存在
|
||||
CodePermCodeExists = 1024 // 权限编码已存在
|
||||
CodeInvalidPermCode = 1025 // 权限编码格式不正确
|
||||
CodeRoleAlreadyAssigned = 1026 // 角色已分配
|
||||
CodePermAlreadyAssigned = 1027 // 权限已分配
|
||||
|
||||
// 服务端错误 (2000-2999) -> 5xx HTTP 状态码
|
||||
CodeInternalError = 2001 // 内部服务器错误
|
||||
CodeDatabaseError = 2002 // 数据库错误
|
||||
@@ -31,22 +51,40 @@ const (
|
||||
|
||||
// errorMessages 错误消息映射表(中文)
|
||||
var errorMessages = map[int]string{
|
||||
CodeSuccess: "成功",
|
||||
CodeInvalidParam: "参数验证失败",
|
||||
CodeMissingToken: "缺失认证令牌",
|
||||
CodeInvalidToken: "无效或过期的令牌",
|
||||
CodeUnauthorized: "未授权访问",
|
||||
CodeForbidden: "禁止访问",
|
||||
CodeNotFound: "资源未找到",
|
||||
CodeConflict: "资源冲突",
|
||||
CodeTooManyRequests: "请求过多,请稍后重试",
|
||||
CodeRequestTooLarge: "请求体过大",
|
||||
CodeInternalError: "内部服务器错误",
|
||||
CodeDatabaseError: "数据库错误",
|
||||
CodeRedisError: "缓存服务错误",
|
||||
CodeServiceUnavailable: "服务暂时不可用",
|
||||
CodeTimeout: "请求超时",
|
||||
CodeTaskQueueError: "任务队列错误",
|
||||
CodeSuccess: "成功",
|
||||
CodeInvalidParam: "参数验证失败",
|
||||
CodeMissingToken: "缺失认证令牌",
|
||||
CodeInvalidToken: "无效或过期的令牌",
|
||||
CodeUnauthorized: "未授权访问",
|
||||
CodeForbidden: "禁止访问",
|
||||
CodeNotFound: "资源未找到",
|
||||
CodeConflict: "资源冲突",
|
||||
CodeTooManyRequests: "请求过多,请稍后重试",
|
||||
CodeRequestTooLarge: "请求体过大",
|
||||
CodeAccountNotFound: "账号不存在",
|
||||
CodeAccountDisabled: "账号已禁用",
|
||||
CodeAccountDeleted: "账号已删除",
|
||||
CodeUsernameExists: "用户名已存在",
|
||||
CodePhoneExists: "手机号已存在",
|
||||
CodeInvalidPassword: "密码格式不正确",
|
||||
CodePasswordTooWeak: "密码强度不足",
|
||||
CodeParentIDRequired: "非 root 用户必须提供上级账号",
|
||||
CodeInvalidParentID: "上级账号不存在或无效",
|
||||
CodeCannotModifyParent: "禁止修改上级账号",
|
||||
CodeCannotModifyUserType: "禁止修改用户类型",
|
||||
CodeRoleNotFound: "角色不存在",
|
||||
CodeRoleNameExists: "角色名称已存在",
|
||||
CodePermissionNotFound: "权限不存在",
|
||||
CodePermCodeExists: "权限编码已存在",
|
||||
CodeInvalidPermCode: "权限编码格式不正确(应为 module:action 格式)",
|
||||
CodeRoleAlreadyAssigned: "角色已分配",
|
||||
CodePermAlreadyAssigned: "权限已分配",
|
||||
CodeInternalError: "内部服务器错误",
|
||||
CodeDatabaseError: "数据库错误",
|
||||
CodeRedisError: "缓存服务错误",
|
||||
CodeServiceUnavailable: "服务暂时不可用",
|
||||
CodeTimeout: "请求超时",
|
||||
CodeTaskQueueError: "任务队列错误",
|
||||
}
|
||||
|
||||
// GetMessage 获取错误码对应的消息
|
||||
|
||||
@@ -43,7 +43,7 @@ func FromFiberContext(c *fiber.Ctx) *ErrorContext {
|
||||
}
|
||||
|
||||
// 提取 User ID(如果已认证)
|
||||
if uid := c.Locals("user_id"); uid != nil {
|
||||
if uid := c.Locals(constants.ContextKeyUserID); uid != nil {
|
||||
if userID, ok := uid.(string); ok {
|
||||
ctx.UserID = userID
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
// TestSafeErrorHandler 测试 SafeErrorHandler 基本功能
|
||||
func TestSafeErrorHandler(t *testing.T) {
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
defer func() { _ = logger.Sync() }()
|
||||
handler := SafeErrorHandler(logger)
|
||||
|
||||
tests := []struct {
|
||||
@@ -156,7 +156,7 @@ func TestAppErrorUnwrap(t *testing.T) {
|
||||
// BenchmarkSafeErrorHandler 基准测试错误处理性能
|
||||
func BenchmarkSafeErrorHandler(b *testing.B) {
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
defer func() { _ = logger.Sync() }()
|
||||
_ = SafeErrorHandler(logger) // 避免未使用变量警告
|
||||
|
||||
testErrors := []error{
|
||||
@@ -330,7 +330,7 @@ func TestErrorMessageSanitization(t *testing.T) {
|
||||
// TestConcurrentErrorHandling 测试并发场景下的错误处理
|
||||
func TestConcurrentErrorHandling(t *testing.T) {
|
||||
logger, _ := zap.NewProduction()
|
||||
defer logger.Sync()
|
||||
defer func() { _ = logger.Sync() }()
|
||||
handler := SafeErrorHandler(logger)
|
||||
if handler == nil {
|
||||
t.Fatal("SafeErrorHandler returned nil")
|
||||
|
||||
Reference in New Issue
Block a user