Files
huang eaa70ac255 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>
2025-11-18 16:44:06 +08:00

617 lines
17 KiB
YAML
Raw Permalink 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.
openapi: 3.0.3
info:
title: Account Management API
description: RBAC 账号管理接口 - 支持账号的创建、查询、更新、删除和角色分配
version: 1.0.0
servers:
- url: http://localhost:8080/api/v1
description: Development server
tags:
- name: accounts
description: 账号管理
- name: account-roles
description: 账号-角色关联
paths:
/accounts:
post:
summary: 创建账号
description: 创建新账号,非 root 账号必须提供 parent_id密码使用 bcrypt 哈希
tags:
- accounts
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/CreateAccountRequest'
responses:
'200':
description: 创建成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/AccountResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
get:
summary: 查询账号列表
description: 分页查询账号列表,自动应用数据权限过滤(只返回自己和下级创建的账号)
tags:
- accounts
parameters:
- name: page
in: query
description: 页码(从 1 开始)
schema:
type: integer
default: 1
minimum: 1
- name: page_size
in: query
description: 每页大小
schema:
type: integer
default: 20
minimum: 1
maximum: 100
- name: username
in: query
description: 用户名模糊查询
schema:
type: string
- name: user_type
in: query
description: 用户类型过滤1=root, 2=平台, 3=代理, 4=企业)
schema:
type: integer
enum: [1, 2, 3, 4]
- name: status
in: query
description: 状态过滤0=禁用, 1=启用)
schema:
type: integer
enum: [0, 1]
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/ListAccountsResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
/accounts/{id}:
get:
summary: 查询账号详情
description: 根据 ID 查询账号详情,自动应用数据权限过滤
tags:
- accounts
parameters:
- name: id
in: path
required: true
description: 账号 ID
schema:
type: integer
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/AccountResponse'
'404':
description: 账号不存在或无权访问
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
put:
summary: 更新账号
description: 更新账号信息,禁止修改 parent_id 和 user_type
tags:
- accounts
parameters:
- name: id
in: path
required: true
description: 账号 ID
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/UpdateAccountRequest'
responses:
'200':
description: 更新成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
$ref: '#/components/schemas/AccountResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'404':
description: 账号不存在或无权访问
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
delete:
summary: 删除账号
description: 软删除账号,设置 deleted_at 字段,并清除该账号及所有上级的下级 ID 缓存
tags:
- accounts
parameters:
- name: id
in: path
required: true
description: 账号 ID
schema:
type: integer
responses:
'200':
description: 删除成功
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'404':
description: 账号不存在或无权访问
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
/accounts/{id}/roles:
post:
summary: 为账号分配角色
description: 批量为账号分配角色,已存在的关联会被忽略
tags:
- account-roles
parameters:
- name: id
in: path
required: true
description: 账号 ID
schema:
type: integer
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AssignRolesToAccountRequest'
responses:
'200':
description: 分配成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/AccountRoleResponse'
'400':
description: 请求参数错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'404':
description: 账号或角色不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
get:
summary: 查询账号的所有角色
description: 查询指定账号已分配的所有角色
tags:
- account-roles
parameters:
- name: id
in: path
required: true
description: 账号 ID
schema:
type: integer
responses:
'200':
description: 查询成功
content:
application/json:
schema:
allOf:
- $ref: '#/components/schemas/ApiResponse'
- type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/RoleResponse'
'404':
description: 账号不存在或无权访问
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
/accounts/{account_id}/roles/{role_id}:
delete:
summary: 移除账号的角色
description: 软删除账号-角色关联
tags:
- account-roles
parameters:
- name: account_id
in: path
required: true
description: 账号 ID
schema:
type: integer
- name: role_id
in: path
required: true
description: 角色 ID
schema:
type: integer
responses:
'200':
description: 移除成功
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'404':
description: 账号或角色不存在
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
'500':
description: 服务器错误
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponse'
components:
schemas:
ApiResponse:
type: object
required:
- code
- message
- timestamp
properties:
code:
type: integer
description: 错误码0 表示成功1xxx 表示客户端错误2xxx 表示服务端错误)
example: 0
message:
type: string
description: 响应消息
example: success
data:
type: object
description: 响应数据
timestamp:
type: string
format: date-time
description: 响应时间戳ISO 8601 格式)
example: "2025-11-18T15:30:00Z"
CreateAccountRequest:
type: object
required:
- username
- phone
- password
- user_type
properties:
username:
type: string
minLength: 3
maxLength: 20
pattern: '^[a-zA-Z0-9_]+$'
description: 用户名3-20 个字符,字母、数字、下划线)
example: admin001
phone:
type: string
pattern: '^1[3-9]\d{9}$'
description: 手机号11 位中国大陆手机号)
example: "13812345678"
password:
type: string
minLength: 8
description: 密码(最少 8 位,包含字母和数字)
example: "Password123"
user_type:
type: integer
enum: [1, 2, 3, 4]
description: 用户类型1=root, 2=平台, 3=代理, 4=企业)
example: 2
shop_id:
type: integer
nullable: true
description: 所属店铺 ID可选
example: 10
parent_id:
type: integer
nullable: true
description: 上级账号 ID非 root 用户必须提供)
example: 1
status:
type: integer
enum: [0, 1]
default: 1
description: 状态0=禁用, 1=启用)
example: 1
UpdateAccountRequest:
type: object
properties:
username:
type: string
minLength: 3
maxLength: 20
pattern: '^[a-zA-Z0-9_]+$'
description: 用户名(可选更新)
example: admin002
phone:
type: string
pattern: '^1[3-9]\d{9}$'
description: 手机号(可选更新)
example: "13812345679"
password:
type: string
minLength: 8
description: 密码(可选更新)
example: "NewPassword123"
status:
type: integer
enum: [0, 1]
description: 状态(可选更新)
example: 0
AccountResponse:
type: object
properties:
id:
type: integer
description: 账号 ID
example: 1
username:
type: string
description: 用户名
example: admin001
phone:
type: string
description: 手机号
example: "13812345678"
user_type:
type: integer
description: 用户类型1=root, 2=平台, 3=代理, 4=企业)
example: 2
shop_id:
type: integer
nullable: true
description: 所属店铺 ID
example: 10
parent_id:
type: integer
nullable: true
description: 上级账号 ID
example: 1
status:
type: integer
description: 状态0=禁用, 1=启用)
example: 1
creator:
type: integer
description: 创建人 ID
example: 1
updater:
type: integer
description: 更新人 ID
example: 1
created_at:
type: string
format: date-time
description: 创建时间
example: "2025-11-18T10:00:00Z"
updated_at:
type: string
format: date-time
description: 更新时间
example: "2025-11-18T10:00:00Z"
ListAccountsResponse:
type: object
properties:
items:
type: array
items:
$ref: '#/components/schemas/AccountResponse'
total:
type: integer
description: 总记录数
example: 100
page:
type: integer
description: 当前页码
example: 1
page_size:
type: integer
description: 每页大小
example: 20
AssignRolesToAccountRequest:
type: object
required:
- role_ids
properties:
role_ids:
type: array
items:
type: integer
minItems: 1
description: 角色 ID 列表
example: [1, 2, 3]
AccountRoleResponse:
type: object
properties:
id:
type: integer
description: 关联 ID
example: 1
account_id:
type: integer
description: 账号 ID
example: 1
role_id:
type: integer
description: 角色 ID
example: 1
status:
type: integer
description: 状态0=禁用, 1=启用)
example: 1
creator:
type: integer
description: 创建人 ID
example: 1
created_at:
type: string
format: date-time
description: 创建时间
example: "2025-11-18T10:00:00Z"
RoleResponse:
type: object
properties:
id:
type: integer
description: 角色 ID
example: 1
role_name:
type: string
description: 角色名称
example: 平台管理员
role_desc:
type: string
description: 角色描述
example: 平台系统管理员角色
role_type:
type: integer
description: 角色类型1=超级, 2=代理, 3=企业)
example: 1
status:
type: integer
description: 状态0=禁用, 1=启用)
example: 1
created_at:
type: string
format: date-time
description: 创建时间
example: "2025-11-18T10:00:00Z"
securitySchemes:
BearerAuth:
type: http
scheme: bearer
bearerFormat: JWT
security:
- BearerAuth: []