# 身份与权限域 - 详细设计

**文档版本**: 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 | 所属项目ID（NULL=系统级） |
| 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 | | 上级部门ID（NULL=顶级） |
| 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 → User（FK: fk_project_staff_user）
- `staffRoles` → @OneToMany → ProjectStaffRole（mappedBy="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/强度校验/弱密码检测/登录锁定）

**加密算法**: BCrypt（Spring 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 / endDate（NULL=永久）

---

## 五、执行约束

### 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校验 |
