主要功能: - 实现完整的 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>
269 lines
16 KiB
Markdown
269 lines
16 KiB
Markdown
# Implementation Plan: RBAC表结构与GORM数据权限过滤
|
||
|
||
**Branch**: `004-rbac-data-permission` | **Date**: 2025-11-18 | **Spec**: [spec.md](./spec.md)
|
||
**Input**: Feature specification from `/specs/004-rbac-data-permission/spec.md`
|
||
|
||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
||
|
||
## Summary
|
||
|
||
实现完整的 RBAC 权限系统和基于 owner_id + shop_id 的自动数据权限过滤机制。核心功能包括:(1) 创建 5 个 RBAC 相关数据库表(账号、角色、权限、账号-角色关联、角色-权限关联)和对应的 GORM 模型,支持层级关系和软删除;(2) 实现 GORM Scopes 自动数据权限过滤,根据当前用户 ID 递归查询所有下级 ID 并结合 shop_id 双重过滤,使用 Redis 缓存优化性能;(3) 将 main 函数重构为多个独立的初始化函数,并将路由按业务模块拆分到 internal/routes/ 目录。
|
||
|
||
## Technical Context
|
||
|
||
**Language/Version**: Go 1.25.4
|
||
**Primary Dependencies**: Fiber v2.x (HTTP 框架), GORM v1.25.x (ORM), Viper (配置管理), Zap + Lumberjack.v2 (日志), sonic (JSON 序列化), Asynq v0.24.x (异步任务队列), golang-migrate (数据库迁移)
|
||
**Storage**: PostgreSQL 14+ (主数据库), Redis 6.0+ (缓存和任务队列存储)
|
||
**Testing**: Go 标准 testing 框架, testcontainers (集成测试)
|
||
**Target Platform**: Linux 服务器 (后端 API 服务)
|
||
**Project Type**: single (单体后端应用)
|
||
**Performance Goals**: API 响应时间 P95 < 200ms, P99 < 500ms; 数据库查询 P95 < 50ms, P99 < 100ms; 递归查询下级 ID P95 < 50ms, P99 < 100ms (含 Redis 缓存); 支持至少 5 层用户层级
|
||
**Constraints**: 内存使用 < 500MB (API 服务正常负载); 数据库连接池 MaxOpenConns=25; Redis 连接池 PoolSize=10; 下级 ID 缓存 30 分钟过期
|
||
**Scale/Scope**: 5 个 RBAC 表; 支持多租户数据隔离; 递归层级深度 ≥5 层; 账号-角色-权限多对多关联; 主函数重构(≤100 行)和路由模块化(6+ 模块文件)
|
||
|
||
## Constitution Check
|
||
|
||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||
|
||
**Tech Stack Adherence**:
|
||
- [x] Feature uses Fiber + GORM + Viper + Zap + Lumberjack.v2 + Validator + sonic JSON + Asynq + PostgreSQL
|
||
- [x] No native calls bypass framework (no `database/sql`, `net/http`, `encoding/json` direct use)
|
||
- [x] All HTTP operations use Fiber framework
|
||
- [x] All database operations use GORM
|
||
- [x] All async tasks use Asynq
|
||
- [x] Uses Go official toolchain: `go fmt`, `go vet`, `golangci-lint`
|
||
- [x] Uses Go Modules for dependency management
|
||
|
||
**Code Quality Standards**:
|
||
- [x] Follows Handler → Service → Store → Model architecture
|
||
- [x] Handler layer only handles HTTP, no business logic
|
||
- [x] Service layer contains business logic with cross-module support
|
||
- [x] Store layer manages all data access with transaction support
|
||
- [x] Uses dependency injection via struct fields (not constructor patterns)
|
||
- [x] Unified error codes in `pkg/errors/`
|
||
- [x] Unified API responses via `pkg/response/`
|
||
- [x] All constants defined in `pkg/constants/`
|
||
- [x] All Redis keys managed via key generation functions (no hardcoded strings)
|
||
- [x] **No hardcoded magic numbers or strings (3+ occurrences must be constants)**
|
||
- [x] **Defined constants are used instead of hardcoding duplicate values**
|
||
- [x] **Code comments prefer Chinese for readability (implementation comments in Chinese)**
|
||
- [x] **Log messages use Chinese (Info/Warn/Error/Debug logs in Chinese)**
|
||
- [x] **Error messages support Chinese (user-facing errors have Chinese messages)**
|
||
- [x] All exported functions/types have Go-style doc comments
|
||
- [x] Code formatted with `gofmt`
|
||
- [x] Follows Effective Go and Go Code Review Comments
|
||
|
||
**Documentation Standards** (Constitution Principle VII):
|
||
- [ ] Feature summary docs placed in `docs/{feature-id}/` mirroring `specs/{feature-id}/`
|
||
- [ ] Summary doc filenames use Chinese (功能总结.md, 使用指南.md, etc.)
|
||
- [ ] Summary doc content uses Chinese
|
||
- [ ] README.md updated with brief Chinese summary (2-3 sentences)
|
||
- [ ] Documentation is concise for first-time contributors
|
||
|
||
**Go Idiomatic Design**:
|
||
- [x] Package structure is flat (max 2-3 levels), organized by feature
|
||
- [x] Interfaces are small (1-3 methods), defined at use site
|
||
- [x] No Java-style patterns: no I-prefix, no Impl-suffix, no getters/setters
|
||
- [x] Error handling is explicit (return errors, no panic/recover abuse)
|
||
- [x] Uses composition over inheritance
|
||
- [x] Uses goroutines and channels (not thread pools)
|
||
- [x] Uses `context.Context` for cancellation and timeouts
|
||
- [x] Naming follows Go conventions: short receivers, consistent abbreviations (URL, ID, HTTP)
|
||
- [x] No Hungarian notation or type prefixes
|
||
- [x] Simple constructors (New/NewXxx), no Builder pattern unless necessary
|
||
|
||
**Testing Standards**:
|
||
- [ ] Unit tests for all core business logic (Service layer)
|
||
- [ ] Integration tests for all API endpoints
|
||
- [ ] Tests use Go standard testing framework
|
||
- [ ] Test files named `*_test.go` in same directory
|
||
- [ ] Test functions use `Test` prefix, benchmarks use `Benchmark` prefix
|
||
- [ ] Table-driven tests for multiple test cases
|
||
- [ ] Test helpers marked with `t.Helper()`
|
||
- [ ] Tests are independent (no external service dependencies)
|
||
- [ ] Target coverage: 70%+ overall, 90%+ for core business
|
||
|
||
**User Experience Consistency**:
|
||
- [x] All APIs use unified JSON response format
|
||
- [x] Error responses include clear error codes and bilingual messages
|
||
- [x] RESTful design principles followed
|
||
- [x] Unified pagination parameters (page, page_size, total)
|
||
- [x] Time fields use ISO 8601 format (RFC3339)
|
||
- [x] Currency amounts use integers (cents) to avoid float precision issues
|
||
|
||
**Performance Requirements**:
|
||
- [x] API response time (P95) < 200ms, (P99) < 500ms
|
||
- [x] Batch operations use bulk queries/inserts
|
||
- [x] All database queries have appropriate indexes
|
||
- [x] List queries implement pagination (default 20, max 100)
|
||
- [x] Non-realtime operations use async tasks
|
||
- [x] Database and Redis connection pools properly configured
|
||
- [x] Uses goroutines/channels for concurrency (not thread pools)
|
||
- [x] Uses `context.Context` for timeout control
|
||
- [x] Uses `sync.Pool` for frequently allocated objects
|
||
|
||
**Access Logging Standards** (Constitution Principle VIII):
|
||
- [ ] ALL HTTP requests logged to access.log without exception
|
||
- [ ] Request parameters (query + body) logged (limited to 50KB)
|
||
- [ ] Response parameters (body) logged (limited to 50KB)
|
||
- [ ] Logging happens via centralized Logger middleware (pkg/logger/Middleware())
|
||
- [ ] No middleware bypasses access logging (including auth failures, rate limits)
|
||
- [ ] Body truncation indicates "... (truncated)" when over 50KB limit
|
||
- [ ] Access log includes all required fields: method, path, query, status, duration_ms, request_id, ip, user_agent, user_id, request_body, response_body
|
||
|
||
**Error Handling Standards** (Constitution Principle X):
|
||
- [x] All API error responses use unified JSON format (via pkg/errors/ global ErrorHandler)
|
||
- [x] Handler layer errors return error (not manual JSON responses)
|
||
- [x] Business errors use pkg/errors.New() or pkg/errors.Wrap() with error codes
|
||
- [x] All error codes defined in pkg/errors/codes.go
|
||
- [x] All panics caught by Recover middleware and converted to 500 responses
|
||
- [x] Error logs include complete request context (Request ID, path, method, params)
|
||
- [x] 5xx server errors auto-sanitized (generic message to client, full error in logs)
|
||
- [x] 4xx client errors may return specific business messages
|
||
- [x] No panic in business code (except unrecoverable programming errors)
|
||
- [x] No manual error response construction in Handler (c.Status().JSON())
|
||
- [x] Error codes follow classification: 0=success, 1xxx=client (4xx), 2xxx=server (5xx)
|
||
- [x] Recover middleware registered first in middleware chain
|
||
- [x] Panic recovery logs complete stack trace
|
||
- [x] Single request panic does not affect other requests
|
||
|
||
**Database Design Principles** (Constitution Principle IX):
|
||
- [x] Database tables MUST NOT have foreign key constraints
|
||
- [x] GORM models MUST NOT use ORM association tags (foreignKey, hasMany, belongsTo, etc.)
|
||
- [x] Table relationships maintained manually via ID fields
|
||
- [x] Associated data queries are explicit in code, not ORM magic
|
||
- [x] Model structs ONLY contain simple fields, no nested model references
|
||
- [x] Migration scripts validated (no FK constraints, no triggers for relationships)
|
||
- [x] Time fields (created_at, updated_at) handled by GORM, not database triggers
|
||
|
||
## Project Structure
|
||
|
||
### Documentation (this feature)
|
||
|
||
**设计文档(specs/ 目录)**:开发前的规划和设计
|
||
```text
|
||
specs/[###-feature]/
|
||
├── plan.md # This file (/speckit.plan command output)
|
||
├── research.md # Phase 0 output (/speckit.plan command)
|
||
├── data-model.md # Phase 1 output (/speckit.plan command)
|
||
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
||
├── contracts/ # Phase 1 output (/speckit.plan command)
|
||
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
||
```
|
||
|
||
**总结文档(docs/ 目录)**:开发完成后的总结和使用指南(遵循 Constitution Principle VII)
|
||
```text
|
||
docs/[###-feature]/
|
||
├── 功能总结.md # 功能概述、核心实现、技术要点(MUST 使用中文命名和内容)
|
||
├── 使用指南.md # 如何使用该功能的详细说明(MUST 使用中文命名和内容)
|
||
└── 架构说明.md # 架构设计和技术决策(可选,MUST 使用中文命名和内容)
|
||
```
|
||
|
||
**README.md 更新**:每次完成功能后 MUST 在 README.md 添加简短描述(2-3 句话,中文)
|
||
|
||
### Source Code (repository root)
|
||
|
||
本功能采用单体后端应用结构(Option 1: Single project),遵循 Handler → Service → Store → Model 分层架构。
|
||
|
||
```text
|
||
internal/
|
||
├── model/ # 数据模型和 DTO
|
||
│ ├── account.go # Account 模型(新增)
|
||
│ ├── account_dto.go # Account DTO(新增)
|
||
│ ├── role.go # Role 模型(新增)
|
||
│ ├── role_dto.go # Role DTO(新增)
|
||
│ ├── permission.go # Permission 模型(新增)
|
||
│ ├── permission_dto.go # Permission DTO(新增)
|
||
│ ├── account_role.go # AccountRole 关联表模型(新增)
|
||
│ ├── account_role_dto.go # AccountRole DTO(新增)
|
||
│ ├── role_permission.go # RolePermission 关联表模型(新增)
|
||
│ ├── role_permission_dto.go # RolePermission DTO(新增)
|
||
│ # 注1: data_transfer_log.go 是未来功能,当前 MVP 不包含
|
||
│ # 注2: user.go 和 order.go 是之前的示例代码,未来实际业务表需自行添加 owner_id/shop_id 字段
|
||
│
|
||
├── handler/ # HTTP 处理层
|
||
│ ├── account.go # 账号管理 Handler(新增)
|
||
│ ├── role.go # 角色管理 Handler(新增)
|
||
│ └── permission.go # 权限管理 Handler(新增)
|
||
│ # 注: user.go 和 order.go 是之前的示例,实际业务 Handler 由业务需求决定
|
||
│
|
||
├── service/ # 业务逻辑层
|
||
│ ├── account/ # 账号服务(新增)
|
||
│ │ └── service.go
|
||
│ ├── role/ # 角色服务(新增)
|
||
│ │ └── service.go
|
||
│ └── permission/ # 权限服务(新增)
|
||
│ └── service.go
|
||
│ # 注: user 和 order 是之前的示例,实际业务服务由业务需求决定
|
||
│
|
||
├── store/ # 数据访问层
|
||
│ ├── options.go # Store 查询选项(新增)
|
||
│ └── postgres/ # PostgreSQL 实现
|
||
│ ├── scopes.go # GORM Scopes(数据权限过滤)(新增)
|
||
│ ├── account_store.go # 账号 Store(新增)
|
||
│ ├── role_store.go # 角色 Store(新增)
|
||
│ ├── permission_store.go # 权限 Store(新增)
|
||
│ ├── account_role_store.go # 账号-角色 Store(新增)
|
||
│ └── role_permission_store.go # 角色-权限 Store(新增)
|
||
│ # 注1: data_transfer_log_store.go 是未来功能,当前 MVP 不包含
|
||
│ # 注2: user_store 和 order_store 是之前的示例,未来业务 Store 需应用 DataPermissionScope
|
||
│
|
||
└── routes/ # 路由注册(新增目录)
|
||
├── routes.go # 路由总入口(新增)
|
||
├── account.go # 账号路由(新增)
|
||
├── role.go # 角色路由(新增)
|
||
├── permission.go # 权限路由(新增)
|
||
├── task.go # 任务路由(新增)
|
||
└── health.go # 健康检查路由(新增)
|
||
# 注: user.go 和 order.go 是之前的示例,实际业务路由由业务需求决定
|
||
|
||
pkg/
|
||
├── constants/ # 常量定义
|
||
│ ├── constants.go # 业务常量(需添加 RBAC 常量)
|
||
│ └── redis.go # Redis key 生成函数(需添加 RedisAccountSubordinatesKey)
|
||
│
|
||
├── middleware/ # 中间件
|
||
│ └── auth.go # 认证中间件(需添加 Context 辅助函数)
|
||
│
|
||
├── errors/ # 错误处理
|
||
│ └── codes.go # 错误码(需添加 RBAC 相关错误码)
|
||
│
|
||
└── response/ # 统一响应
|
||
└── response.go # 响应结构(已有)
|
||
|
||
cmd/
|
||
└── api/
|
||
└── main.go # 主函数(需重构为编排函数)
|
||
|
||
migrations/ # 数据库迁移
|
||
├── 000002_rbac_data_permission.up.sql # RBAC 表创建脚本(新增)
|
||
├── 000002_rbac_data_permission.down.sql # RBAC 表回滚脚本(新增)
|
||
├── 000003_add_owner_id_shop_id.up.sql # 业务表添加 owner_id/shop_id 示例(新增)
|
||
└── 000003_add_owner_id_shop_id.down.sql # 业务表回滚示例(新增)
|
||
# 注: 000004_data_transfer_log 迁移是未来功能,当前 MVP 不包含
|
||
|
||
tests/
|
||
├── integration/ # 集成测试
|
||
│ ├── account_test.go # 账号集成测试(新增)
|
||
│ ├── role_test.go # 角色集成测试(新增)
|
||
│ ├── permission_test.go # 权限集成测试(新增)
|
||
│ ├── account_role_test.go # 账号-角色关联测试(新增)
|
||
│ ├── role_permission_test.go # 角色-权限关联测试(新增)
|
||
│ └── data_permission_test.go # 数据权限过滤测试(新增)
|
||
│
|
||
└── unit/ # 单元测试
|
||
├── account_service_test.go # 账号 Service 测试(新增)
|
||
└── data_permission_test.go # 递归查询和缓存测试(新增)
|
||
```
|
||
|
||
**Structure Decision**: 本功能使用单体后端结构(单项目),严格遵循 Handler → Service → Store → Model 四层架构。新增 `internal/routes/` 目录用于路由模块化,将原本集中在 `main.go` 中的路由注册按业务模块拆分。所有 RBAC 相关的模型、Handler、Service、Store 都遵循相同的分层模式,确保代码组织一致性和可维护性。
|
||
|
||
## Complexity Tracking
|
||
|
||
> **Fill ONLY if Constitution Check has violations that must be justified**
|
||
|
||
| Violation | Why Needed | Simpler Alternative Rejected Because |
|
||
|-----------|------------|-------------------------------------|
|
||
| [e.g., 4th project] | [current need] | [why 3 projects insufficient] |
|
||
| [e.g., Repository pattern] | [specific problem] | [why direct DB access insufficient] |
|