ether-docs/02-DESIGN/detail/DETAIL-AUTH.md

1059 lines
46 KiB
Markdown
Raw 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.

# 身份与权限域 - 详细设计
**文档版本**: v1.0
**生成日期**: 2026-05-18
**数据来源**: module-auth 实际代码 + REVERSE-AUTH.md
**对照需求**: 05-AUTH.md
---
## 一、功能点清单
| 功能ID | 功能名称 | 优先级 | 实现状态 | 对应需求ID |
|--------|----------|--------|----------|------------|
| AUTH-001 | 用户登录 | P0 | 已实现 | 05-AUTH-001 |
| AUTH-002 | 用户登出 | P0 | 已实现 | 05-AUTH-002 |
| AUTH-003 | 获取当前用户 | P0 | 已实现 | 05-AUTH-003 |
| AUTH-004 | Token刷新 | P0 | 已实现 | 05-AUTH-004 |
| AUTH-005 | 用户CRUD | P0 | 已实现 | 05-AUTH-010 |
| AUTH-006 | 修改密码 | P0 | 已实现 | 05-AUTH-011 |
| AUTH-007 | 分配用户角色 | P0 | 已实现 | 05-AUTH-020 |
| AUTH-008 | 用户项目关联 | P1 | 已实现 | 05-AUTH-030 |
| AUTH-009 | 企业员工管理 | P1 | 已实现 | 05-AUTH-012 |
| AUTH-010 | 角色CRUD | P0 | 已实现 | 05-AUTH-020 |
| AUTH-011 | 角色权限分配 | P0 | 已实现 | 05-AUTH-021 |
| AUTH-012 | 按项目查询角色 | P1 | 已实现 | 05-AUTH-022 |
| AUTH-013 | 权限CRUD | P0 | 已实现 | 05-AUTH-025 |
| AUTH-014 | 按类型查询权限 | P1 | 已实现 | 05-AUTH-026 |
| AUTH-015 | 菜单权限查询 | P1 | 已实现 | 05-AUTH-027 |
| AUTH-016 | 部门树管理 | P1 | 已实现 | 05-AUTH-040 |
| AUTH-017 | 部门成员查询 | P1 | 已实现 | 05-AUTH-041 |
| AUTH-018 | 项目成员管理 | P0 | 已实现 | 05-AUTH-030 |
| AUTH-019 | 登录失败锁定 | P0 | 已实现 | 05-AUTH-005 |
| AUTH-020 | 密码强度校验 | P0 | 已实现 | 05-AUTH-006 |
| AUTH-021 | 弱密码检测 | P1 | 已实现 | 05-AUTH-007 |
| AUTH-022 | 审计日志查询 | P1 | 已实现 | 05-AUTH-050 |
| AUTH-023 | 审计日志统计 | P2 | 已实现 | 05-AUTH-051 |
| AUTH-024 | 系统配置管理 | P1 | 已实现 | 05-AUTH-060 |
| AUTH-025 | 数据访问授权 | P2 | 已实现(@Deprecated) | 05-AUTH-035 |
| AUTH-026 | 住户认证流程 | P1 | 已实现 | 05-AUTH-015 |
| AUTH-027 | 住户房屋绑定 | P1 | 已实现 | 05-AUTH-016 |
| AUTH-028 | 项目员工角色分配 | P1 | 已实现 | 05-AUTH-031 |
---
## 二、数据结构设计
### 2.1 实体定义
#### 2.1.1 User用户
**表名**: `auth_user`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 用户唯一标识符 |
| username | VARCHAR(50) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_]+$`, 3-50位 | 登录用户名 |
| password | VARCHAR(255) | NOT NULL, @JsonIgnore | BCrypt加密密码 |
| salt | VARCHAR | @JsonIgnore | 密码盐值BCrypt模式下冗余 |
| realName | VARCHAR(50) | | 真实姓名 |
| phone | VARCHAR(20) | 正则`^1[3-9]\d{9}$` | 手机号码 |
| email | VARCHAR(100) | @Email | 电子邮箱 |
| avatar | VARCHAR | | 头像URL |
| status | VARCHAR(20) | NOT NULL, 默认ACTIVE, @Enumerated(STRING) | 用户状态枚举 |
| userType | VARCHAR(20) | NOT NULL | 用户类型ENTERPRISE/PROJECT_STAFF/RESIDENT/CUSTOMER |
| deptId | UUID | | 所属部门ID |
| lastLoginTime | LocalDateTime | | 最后登录时间 |
| lastLoginIp | VARCHAR | | 最后登录IP |
| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 |
| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 |
| createdBy | UUID | | 创建人ID |
**索引**:
- `auth_user_username_key` → username (UNIQUE)
**关系**:
- `roles` → M2M → Role通过 `auth_user_role` 中间表LAZY加载
**中间表: auth_user_role**
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| user_id | UUID | FK → auth_user, fk_auth_user_role_user | 用户ID |
| role_id | UUID | FK → auth_role, fk_auth_user_role_role | 角色ID |
---
#### 2.1.2 Role角色
**表名**: `auth_role`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 角色唯一标识符 |
| code | VARCHAR(50) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_]+$`, 2-50位 | 角色代码 |
| name | VARCHAR(50) | NOT NULL, 2-50位 | 角色名称 |
| description | VARCHAR(200) | | 角色描述 |
| type | VARCHAR(20) | NOT NULL, @Enumerated(STRING) | 角色类型枚举 |
| dataScope | VARCHAR(20) | 默认SELF, @Enumerated(STRING) | 数据范围枚举 |
| projectId | UUID | columnDefinition=uuid | 所属项目IDNULL=系统级) |
| status | VARCHAR(20) | NOT NULL, 默认ENABLED, @Enumerated(STRING) | 角色状态枚举 |
| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 |
| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 |
**索引**:
- `auth_role_code_key` → code (UNIQUE)
**关系**:
- `permissions` → M2M → Permission通过 `auth_role_permission` 中间表LAZY加载
**中间表: auth_role_permission**
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| role_id | UUID | FK → auth_role, fk_auth_role_permission_role | 角色ID |
| permission_id | UUID | FK → auth_permission, fk_auth_role_permission_permission | 权限ID |
---
#### 2.1.3 Permission权限
**表名**: `auth_permission`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 权限唯一标识符 |
| code | VARCHAR(100) | NOT NULL, UNIQUE, 正则`^[a-zA-Z0-9_:]+$`, 2-100位 | 权限代码,格式`module:resource:action` |
| name | VARCHAR(100) | NOT NULL, 2-100位 | 权限名称 |
| type | VARCHAR(20) | | 权限类型MENU/BUTTON/API |
| resource | VARCHAR(50) | | 资源路径API类型 |
| method | VARCHAR(50) | | HTTP方法API类型 |
| description | VARCHAR(200) | | 权限描述 |
| parentCode | VARCHAR(50) | | 父权限代码(树形结构) |
| path | VARCHAR(200) | | 路由路径(菜单类型) |
| component | VARCHAR(200) | | 组件路径(菜单类型) |
| icon | VARCHAR(100) | | 图标名称(菜单类型) |
| sortOrder | Integer | | 排序序号 |
| visible | Boolean | NOT NULL, 默认true | 是否可见 |
| createdAt | LocalDateTime | @PrePersist自动填充 | 创建时间 |
| updatedAt | LocalDateTime | @PreUpdate自动填充 | 更新时间 |
**索引**:
- `auth_permission_code_key` → code (UNIQUE)
**关系**:
- `roles` → M2M → Role通过 `auth_role_permission` 中间表LAZY加载@JsonIgnoreProperties避免循环序列化
---
#### 2.1.4 Dept部门
**表名**: `dept`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 部门唯一标识符 |
| parentId | UUID | | 上级部门IDNULL=顶级) |
| deptName | VARCHAR(100) | NOT NULL | 部门名称 |
| deptType | VARCHAR(20) | 默认"ADMIN" | 部门类型 |
| leaderId | UUID | | 部门负责人ID |
| sortOrder | Integer | | 排序序号 |
| status | VARCHAR(20) | 默认"ACTIVE" | 状态ACTIVE/DISABLED |
| createdAt | LocalDateTime | @PrePersist自动填充 | 创建时间 |
| updatedAt | LocalDateTime | @PreUpdate自动填充 | 更新时间 |
| createdBy | UUID | column: created_by | 创建人ID |
| updatedBy | UUID | column: updated_by | 更新人ID |
**关系**:
- 自引用树形结构parentId → Dept.id
---
#### 2.1.5 AuditLog审计日志
**表名**: `sys_audit_log`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 日志ID |
| userId | UUID | column: user_id | 操作用户ID |
| username | VARCHAR(64) | NOT NULL | 操作用户名 |
| operation | VARCHAR(128) | NOT NULL | 操作描述 |
| module | VARCHAR(64) | NOT NULL | 模块标识 |
| action | VARCHAR(64) | NOT NULL, @Enumerated(STRING) | 操作类型枚举 |
| targetType | VARCHAR(64) | | 目标类型 |
| targetId | VARCHAR(64) | | 目标ID |
| content | VARCHAR(2000) | | 操作内容 |
| params | LONGVARCHAR(5000) | | 请求参数 |
| result | LONGVARCHAR(5000) | | 操作结果 |
| ipAddress | VARCHAR(64) | column: ip_address | IP地址 |
| userAgent | VARCHAR(512) | | 用户代理 |
| requestUrl | VARCHAR(512) | column: request_url | 请求URL |
| requestMethod | VARCHAR(16) | column: request_method | 请求方法 |
| executionTimeMs | Integer | column: execution_time_ms | 执行耗时(ms) |
| status | VARCHAR(16) | 默认SUCCESS, @Enumerated(STRING) | 状态枚举 |
| errorMsg | VARCHAR(2000) | column: error_msg | 错误信息 |
| createdAt | LocalDateTime | NOT NULL, @CreationTimestamp, 不可更新 | 创建时间 |
| tenantId | UUID | column: tenant_id | 租户ID |
**索引**:
- `idx_audit_log_created_at` → createdAt
- `idx_audit_log_user_id` → userId
- `idx_audit_log_module` → module
- `idx_audit_log_action` → action
- `idx_al_user_createdat` → userId, createdAt DESC
---
#### 2.1.6 EnterpriseUser企业员工
**表名**: `enterprise_user`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| userId | UUID | PK, FK → auth_user | 用户ID共享主键 |
| employeeNo | VARCHAR(50) | | 员工工号 |
| deptId | UUID | | 部门ID |
| position | VARCHAR(50) | | 职位 |
| entryDate | LocalDate | | 入职日期 |
| userCategory | VARCHAR(50) | | 员工类别ENTERPRISE/MANAGEMENT |
**关系**:
- `user`@OneToOne → User@MapsId共享主键
---
#### 2.1.7 ProjectStaff项目员工
**表名**: `project_staff`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 主键ID |
| userId | UUID | FK → auth_user, insertable=false, updatable=false | 用户ID |
| projectId | UUID | column: project_id | 所属项目ID |
| deptId | UUID | column: dept_id | 所属部门ID |
| staffType | VARCHAR(50) | | 员工类型SECURITY/CLEANING/GARDEN/MAINTENANCE/CUSTOMER_SERVICE/GENERAL |
| shiftType | VARCHAR(20) | | 班次类型DAY/NIGHT/ROTATION |
| leaderId | UUID | | 班组长ID |
| assignmentStatus | VARCHAR(20) | | 在岗状态ASSIGNED/ON_LEAVE/TRANSFERRED |
| createdAt | LocalDateTime | column: created_at, 不可更新 | 创建时间 |
| updatedAt | LocalDateTime | column: updated_at | 更新时间 |
**关系**:
- `user`@OneToOne → UserFK: fk_project_staff_user
- `staffRoles`@OneToMany → ProjectStaffRolemappedBy="staff", CASCADE=ALL, orphanRemoval=true
---
#### 2.1.8 ProjectStaffRole项目员工角色
**表名**: `project_staff_role`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 主键ID |
| staff_id | UUID | FK → project_staff, NOT NULL | 关联项目员工 |
| role_id | UUID | FK → auth_role, NOT NULL | 关联角色 |
| createdAt | LocalDateTime | column: created_at, @PrePersist自动填充 | 创建时间 |
**关系**:
- `staff`@ManyToOne(LAZY) → ProjectStaff
- `role`@ManyToOne(EAGER) → Role
---
#### 2.1.9 Resident住户
**表名**: `resident`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| userId | UUID | PK, FK → auth_user | 用户ID共享主键 |
| idCard | VARCHAR(18) | @JsonIgnore | 身份证号码 |
| residentType | VARCHAR(20) | | 住户类型OWNER/FAMILY/TENANT |
| verificationStatus | VARCHAR(20) | | 认证状态UNVERIFIED/PENDING/VERIFIED/REJECTED |
| verifiedAt | LocalDateTime | | 认证时间 |
| verifiedBy | UUID | | 认证人ID |
| createdAt | LocalDateTime | NOT NULL, @PrePersist自动填充 | 创建时间 |
| updatedAt | LocalDateTime | NOT NULL, @PreUpdate自动填充 | 更新时间 |
| createdBy | UUID | column: created_by | 创建人ID |
| updatedBy | UUID | column: updated_by | 更新人ID |
**关系**:
- `user`@OneToOne → User@MapsId共享主键
---
#### 2.1.10 Space房屋空间
**表名**: `space`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 空间ID |
| projectId | UUID | column: project_id | 所属项目ID |
| building | VARCHAR(50) | | 楼栋 |
| unit | VARCHAR(50) | | 单元 |
| roomNo | VARCHAR(50) | | 房号 |
| spaceType | VARCHAR(20) | | 房屋类型RESIDENTIAL/COMMERCIAL |
| floor | Integer | | 楼层 |
| unitArea | BigDecimal | | 建筑面积(㎡) |
| status | VARCHAR(20) | | 状态 |
---
#### 2.1.11 ResidentSpace住户-房屋关联)
**表名**: `resident_space`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 关联记录ID |
| userId | UUID | NOT NULL, column: user_id | 用户ID |
| spaceId | UUID | NOT NULL, column: space_id | 房屋ID |
| relationType | VARCHAR(20) | | 关系类型OWNER/FAMILY/TENANT |
| bindingStatus | VARCHAR(20) | | 绑定状态PENDING/ACTIVE/EXPIRED/CANCELLED |
| startDate | LocalDate | | 生效日期 |
| endDate | LocalDate | | 失效日期NULL=永久) |
---
#### 2.1.12 UserProject用户-项目关联)
**表名**: `user_project`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 关联记录ID |
| userId | UUID | NOT NULL, column: user_id | 用户ID |
| projectId | UUID | NOT NULL, column: project_id | 项目ID |
| roleInProject | VARCHAR | NOT NULL, 默认"member", column: role_in_project | 项目中角色leader/member/viewer |
| joinedAt | LocalDateTime | NOT NULL, 默认当前时间, column: joined_at | 加入时间 |
---
#### 2.1.13 SysConfig系统配置
**表名**: `sys_config`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 配置项ID |
| configKey | VARCHAR(128) | NOT NULL, UNIQUE, column: config_key | 配置键 |
| configValue | VARCHAR(5000) | column: config_value | 配置值 |
| description | VARCHAR(256) | | 配置描述 |
| createdAt | LocalDateTime | NOT NULL, @CreationTimestamp, 不可更新 | 创建时间 |
| updatedAt | LocalDateTime | NOT NULL, @UpdateTimestamp | 更新时间 |
---
#### 2.1.14 DataAccess数据访问授权@Deprecated
**表名**: `biz_data_access`
| 字段名 | 类型 | 约束 | 说明 |
|--------|------|------|------|
| id | UUID | PK, 自动生成 | 授权记录ID |
| dataType | VARCHAR | NOT NULL, column: data_type | 数据类型 |
| dataId | UUID | NOT NULL, column: data_id | 数据ID |
| accessType | VARCHAR | NOT NULL, column: access_type | 访问者类型 |
| accessId | UUID | NOT NULL, column: access_id | 访问者ID |
| accessLevel | VARCHAR | NOT NULL, 默认"read", column: access_level | 访问级别 |
| grantedBy | UUID | column: granted_by | 授权人ID |
| grantedAt | LocalDateTime | NOT NULL, 默认当前时间, column: granted_at | 授权时间 |
> **注意**: DataAccess 实体及对应控制器已标记 @Deprecated后续版本将移除。
---
### 2.2 枚举定义
#### UserStatus
| 值 | 描述 |
|----|------|
| ACTIVE | 正常 |
| LOCKED | 锁定 |
| DISABLED | 禁用 |
#### RoleType
| 值 | 描述 |
|----|------|
| SYSTEM | 系统级,可访问所有项目数据 |
| PROJECT | 项目级,仅可访问指定项目数据 |
| DEPARTMENT | 部门级,仅可访问本部门数据 |
#### DataScope
| 值 | 描述 |
|----|------|
| ALL | 全部数据 |
| PROJECT | 本项目数据 |
| DEPARTMENT | 本部门数据 |
| SELF | 仅本人数据 |
#### RoleStatus
| 值 | 描述 |
|----|------|
| ENABLED | 启用 |
| DISABLED | 禁用 |
#### DeptType
| 值 | 描述 |
|----|------|
| ADMIN | 行政管理 |
| ENGINEERING | 工程部 |
| SECURITY | 安保部 |
| CS | 客服部 |
| CLEANING | 保洁部 |
#### ActionType
| 值 | 描述 |
|----|------|
| CREATE | 创建 |
| UPDATE | 修改 |
| DELETE | 删除 |
| QUERY | 查询 |
| VIEW | 查看 |
| LOGIN | 登录 |
| LOGOUT | 登出 |
| EXPORT | 导出 |
| IMPORT | 导入 |
| ASSIGN | 分配 |
| REVOKE | 撤销 |
---
### 2.3 ER关系图Mermaid
```mermaid
erDiagram
User ||--o{ Role : "auth_user_role M2M"
Role ||--o{ Permission : "auth_role_permission M2M"
User ||--|| EnterpriseUser : "共享主键 1:1"
User ||--|| ProjectStaff : "FK 1:1"
User ||--|| Resident : "共享主键 1:1"
ProjectStaff ||--o{ ProjectStaffRole : "1:N 级联删除"
ProjectStaffRole }o--|| Role : "N:1 EAGER"
Resident ||--o{ ResidentSpace : "1:N"
ResidentSpace }o--|| Space : "N:1"
User ||--o{ UserProject : "1:N"
UserProject }o--|| Project : "N:1"
Dept ||--o{ Dept : "自引用树形 parentId"
AuditLog {
UUID id PK
UUID userId
String username
String operation
String module
String action
String targetType
String targetId
String status
}
SysConfig {
UUID id PK
String configKey UK
String configValue
}
DataAccess {
UUID id PK
String dataType
UUID dataId
String accessType
UUID accessId
String accessLevel
}
```
---
## 三、API设计
### 3.1 AuthController -- 认证管理
**基础路径**: `/api/auth`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| AUTH-API-001 | POST | `/login` | 用户登录 | Body: `{username: String(3-50), password: String(8-128)}` | `{code, data: {token, userId, username, realName, roles}}` | 无(公开) | 用户名不存在; 密码错误; 账户锁定; 账户禁用; 登录失败超5次锁定10分钟 |
| AUTH-API-002 | POST | `/logout` | 用户登出 | Header: Authorization: Bearer {token} | `{code, data: null}` | 已登录 | Token无效 |
| AUTH-API-003 | GET | `/me` | 获取当前用户 | Header: Authorization: Bearer {token} | `{code, data: {username}}` | 已登录 | Token缺失; Token无效 |
| AUTH-API-004 | POST | `/refresh` | 刷新Token | Header: Authorization: Bearer {token} | `{code, data: {token}}` | 已登录 | Token缺失; Token无效; Token已过期 |
**登录响应详细格式**:
```json
{
"code": 200,
"data": {
"token": "eyJhbGciOiJIUzI1NiJ9...",
"userId": "uuid",
"username": "admin",
"realName": "管理员",
"roles": ["SYSTEM_ADMIN", "PROJECT_MANAGER"]
}
}
```
---
### 3.2 UserController -- 用户管理
**基础路径**: `/api/auth/users`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| USER-API-001 | GET | `/` | 分页查询用户列表 | page(int, 默认0), size(int, 默认10) | `{code, data: {content: [User], totalElements, totalPages, ...}}` | 已登录 | 分页参数越界 |
| USER-API-002 | GET | `/{id}` | 根据ID获取用户 | id(UUID, Path) | `{code, data: User}` | 已登录 | 用户不存在 |
| USER-API-003 | POST | `/` | 创建用户 | Body: User实体 | `{code, data: User}` | 管理员 | 用户名已存在; 字段校验失败 |
| USER-API-004 | PUT | `/{id}` | 更新用户 | id(UUID, Path), Body: User实体 | `{code, data: User}` | 管理员 | 用户不存在; 用户名冲突 |
| USER-API-005 | DELETE | `/{id}` | 删除用户 | id(UUID, Path) | `{code, data: null}` | 管理员 | 用户不存在; 用户有关联数据 |
| USER-API-006 | PUT | `/{id}/password` | 修改密码 | id(UUID, Path), Body: `{oldPassword: String(8-128), newPassword: String(8-128)}` | `{code, data: null}` | 本人或管理员 | 原密码错误; 新密码强度不足; 新密码为弱密码 |
| USER-API-007 | POST | `/{id}/roles` | 分配角色 | id(UUID, Path), Body: `[roleId1, roleId2, ...]` | `{code, data: null}` | 管理员 | 用户不存在; 角色ID不存在; 分配数量超限 |
| USER-API-008 | GET | `/{id}/projects` | 获取用户项目列表 | id(UUID, Path) | `{code, data: [UserProject]}` | 已登录 | 用户不存在 |
| USER-API-009 | POST | `/{id}/projects` | 添加用户到项目 | id(UUID, Path), Body: `{projectId: UUID, roleInProject: String}` | `{code, data: null}` | 管理员 | 用户不存在; 项目不存在; 已在该项目中 |
| USER-API-010 | DELETE | `/{id}/projects/{projectId}` | 从项目移除用户 | id(UUID, Path), projectId(UUID, Path) | `{code, data: null}` | 管理员 | 关联不存在 |
| USER-API-011 | GET | `/enterprise` | 获取企业员工列表 | 无 | `{code, data: [UserVO]}` | 已登录 | 无 |
**UserVO格式**:
```json
{
"id": "uuid",
"username": "zhangsan",
"realName": "张三",
"phone": "13800138000",
"email": "zhangsan@example.com",
"avatar": "url",
"status": "ACTIVE",
"userType": "ENTERPRISE",
"deptId": "uuid"
}
```
---
### 3.3 RoleController -- 角色管理
**基础路径**: `/api/auth/roles`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| ROLE-API-001 | GET | `/` | 分页查询角色列表 | page(int, 默认0), size(int, 默认10) | `{code, data: Page<Role>}` | 已登录 | 分页参数越界 |
| ROLE-API-002 | GET | `/{id}` | 根据ID获取角色 | id(UUID, Path) | `{code, data: Role}` | 已登录 | 角色不存在 |
| ROLE-API-003 | GET | `/project/{projectId}` | 根据项目ID查询角色 | projectId(UUID, Path) | `{code, data: [Role]}` | 已登录 | 项目不存在 |
| ROLE-API-004 | POST | `/` | 创建角色 | Body: Role实体 | `{code, data: Role}` | 管理员 | 角色代码已存在; 字段校验失败 |
| ROLE-API-005 | PUT | `/{id}` | 更新角色 | id(UUID, Path), Body: Role实体 | `{code, data: Role}` | 管理员 | 角色不存在; 角色代码冲突 |
| ROLE-API-006 | DELETE | `/{id}` | 删除角色 | id(UUID, Path) | `{code, data: null}` | 管理员 | 角色不存在; 角色已分配给用户 |
| ROLE-API-007 | POST | `/{id}/permissions` | 为角色分配权限 | id(UUID, Path), Body: `[permissionId1, permissionId2, ...]` | `{code, data: null}` | 管理员 | 角色不存在; 权限ID不存在 |
| ROLE-API-008 | GET | `/{id}/permissions` | 获取角色权限列表 | id(UUID, Path) | `{code, data: [Permission]}` | 已登录 | 角色不存在 |
| ROLE-API-009 | GET | `/{id}/users` | 获取拥有某角色的用户 | id(UUID, Path) | `{code, data: [User]}` | 已登录 | 角色不存在 |
---
### 3.4 PermissionController -- 权限管理
**基础路径**: `/api/auth/permissions`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| PERM-API-001 | GET | `/` | 分页查询权限列表 | page(int, 默认0), size(int, 默认10) | `{code, data: Page<Permission>}` | 已登录 | 分页参数越界 |
| PERM-API-002 | GET | `/{id}` | 根据ID获取权限 | id(UUID, Path) | `{code, data: Permission}` | 已登录 | 权限不存在 |
| PERM-API-003 | GET | `/type/{type}` | 根据类型查询权限 | type(String, Path): MENU/BUTTON/API | `{code, data: [Permission]}` | 已登录 | 无效类型 |
| PERM-API-004 | GET | `/menus` | 查询所有菜单权限 | 无 | `{code, data: [Permission]}` | 已登录 | 无 |
| PERM-API-005 | POST | `/` | 创建权限 | Body: Permission实体 | `{code, data: Permission}` | 管理员 | 权限代码已存在; 字段校验失败 |
| PERM-API-006 | PUT | `/{id}` | 更新权限 | id(UUID, Path), Body: Permission实体 | `{code, data: Permission}` | 管理员 | 权限不存在; 权限代码冲突 |
| PERM-API-007 | DELETE | `/{id}` | 删除权限 | id(UUID, Path) | `{code, data: null}` | 管理员 | 权限不存在; 权限已分配给角色 |
---
### 3.5 DeptController -- 部门管理
**基础路径**: `/api/auth/depts`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| DEPT-API-001 | GET | `/tree` | 获取部门树 | 无 | `{code, data: [DeptVO]}` | 已登录 | 无 |
| DEPT-API-002 | GET | `/` | 获取所有启用部门 | 无 | `{code, data: [Dept]}` | 已登录 | 无 |
| DEPT-API-003 | GET | `/{id}` | 根据ID获取部门 | id(UUID, Path) | `{code, data: Dept}` | 已登录 | 部门不存在 |
| DEPT-API-004 | POST | `/` | 创建部门 | Body: DeptDTO `{deptName, parentId, deptType, leaderId, sortOrder}` | `{code, data: Dept}` | 管理员 | 父部门不存在; 字段校验失败 |
| DEPT-API-005 | PUT | `/{id}` | 更新部门 | id(UUID, Path), Body: DeptDTO | `{code, data: Dept}` | 管理员 | 部门不存在; 形成环 |
| DEPT-API-006 | DELETE | `/{id}` | 删除部门 | id(UUID, Path) | `{code, data: null}` | 管理员 | 部门不存在; 有子部门; 部门下有用户 |
| DEPT-API-007 | GET | `/{deptId}/members` | 获取部门成员 | deptId(UUID, Path) | `{code, data: [UserVO]}` | 已登录 | 部门不存在 |
| DEPT-API-008 | GET | `/by-type/{deptType}` | 根据类型查询部门 | deptType(String, Path): ADMIN/ENGINEERING/SECURITY/CS/CLEANING | `{code, data: [Dept]}` | 已登录 | 无效类型 |
**DeptVO格式**:
```json
{
"id": "uuid",
"parentId": "uuid",
"deptName": "工程部",
"deptType": "ENGINEERING",
"leaderId": "uuid",
"sortOrder": 1,
"status": "ACTIVE",
"children": [DeptVO, DeptVO, ...]
}
```
---
### 3.6 ProjectMemberController -- 项目成员管理
**基础路径**: `/api/auth/projects`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| PMM-API-001 | GET | `/{projectId}/members` | 查询项目成员列表 | projectId(UUID, Path), page(int, 默认0), size(int, 默认20) | `{code, data: PageResponse<ProjectMemberVO>}` | 已登录 | 项目不存在 |
| PMM-API-002 | GET | `/{projectId}/available-members` | 获取可添加成员 | projectId(UUID, Path), search(String, 可选) | `{code, data: [UserVO]}` | 已登录 | 项目不存在 |
| PMM-API-003 | POST | `/{projectId}/members` | 添加项目成员 | projectId(UUID, Path), Body: AddProjectMemberDTO `{userId, staffType, roleIds}` | `{code, data: null}` | 项目管理员 | 用户不存在; 项目不存在; 用户已是成员 |
| PMM-API-004 | DELETE | `/{projectId}/members/{userId}` | 移除项目成员 | projectId(UUID, Path), userId(UUID, Path) | `{code, data: null}` | 项目管理员 | 成员不存在 |
**ProjectMemberVO格式**:
```json
{
"id": "uuid",
"userId": "uuid",
"username": "zhangsan",
"realName": "张三",
"staffType": "SECURITY",
"shiftType": "DAY",
"assignmentStatus": "ASSIGNED",
"roles": [
{"id": "uuid", "code": "SECURITY_GUARD", "name": "保安"}
]
}
```
---
### 3.7 DataAccessController -- 数据访问授权 @Deprecated
**基础路径**: `/api/data-access`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| DA-API-001 | POST | `/` | 授予数据访问权限 | Body: DataAccessRequest `{dataType, dataId, accessType, accessId, accessLevel}` | `{code, data: null}` | 已登录 | 未登录(401); 字段校验失败 |
| DA-API-002 | DELETE | `/{id}` | 撤销数据访问权限 | id(UUID, Path) | `{code, data: null}` | 已登录 | 记录不存在 |
| DA-API-003 | GET | `/` | 查询数据访问记录 | dataType(String), dataId(UUID) | `{code, data: [DataAccess]}` | 已登录 | 参数缺失 |
> **注意**: 此控制器所有端点已标记 @Deprecated后续版本将移除。
---
### 3.8 AuditLogController -- 审计日志
**基础路径**: `/api/audit-logs`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| AUDIT-API-001 | GET | `/` | 分页查询审计日志 | page(int, 默认0), size(int, 默认10), module(String, 可选), action(String, 可选), username(String, 可选), startDate(DateTime, 可选), endDate(DateTime, 可选) | `{code, data: Page<AuditLog>}` | 管理员 | 查询范围超30天自动截断 |
| AUDIT-API-002 | GET | `/modules` | 获取模块列表 | 无 | `{code, data: [{value, label}]}` | 管理员 | 无 |
| AUDIT-API-003 | GET | `/actions` | 获取操作类型列表 | 无 | `{code, data: [{value, label}]}` | 管理员 | 无 |
| AUDIT-API-004 | GET | `/stats` | 获取最近30天日志统计 | 无 | `{code, data: {total, retentionDays, description}}` | 管理员 | 无 |
**模块列表固定值**: USER(用户管理), ROLE(角色管理), PERMISSION(权限管理), PROJECT(项目管理), AUTH(登录认证)
---
### 3.9 SysConfigController -- 系统配置
**基础路径**: `/api/config`
| 编号 | 方法 | 路径 | 说明 | 请求参数 | 响应格式 | 权限要求 | 例外情况 |
|------|------|------|------|----------|----------|----------|----------|
| CFG-API-001 | GET | `/` | 获取所有配置项 | 无 | `{code, data: {key1: value1, key2: value2}}` | 管理员 | 无 |
| CFG-API-002 | GET | `/{configKey}` | 根据键获取配置 | configKey(String, Path) | `{code, data: SysConfig}` | 管理员 | 配置键不存在 |
| CFG-API-003 | PUT | `/{configKey}` | 更新单个配置 | configKey(String, Path), Body: `{configValue: String(NotBlank)}` | `{code, data: SysConfig}` | 管理员 | 配置键不存在; 配置值为空 |
| CFG-API-004 | PUT | `/` | 批量更新配置 | Body: `{key1: value1, key2: value2}` | `{code, data: {key1: value1, key2: value2}}` | 管理员 | 配置键不存在 |
---
## 四、业务规则
### 4.1 RBAC权限模型角色-权限-用户关联规则)
**模型结构**: User <--M2M--> Role <--M2M--> Permission
**双层角色分配**:
1. **用户直接角色**: 通过 `auth_user_role` 中间表关联
- 用户创建时分配
- 通过 `UserController.assignRoles` 接口分配
- 覆盖模式:新分配的角色列表完全替换旧列表
2. **项目员工角色**: 通过 `ProjectStaffRole` 关联
- 项目成员管理时分配
- 覆盖模式:先删除旧角色,再添加新角色
- 级联删除:删除 ProjectStaff 时自动删除其所有 ProjectStaffRole
**登录时角色收集规则**:
```
用户所有角色 = 用户直接角色(auth_user_role) 项目员工角色(ProjectStaffRole → Role)
```
**角色类型与数据范围对应关系**:
| 角色类型 | 说明 | 默认数据范围 | projectId |
|---------|------|-------------|-----------|
| SYSTEM | 系统级角色,跨项目 | ALL | NULL |
| PROJECT | 项目级角色,绑定特定项目 | PROJECT | 非NULL |
| DEPARTMENT | 部门级角色 | DEPARTMENT | NULL |
**角色分配约束**:
- 角色分配数量受 `BatchOperationValidator.validateAssignmentSize()` 限制
- 分配不存在的角色ID将抛出异常
- 角色状态为 DISABLED 时不应分配给用户(需业务层校验)
---
### 4.2 数据范围控制ALL/PROJECT/DEPARTMENT/SELF的SQL过滤规则
**四级数据范围**:
| 数据范围 | 编码 | 说明 | SQL过滤规则 |
|---------|------|------|------------|
| 全部 | ALL | 可查看所有数据 | 不做数据过滤 |
| 项目 | PROJECT | 仅可查看所属项目数据 | `WHERE project_id IN (用户可访问的项目ID列表)` |
| 部门 | DEPARTMENT | 仅可查看本部门数据 | `WHERE project_id IN (...) AND dept_id = 用户部门ID` |
| 本人 | SELF | 仅可查看本人数据 | `WHERE assigned_to = 当前用户ID OR created_by = 当前用户ID` |
**DataScopeService 核心方法**:
| 方法 | 说明 | 返回值 |
|------|------|--------|
| `canAccessAllData()` | 检查角色集中是否有ALL范围 | Boolean |
| `isSelfScopeOnly()` | 检查是否所有角色都是SELF范围 | Boolean |
| `getPermittedProjectIds()` | 获取用户可访问的项目ID列表 | List\<UUID\> |
**项目上下文传递机制**:
1. 前端请求携带 `X-Project-ID` Header
2. `ProjectContextInterceptor` 拦截器设置 ThreadLocal
3. 业务层通过 DataScopeService 过滤数据
---
### 4.3 JWT认证流程登录→Token生成→验证→刷新→登出
**Token生成**:
- 算法: HMAC-SHA256
- Claims: userId(subject), username, roles列表
- 过期时间: 可配置默认86400000ms = 24小时
- 密钥: 配置项 `jwt.secret`
**完整认证流程**:
```
1. 登录 POST /api/auth/login
├── 检查登录锁定(Redis: login:attempt:{username})
├── 查询用户(含角色, LAZY加载)
├── 验证密码(BCrypt.matches)
├── 检查用户状态(LOCKED/DISABLED → 拒绝)
├── 收集角色(直接角色 + 项目员工角色)
├── 生成JWT Token
├── 清除登录失败计数
├── 更新lastLoginTime/lastLoginIp
└── 返回 {token, userId, username, realName, roles}
2. 请求认证
├── Header: Authorization: Bearer {token}
├── JwtTokenProvider.validateToken() 验证签名和过期
└── 解析 userId / username / roles
3. Token刷新 POST /api/auth/refresh
├── 验证当前Token有效
├── 检查Token未过期
├── 生成新Token
└── 返回 {token}
4. 登出 POST /api/auth/logout
└── 客户端清除Token服务端无状态
```
---
### 4.4 密码策略BCrypt/强度校验/弱密码检测/登录锁定)
**加密算法**: BCryptSpring Security PasswordEncoder
**密码强度校验规则**(可配置,前缀 `password.*`:
| 规则 | 配置键 | 默认值 | 说明 |
|------|--------|--------|------|
| 最小长度 | password.minLength | 8 | 最小长度 |
| 最大长度 | password.maxLength | 20 | 最大长度 |
| 需要大写字母 | password.requireUppercase | true | 至少1个大写字母 |
| 需要小写字母 | password.requireLowercase | true | 至少1个小写字母 |
| 需要数字 | password.requireDigit | true | 至少1个数字 |
| 需要特殊字符 | password.requireSpecial | true | 至少1个特殊字符 |
**弱密码黑名单**: password, 123456, admin, qwerty, letmein, welcome, monkey, dragon
**旧密码兼容策略**:
- 检测非BCrypt格式密码MD5/SHA-1等
- 非BCrypt密码强制返回 `matches=false`
- 用户需通过修改密码流程重置为BCrypt格式
**登录失败锁定机制**基于Redis:
| 参数 | 默认值 | 说明 |
|------|--------|------|
| maxAttempts | 5 | 最大失败次数 |
| lockoutDurationMinutes | 10 | 锁定时长(分钟) |
| Key格式 | `login:attempt:{username}` | Redis Key |
**锁定流程**:
1. 每次失败: increment计数首次设置TTL
2. 达到上限(5次): isLockedOut返回true拒绝登录
3. 登录成功: 清除计数
4. 手动解锁: unlock方法清除Key
---
### 4.5 项目成员管理(添加/移除/角色分配/在岗状态变更)
**添加项目成员流程**:
1. 接收 AddProjectMemberDTO: `{userId, staffType, roleIds}`
2. 创建/更新 ProjectStaff 记录(设定员工类型、分配状态=ASSIGNED
3. 覆盖模式更新角色: 先删除旧 ProjectStaffRole再添加新 ProjectStaffRole
4. 创建 UserProject 关联记录
**移除项目成员流程**:
1. 删除 ProjectStaff 记录(级联删除 ProjectStaffRole
2. 删除 UserProject 关联记录
**在岗状态变更**:
- ASSIGNED: 已分配(正常在岗)
- ON_LEAVE: 请假(临时离岗)
- TRANSFERRED: 已调离(永久离岗)
**员工类型**:
- SECURITY(保安), CLEANING(保洁), GARDEN(绿化), MAINTENANCE(维修), CUSTOMER_SERVICE(客服), GENERAL(普通员工)
**班次类型**:
- DAY(白班), NIGHT(夜班), ROTATION(轮班)
---
### 4.6 住户认证流程(申请→审核→通过/拒绝→房屋绑定)
**认证状态流转**:
```
UNVERIFIED(未认证) → PENDING(待审核) → VERIFIED(已认证)
→ REJECTED(已拒绝) → PENDING(重新申请)
```
**认证流程**:
1. 住户注册账号verificationStatus = UNVERIFIED
2. 住户提交认证申请身份证、住户类型verificationStatus = PENDING
3. 管理员审核:
- 通过: verificationStatus = VERIFIED, 记录 verifiedAt/verifiedBy
- 拒绝: verificationStatus = REJECTED
**房屋绑定流程**:
1. 住户认证通过后,创建 ResidentSpace 关联
2. 关联字段: userId, spaceId, relationType(OWNER/FAMILY/TENANT)
3. 绑定状态: PENDING(待生效) → ACTIVE(生效中) → EXPIRED(已过期) / CANCELLED(已取消)
4. 支持生效/失效日期: startDate / endDateNULL=永久)
---
## 五、执行约束
### 5.1 用户名唯一性约束
- **数据库约束**: `auth_user.username` 列 UNIQUE
- **校验规则**: 正则 `^[a-zA-Z0-9_]+$`长度3-50位
- **违反后果**: 创建/更新用户时抛出 DataIntegrityViolationException
- **处理方式**: 业务层先查询是否存在相同用户名,存在则抛出 BusinessException
### 5.2 角色代码唯一性约束
- **数据库约束**: `auth_role.code` 列 UNIQUE
- **校验规则**: 正则 `^[a-zA-Z0-9_]+$`长度2-50位
- **违反后果**: 创建/更新角色时抛出 DataIntegrityViolationException
- **处理方式**: 业务层先查询是否存在相同角色代码,存在则抛出 BusinessException
### 5.3 权限代码唯一性约束
- **数据库约束**: `auth_permission.code` 列 UNIQUE
- **校验规则**: 正则 `^[a-zA-Z0-9_:]+$`长度2-100位格式 `module:resource:action`
- **违反后果**: 创建/更新权限时抛出 DataIntegrityViolationException
- **处理方式**: 业务层先查询是否存在相同权限代码,存在则抛出 BusinessException
### 5.4 部门树完整性约束(不能形成环)
- **约束说明**: 部门通过 parentId 自引用形成树形结构,更新 parentId 时不能形成环路
- **校验方式**: 业务层在更新 parentId 时,沿 parentId 链向上遍历,检查是否会回到当前节点
- **违反后果**: 抛出 BusinessException提示"不能将部门设置为其子部门的下级"
- **附加约束**: 删除部门时,必须先删除或移动其所有子部门
### 5.5 密码强度约束
- **最小长度**: 8位
- **最大长度**: 20位业务层校验/ 128位API层校验
- **必须包含**: 大写字母、小写字母、数字、特殊字符各至少1个
- **弱密码检测**: 不允许使用黑名单中的密码
- **违反后果**: 修改密码时抛出 BusinessException提示具体不满足的规则
---
## 六、权限控制
### 6.1 API端点权限矩阵
| 控制器 | 端点 | 所需角色 | 数据范围过滤 |
|--------|------|----------|-------------|
| AuthController | POST /login | 公开(无需认证) | 无 |
| AuthController | POST /logout | 已登录用户 | 无 |
| AuthController | GET /me | 已登录用户 | SELF |
| AuthController | POST /refresh | 已登录用户 | 无 |
| UserController | GET / | 管理员 | ALL/PROJECT/DEPARTMENT/SELF |
| UserController | GET /{id} | 管理员 | 同上 |
| UserController | POST / | 管理员(SYSTEM级) | 无 |
| UserController | PUT /{id} | 管理员 | 同上 |
| UserController | DELETE /{id} | 管理员(SYSTEM级) | 无 |
| UserController | PUT /{id}/password | 本人或管理员 | SELF |
| UserController | POST /{id}/roles | 管理员(SYSTEM级) | 无 |
| UserController | GET /{id}/projects | 已登录用户 | SELF |
| UserController | POST /{id}/projects | 管理员(PROJECT级) | 无 |
| UserController | DELETE /{id}/projects/{projectId} | 管理员(PROJECT级) | 无 |
| UserController | GET /enterprise | 已登录用户 | ALL/PROJECT |
| RoleController | GET / | 已登录用户 | ALL/PROJECT |
| RoleController | GET /{id} | 已登录用户 | 同上 |
| RoleController | GET /project/{projectId} | 已登录用户 | PROJECT |
| RoleController | POST / | 管理员(SYSTEM级) | 无 |
| RoleController | PUT /{id} | 管理员(SYSTEM级) | 无 |
| RoleController | DELETE /{id} | 管理员(SYSTEM级) | 无 |
| RoleController | POST /{id}/permissions | 管理员(SYSTEM级) | 无 |
| RoleController | GET /{id}/permissions | 已登录用户 | 同上 |
| RoleController | GET /{id}/users | 已登录用户 | 同上 |
| PermissionController | GET / | 已登录用户 | ALL |
| PermissionController | GET /{id} | 已登录用户 | ALL |
| PermissionController | GET /type/{type} | 已登录用户 | ALL |
| PermissionController | GET /menus | 已登录用户 | SELF(按角色过滤) |
| PermissionController | POST / | 管理员(SYSTEM级) | 无 |
| PermissionController | PUT /{id} | 管理员(SYSTEM级) | 无 |
| PermissionController | DELETE /{id} | 管理员(SYSTEM级) | 无 |
| DeptController | GET /tree | 已登录用户 | ALL |
| DeptController | GET / | 已登录用户 | ALL |
| DeptController | GET /{id} | 已登录用户 | ALL |
| DeptController | POST / | 管理员(SYSTEM级) | 无 |
| DeptController | PUT /{id} | 管理员(SYSTEM级) | 无 |
| DeptController | DELETE /{id} | 管理员(SYSTEM级) | 无 |
| DeptController | GET /{deptId}/members | 已登录用户 | DEPARTMENT |
| DeptController | GET /by-type/{deptType} | 已登录用户 | ALL |
| ProjectMemberController | GET /{projectId}/members | 项目成员 | PROJECT |
| ProjectMemberController | GET /{projectId}/available-members | 项目管理员 | PROJECT |
| ProjectMemberController | POST /{projectId}/members | 项目管理员 | PROJECT |
| ProjectMemberController | DELETE /{projectId}/members/{userId} | 项目管理员 | PROJECT |
| DataAccessController | POST / | 已登录用户(@Deprecated) | 无 |
| DataAccessController | DELETE /{id} | 已登录用户(@Deprecated) | 无 |
| DataAccessController | GET / | 已登录用户(@Deprecated) | 无 |
| AuditLogController | GET / | 管理员(SYSTEM级) | ALL |
| AuditLogController | GET /modules | 管理员(SYSTEM级) | 无 |
| AuditLogController | GET /actions | 管理员(SYSTEM级) | 无 |
| AuditLogController | GET /stats | 管理员(SYSTEM级) | ALL |
| SysConfigController | GET / | 管理员(SYSTEM级) | ALL |
| SysConfigController | GET /{configKey} | 管理员(SYSTEM级) | ALL |
| SysConfigController | PUT /{configKey} | 管理员(SYSTEM级) | 无 |
| SysConfigController | PUT / | 管理员(SYSTEM级) | 无 |
### 6.2 数据范围过滤规则
| 操作类型 | ALL范围 | PROJECT范围 | DEPARTMENT范围 | SELF范围 |
|---------|---------|------------|---------------|---------|
| 查询用户列表 | 全部用户 | 本项目成员 | 本部门用户 | 仅本人 |
| 查询角色列表 | 全部角色 | 本项目角色 | - | - |
| 查询审计日志 | 全部日志 | 本项目日志 | 本部门日志 | 本人操作日志 |
| 查询部门成员 | 全部 | 本项目部门成员 | 本部门成员 | - |
---
## 七、例外情况处理
### 7.1 用户相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 用户名已存在 | 409 | "用户名{username}已存在" | 创建/更新前查询,存在则拒绝 |
| 用户不存在 | 404 | "用户不存在" | findById返回空时抛出 |
| 密码错误 | 401 | "用户名或密码错误" | BCrypt.matches返回false |
| 账户锁定 | 423 | "账户已锁定,请{minutes}分钟后重试" | 检查Redis锁定状态 |
| 账户禁用 | 403 | "账户已被禁用" | 检查UserStatus.DISABLED |
| 原密码错误 | 400 | "原密码不正确" | 修改密码时验证 |
| 新密码强度不足 | 400 | "密码必须包含大写字母、小写字母、数字和特殊字符长度8-20位" | 密码策略校验 |
| 新密码为弱密码 | 400 | "该密码过于简单,请使用更复杂的密码" | 弱密码黑名单检测 |
| 手机号格式错误 | 400 | "手机号格式不正确" | 正则校验 |
| 邮箱格式错误 | 400 | "邮箱格式不正确" | @Email校验 |
| 删除有关联数据的用户 | 400 | "该用户存在关联数据,无法删除" | 检查UserProject/ProjectStaff等关联 |
### 7.2 角色相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 角色代码已存在 | 409 | "角色代码{code}已存在" | 创建/更新前查询 |
| 角色不存在 | 404 | "角色不存在" | findById返回空时抛出 |
| 删除已分配的角色 | 400 | "该角色已分配给用户,无法删除" | 检查auth_user_role关联 |
| 角色代码格式错误 | 400 | "角色代码只能包含字母、数字和下划线" | 正则校验 |
### 7.3 权限相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 权限代码已存在 | 409 | "权限代码{code}已存在" | 创建/更新前查询 |
| 权限不存在 | 404 | "权限不存在" | findById返回空时抛出 |
| 删除已分配的权限 | 400 | "该权限已分配给角色,无法删除" | 检查auth_role_permission关联 |
| 权限代码格式错误 | 400 | "权限代码只能包含字母、数字、冒号和下划线" | 正则校验 |
### 7.4 部门相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 部门不存在 | 404 | "部门不存在" | getById返回空时返回error |
| 有子部门不能删除 | 400 | "该部门存在子部门,无法删除" | 检查子部门数量 |
| 部门下有用户不能删除 | 400 | "该部门下存在用户,无法删除" | 检查部门成员 |
| 部门树形成环 | 400 | "不能将部门设置为其子部门的下级" | 遍历parentId链检查 |
| 父部门不存在 | 400 | "上级部门不存在" | 创建/更新时校验parentId |
### 7.5 认证相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| Token缺失 | 401 | "未授权" | Authorization Header为空 |
| Token无效 | 401 | "Token无效" | JWT签名验证失败 |
| Token已过期 | 401 | "Token已过期" | JWT过期时间检查 |
| 登录失败超限 | 423 | "登录失败次数过多,账户已锁定{minutes}分钟" | Redis计数检查 |
| 用户名不存在 | 401 | "用户名或密码错误" | 不暴露具体原因 |
### 7.6 项目成员相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 用户已是项目成员 | 409 | "该用户已是项目成员" | 检查ProjectStaff存在性 |
| 项目成员不存在 | 404 | "项目成员不存在" | 删除时校验 |
| 角色ID不存在 | 400 | "角色ID{id}不存在" | 分配角色时校验 |
### 7.7 系统配置相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 配置键不存在 | 404 | "配置项{key}不存在" | 查询时校验 |
| 配置值为空 | 400 | "配置值不能为空" | @NotBlank校验 |
### 7.8 审计日志相关例外
| 例外场景 | 错误码 | 错误信息 | 处理方式 |
|---------|--------|---------|---------|
| 查询范围超30天 | 200(自动截断) | 无(自动截断起始时间) | 强制限制查询窗口 |
| 分页参数过大 | 200(自动修正) | 无(使用getSafeSize修正) | PaginationValidator校验 |