# OARMS 权限体系设计与实现计划 ## 目标 基于 PRD 第三章"角色权限"定义,设计并实现完整的 RBAC + 数据权限体系,覆盖后端认证/鉴权 + 前端路由/按钮控制。 ## PRD 角色定义 | 角色 | 角色代码 | 操作范围 | 数据范围 | |------|---------|---------|---------| | 市局广告监管人员 | `CITY_SUPERVISOR` | 全模块读写 | 全市数据 | | 区局广告监管人员 | `DISTRICT_SUPERVISOR` | 监控/取证/规则只读 | 本辖区数据 | | 广告大屏运营商 | `OPERATOR` | 大屏维护/录屏查看 | 本公司大屏数据 | ## 当前状态 | 组件 | 现状 | 缺失 | |------|------|------| | Sa-Token 框架 | 已集成,拦截器已配置 | 仅 checkLogin,无角色/权限校验 | | LoginController | 硬编码 userId,admin 权限 | 无数据库校验 | | 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 新增 删除 ``` **状态**: ✅ 完成 --- ### 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 --- ## 修复决策 (实施过程中记录)