gz-oarms/task_plan.md

390 lines
12 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.

# OARMS 权限体系设计与实现计划
## 目标
基于 PRD 第三章"角色权限"定义,设计并实现完整的 RBAC + 数据权限体系,覆盖后端认证/鉴权 + 前端路由/按钮控制。
## PRD 角色定义
| 角色 | 角色代码 | 操作范围 | 数据范围 |
|------|---------|---------|---------|
| 市局广告监管人员 | `CITY_SUPERVISOR` | 全模块读写 | 全市数据 |
| 区局广告监管人员 | `DISTRICT_SUPERVISOR` | 监控/取证/规则只读 | 本辖区数据 |
| 广告大屏运营商 | `OPERATOR` | 大屏维护/录屏查看 | 本公司大屏数据 |
## 当前状态
| 组件 | 现状 | 缺失 |
|------|------|------|
| Sa-Token 框架 | 已集成,拦截器已配置 | 仅 checkLogin无角色/权限校验 |
| LoginController | 硬编码 userIdadmin 权限 | 无数据库校验 |
| StpInterfaceImpl | 返回空列表 | 未对接数据库 |
| UserBaseServiceImpl | login() 返回 null | 未实现 |
| 数据库 | 无用户/角色表 | 需建表 |
| 前端路由 | 仅 admin/guest | 需增加 3 个角色 |
| 前端权限守卫 | admin 全通 | 需角色过滤 |
---
## 实现阶段7 个 Phase
### Phase 1数据库设计 — 用户/角色/权限表DDL
在 OARMS schema 下创建系统管理相关表,表前缀 `sys_`
**表设计**
#### 1.1 `SYS_USER` 用户表
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR(32) PK | UUID |
| USERNAME | VARCHAR(50) NOT NULL UNIQUE | 登录用户名 |
| PASSWORD | VARCHAR(100) NOT NULL | BCrypt 加密密码 |
| REAL_NAME | VARCHAR(50) | 真实姓名 |
| PHONE | VARCHAR(20) | 联系电话 |
| ORG_NAME | VARCHAR(100) | 所属单位 |
| DISTRICT_CODE | VARCHAR(20) | 所属区域编码(区局/运营商用) |
| STATUS | TINYINT DEFAULT 1 | 1=正常 0=停用 |
| + SuperEntity 审计字段 | | createBy/createTime/updateBy/updateTime/updateName |
#### 1.2 `SYS_ROLE` 角色表
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR(32) PK | UUID |
| ROLE_CODE | VARCHAR(30) NOT NULL UNIQUE | 角色编码CITY_SUPERVISOR 等) |
| ROLE_NAME | VARCHAR(50) NOT NULL | 角色显示名 |
| DESCRIPTION | VARCHAR(200) | 描述 |
| STATUS | TINYINT DEFAULT 1 | 1=正常 0=停用 |
| + SuperEntity 审计字段 | | |
#### 1.3 `SYS_USER_ROLE` 用户角色关联表
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR(32) PK | UUID |
| USER_ID | VARCHAR(32) NOT NULL | 关联 SYS_USER.ID |
| ROLE_ID | VARCHAR(32) NOT NULL | 关联 SYS_ROLE.ID |
| + createBy/createTime | | |
#### 1.4 `SYS_PERMISSION` 权限表(操作权限码)
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR(32) PK | UUID |
| PERM_CODE | VARCHAR(50) NOT NULL UNIQUE | 权限编码(如 `screen:view`、`evidence:create` |
| PERM_NAME | VARCHAR(100) NOT NULL | 权限名称 |
| MODULE | VARCHAR(30) | 所属模块BS/LB/AM/MR/CW |
| + SuperEntity 审计字段 | | |
#### 1.5 `SYS_ROLE_PERMISSION` 角色权限关联表
| 列名 | 类型 | 说明 |
|------|------|------|
| ID | VARCHAR(32) PK | UUID |
| ROLE_ID | VARCHAR(32) NOT NULL | 关联 SYS_ROLE.ID |
| PERM_ID | VARCHAR(32) NOT NULL | 关联 SYS_PERMISSION.ID |
| + createBy/createTime | | |
#### 1.6 初始数据
3 条角色 + 3 条管理员用户(各角色 1 个)+ 权限码 + 角色权限映射。
**权限码清单**(按模块):
| 权限码 | 说明 | 市局 | 区局 | 运营商 |
|--------|------|------|------|--------|
| `screen:view` | 查看大屏 | Y | Y(本辖区) | Y(本公司) |
| `screen:create` | 新增大屏 | Y | N | Y(本公司) |
| `screen:edit` | 编辑大屏 | Y | N | Y(本公司) |
| `screen:remove` | 删除大屏 | Y | N | N |
| `screen:export` | 导出大屏 | Y | Y(本辖区) | N |
| `law:view` | 查看法律条款 | Y | Y | Y |
| `law:create` | 新增条款 | Y | N | N |
| `law:edit` | 编辑条款 | Y | N | N |
| `law:remove` | 删除条款 | Y | N | N |
| `monitoring-rule:view` | 查看规则 | Y | Y | N |
| `monitoring-rule:create` | 新增规则 | Y | N | N |
| `monitoring-rule:edit` | 编辑规则 | Y | N | N |
| `monitoring-rule:remove` | 删除规则 | Y | N | N |
| `recording-config:view` | 查看录屏配置 | Y | Y(本辖区) | Y(本公司) |
| `recording-config:create` | 新增录屏配置 | Y | N | N |
| `recording-config:edit` | 编辑录屏配置 | Y | N | N |
| `recording-task:view` | 查看录屏任务 | Y | Y(本辖区) | Y(本公司) |
| `monitor-record:view` | 查看监控记录 | Y | Y(本辖区) | N |
| `monitor-record:handle` | 处理监控 | Y | Y(本辖区) | N |
| `evidence:view` | 查看取证 | Y | Y(本辖区) | N |
| `evidence:create` | 固化取证 | Y | Y(本辖区) | N |
| `evidence-relation:view` | 查看规则关联 | Y | Y(本辖区) | N |
| `evidence-relation:bind` | 关联规则 | Y | Y(本辖区) | N |
| `monitoring-clue:view` | 查看线索 | Y | Y(本辖区) | N |
| `monitoring-clue:create` | 生成线索 | Y | Y(本辖区) | N |
| `clue-transfer:view` | 查看转办 | Y | Y(本辖区) | N |
| `clue-transfer:create` | 发起转办 | Y | Y(本辖区) | N |
**数据权限**(不在权限码中,在角色级别控制):
| 角色 | 数据范围 | 实现方式 |
|------|---------|---------|
| CITY_SUPERVISOR | 全市 | 无 SQL 过滤 |
| DISTRICT_SUPERVISOR | 本辖区 | 按 district_code 过滤 |
| OPERATOR | 本公司 | 按 org_name 过滤 |
**阶段输出**: DDL SQL 文件 + 初始数据 SQL
**状态**: ✅ 完成
---
### Phase 2后端 Entity/Mapper 层
按项目约定创建:
```
modules/system/
├── entity/
│ ├── SysUserEntity.java
│ ├── SysRoleEntity.java
│ ├── SysUserRoleEntity.java
│ ├── SysPermissionEntity.java
│ ├── SysRolePermissionEntity.java
│ ├── query/
│ │ └── SysUserQuery.java
│ └── vo/
│ ├── SysUserVO.java
│ └── LoginUserVO.java # 登录后返回给前端的信息
├── mapper/
│ ├── SysUserMapper.java
│ ├── SysRoleMapper.java
│ ├── SysUserRoleMapper.java
│ ├── SysPermissionMapper.java
│ └── SysRolePermissionMapper.java
└── service/
├── ISysUserService.java
├── ISysRoleService.java
└── impl/
├── SysUserServiceImpl.java
└── SysRoleServiceImpl.java
```
**关键方法**
- `SysUserMapper.findByUsername(username)` — 登录查询
- `SysRoleMapper.selectByUserId(userId)` — 查用户角色
- `SysPermissionMapper.selectByUserId(userId)` — 查用户权限码
- `SysPermissionMapper.selectByRoleCode(roleCode)` — 查角色权限
**LoginUserVO** 返回给前端的信息:
```json
{
"userId": "...",
"username": "...",
"realName": "...",
"roles": ["CITY_SUPERVISOR"],
"roleNames": ["市局广告监管人员"],
"permissions": ["screen:view", "screen:create", ...],
"districtCode": null,
"orgName": "广州市市场监督管理局"
}
```
**状态**: ✅ 完成
---
### Phase 3认证流程实现LoginController + StpInterfaceImpl
#### 3.1 LoginController 改造
```
POST /user/auth/login
→ SysUserService.findByUsername(username)
→ BCrypt.checkPassword(password, user.password)
→ StpUtil.login(user.id)
→ session.set(USER_KEY, LoginUserVO)
→ response.setHeader("satoken", token)
→ return RestResult.ok(LoginUserVO)
```
#### 3.2 StpInterfaceImpl 实现
```
getPermissionList(loginId) → SysPermissionMapper.selectByUserId(loginId)
getRoleList(loginId) → SysRoleMapper.selectRoleCodesByUserId(loginId)
```
#### 3.3 /user/auth/login/info 改造
返回 LoginUserVO含 roles + permissions + districtCode + orgName
#### 3.4 UserBaseServiceImpl 完善
- login() 对接 SysUserService
- getCurrentUser() 从 session 取 LoginUserVO
**状态**: ✅ 完成
---
### Phase 4鉴权拦截器 + 数据权限
#### 4.1 SpringMvcConfig 权限路由
按模块配置权限检查:
```java
SaRouter.match("/api/screen/**", r -> StpUtil.checkLogin());
SaRouter.match("/api/monitoring-rule/**", r -> {
StpUtil.checkLogin();
// 具体 CRUD 方法用注解控制
});
// ... 其余模块类似
```
#### 4.2 Controller 方法级权限注解
在需要权限控制的 Controller 方法上加:
```java
@SaCheckPermission("screen:create")
@PostMapping("save")
public RestResult<?> save(...) { ... }
@SaCheckPermission("screen:remove")
@PostMapping("remove")
public RestResult<?> remove(...) { ... }
```
#### 4.3 数据权限过滤
在 Service 层的查询方法中,根据当前用户的角色过滤数据:
- CITY_SUPERVISOR: 不过滤
- DISTRICT_SUPERVISOR: 在 Query 中追加 `district_code = currentUser.districtCode`
- OPERATOR: 在 Query 中追加 `org_name = currentUser.orgName`
实现方式:在 Service 中获取当前用户,动态拼装查询条件。
**注意**:数据权限需要修改现有 10 个模块的 Service 查询方法,影响范围较大。**建议先实现 Phase 1-3 的认证和操作权限Phase 4 数据权限单独迭代。**
**实际实施**
- 创建 `DataScopeHelper` 工具类(`modules/system/util/`),提供角色判断静态方法
- 修改 7 个 Service 的 queryList 方法,添加数据权限过滤:
- ScreenServiceImpl: queryList + exportData + getScreenOptions
- RecordingConfigServiceImpl: queryList + getConfigurableScreens
- RecordingTaskServiceImpl: queryList
- MonitorRecordServiceImpl: queryList
- EvidenceRecordServiceImpl: queryList
- MonitoringClueServiceImpl: queryList
- ClueTransferServiceImpl: queryList + queryPendingClues
- 过滤规则:
- CITY_SUPERVISOR: 不过滤(全市数据)
- DISTRICT_SUPERVISOR: 按 `district = currentUser.districtCode` 过滤
- OPERATOR: Screen 按 `operatorUnit = orgName`,其他模块通过 screenId 关联 Screen 过滤
- ClueTransferServiceImpl 特殊处理OR 条件(转办目标辖区 + 线索来源辖区)
- 跳过模块LawClauseServiceImpl无区域字段、MonitoringRuleServiceImpl复杂 scope JSON、EvidenceRuleRelationServiceImpl关联表
**状态**: ✅ 完成
---
### Phase 5前端角色路由映射
#### 5.1 定义角色常量
```javascript
// src/utils/constants.js 或 src/router/roles.js
export const ROLES = {
CITY_SUPERVISOR: 'CITY_SUPERVISOR',
DISTRICT_SUPERVISOR: 'DISTRICT_SUPERVISOR',
OPERATOR: 'OPERATOR'
}
```
#### 5.2 更新 routes.js 的 meta.roles
按 PRD 权限矩阵为每个业务路由分配角色:
```javascript
// BS-大屏基础库 - 三个角色都能访问
meta: { roles: [ROLES.CITY_SUPERVISOR, ROLES.DISTRICT_SUPERVISOR, ROLES.OPERATOR] }
// MR-监测规则 - 市局+区局
meta: { roles: [ROLES.CITY_SUPERVISOR, ROLES.DISTRICT_SUPERVISOR] }
// CW-内容预警 - 市局+区局
meta: { roles: [ROLES.CITY_SUPERVISOR, ROLES.DISTRICT_SUPERVISOR] }
```
#### 5.3 更新 permission.js
- 去掉 admin 硬编码全通逻辑
- 使用后端返回的 permissions 数组做按钮级控制
#### 5.4 按钮级权限指令
添加 `v-permission` 指令,控制按钮显示:
```html
<el-button v-permission="'screen:create'">新增</el-button>
<el-button v-permission="'screen:remove'">删除</el-button>
```
**状态**: ✅ 完成
---
### Phase 6用户管理接口可选
提供用户 CRUD 接口供后台管理使用:
- `POST /api/sys-user/query` — 用户列表(分页)
- `POST /api/sys-user/save` — 新增用户
- `POST /api/sys-user/update` — 编辑用户
- `POST /api/sys-user/remove` — 删除用户
- `POST /api/sys-user/reset-password` — 重置密码
- `GET /api/sys-user/role-options` — 角色选项列表
前端对应的管理页面可后续迭代。
**状态**: pending
---
### Phase 7编译验证 + 端到端测试
1. DDL 执行到 DM8 数据库
2. 后端编译通过(`mvn compile -Dcheckstyle.skip=true`
3. 启动后端,验证登录流程
4. 验证权限校验(无权限返回 403
5. 前端 `npm run lint` 通过
6. 前端登录后路由正确过滤
**状态**: ✅ 完成BUILD SUCCESS
---
## 实施建议
### 优先级排序
| 优先级 | Phase | 说明 |
|--------|-------|------|
| **P0 必须** | Phase 1-3 | DDL + Entity/Mapper + 认证流程,使登录对接真实数据库 |
| **P1 重要** | Phase 5 | 前端角色路由,使不同角色看到不同菜单 |
| **P2 增强** | Phase 4 | 操作权限注解 + 数据权限过滤 |
| **P3 可选** | Phase 6 | 用户管理 CRUD |
| **P0 验证** | Phase 7 | 编译 + 测试 |
### 建议实施路径
**第一轮(核心认证)**: Phase 1 → 2 → 3 → 7
**第二轮(前端权限)**: Phase 5 → 7
**第三轮(精细控制)**: Phase 4 → 6
---
## 修复决策
(实施过程中记录)