feat: RBAC权限体系实现 — 认证+鉴权+操作权限

- 新增 system 模块:5张表(SYS_USER/ROLE/PERMISSION等) DDL + 初始数据
  (3角色: 市局/区局/运营商, 27权限码, 3初始用户)
- 改造 LoginController: 数据库BCrypt认证,返回LoginUserVO
- 实现 StpInterfaceImpl: 对接数据库角色/权限查询
- 9个Controller写操作方法添加 @SaCheckPermission 注解
- 新增 PasswordConfig(BCrypt), spring-security-crypto依赖
- 前端: routes.js添加角色meta, permission.js去掉admin硬编码,
  user.js适配LoginUserVO, 新增v-permission按钮级权限指令

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
chenjy 2026-05-24 17:10:48 +08:00
parent 2a30d0d609
commit f4f8470dfe
35 changed files with 1063 additions and 81 deletions

View File

@ -0,0 +1,164 @@
-- ============================================================================
-- OARMS - 系统管理模块 DDL
-- Database: DM8 (达梦)
-- Schema: OARMS
-- Version: V11.0.0
-- Date: 2026-05-24
-- Description: 用户/角色/权限管理RBAC
-- ============================================================================
-- ----------------------------------------------------------------------------
-- 1. sys_user - 系统用户表
-- ----------------------------------------------------------------------------
CREATE TABLE OARMS.SYS_USER (
id VARCHAR(50) NOT NULL,
username VARCHAR(50) NOT NULL,
password VARCHAR(100) NOT NULL,
real_name VARCHAR(50),
phone VARCHAR(20),
org_name VARCHAR(100),
district_code VARCHAR(20),
status TINYINT NOT NULL DEFAULT 1,
create_by VARCHAR(50),
create_time TIMESTAMP,
create_name VARCHAR(50),
update_by VARCHAR(50),
update_time TIMESTAMP,
update_name VARCHAR(50),
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX IDX_SYS_USER_USERNAME ON OARMS.SYS_USER (USERNAME);
CREATE INDEX IDX_SYS_USER_STATUS ON OARMS.SYS_USER (STATUS);
CREATE INDEX IDX_SYS_USER_DISTRICT ON OARMS.SYS_USER (DISTRICT_CODE);
COMMENT ON TABLE OARMS.SYS_USER IS '系统用户';
COMMENT ON COLUMN OARMS.SYS_USER.ID IS '主键ID';
COMMENT ON COLUMN OARMS.SYS_USER.USERNAME IS '登录用户名';
COMMENT ON COLUMN OARMS.SYS_USER.PASSWORD IS '密码(BCrypt)';
COMMENT ON COLUMN OARMS.SYS_USER.REAL_NAME IS '真实姓名';
COMMENT ON COLUMN OARMS.SYS_USER.PHONE IS '联系电话';
COMMENT ON COLUMN OARMS.SYS_USER.ORG_NAME IS '所属单位';
COMMENT ON COLUMN OARMS.SYS_USER.DISTRICT_CODE IS '所属区域编码';
COMMENT ON COLUMN OARMS.SYS_USER.STATUS IS '状态(1=正常,0=停用)';
COMMENT ON COLUMN OARMS.SYS_USER.CREATE_BY IS '创建人ID';
COMMENT ON COLUMN OARMS.SYS_USER.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN OARMS.SYS_USER.CREATE_NAME IS '创建人姓名';
COMMENT ON COLUMN OARMS.SYS_USER.UPDATE_BY IS '更新人ID';
COMMENT ON COLUMN OARMS.SYS_USER.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN OARMS.SYS_USER.UPDATE_NAME IS '更新人姓名';
-- ----------------------------------------------------------------------------
-- 2. sys_role - 系统角色表
-- ----------------------------------------------------------------------------
CREATE TABLE OARMS.SYS_ROLE (
id VARCHAR(50) NOT NULL,
role_code VARCHAR(30) NOT NULL,
role_name VARCHAR(50) NOT NULL,
description VARCHAR(200),
status TINYINT NOT NULL DEFAULT 1,
create_by VARCHAR(50),
create_time TIMESTAMP,
create_name VARCHAR(50),
update_by VARCHAR(50),
update_time TIMESTAMP,
update_name VARCHAR(50),
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX IDX_SYS_ROLE_CODE ON OARMS.SYS_ROLE (ROLE_CODE);
COMMENT ON TABLE OARMS.SYS_ROLE IS '系统角色';
COMMENT ON COLUMN OARMS.SYS_ROLE.ID IS '主键ID';
COMMENT ON COLUMN OARMS.SYS_ROLE.ROLE_CODE IS '角色编码';
COMMENT ON COLUMN OARMS.SYS_ROLE.ROLE_NAME IS '角色名称';
COMMENT ON COLUMN OARMS.SYS_ROLE.DESCRIPTION IS '角色描述';
COMMENT ON COLUMN OARMS.SYS_ROLE.STATUS IS '状态(1=正常,0=停用)';
COMMENT ON COLUMN OARMS.SYS_ROLE.CREATE_BY IS '创建人ID';
COMMENT ON COLUMN OARMS.SYS_ROLE.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN OARMS.SYS_ROLE.CREATE_NAME IS '创建人姓名';
COMMENT ON COLUMN OARMS.SYS_ROLE.UPDATE_BY IS '更新人ID';
COMMENT ON COLUMN OARMS.SYS_ROLE.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN OARMS.SYS_ROLE.UPDATE_NAME IS '更新人姓名';
-- ----------------------------------------------------------------------------
-- 3. sys_user_role - 用户角色关联表
-- ----------------------------------------------------------------------------
CREATE TABLE OARMS.SYS_USER_ROLE (
id VARCHAR(50) NOT NULL,
user_id VARCHAR(50) NOT NULL,
role_id VARCHAR(50) NOT NULL,
create_by VARCHAR(50),
create_time TIMESTAMP,
create_name VARCHAR(50),
PRIMARY KEY (id)
);
CREATE INDEX IDX_SYS_USER_ROLE_UID ON OARMS.SYS_USER_ROLE (USER_ID);
CREATE INDEX IDX_SYS_USER_ROLE_RID ON OARMS.SYS_USER_ROLE (ROLE_ID);
CREATE UNIQUE INDEX IDX_SYS_USER_ROLE_UK ON OARMS.SYS_USER_ROLE (USER_ID, ROLE_ID);
COMMENT ON TABLE OARMS.SYS_USER_ROLE IS '用户角色关联';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.ID IS '主键ID';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.USER_ID IS '用户ID';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.ROLE_ID IS '角色ID';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.CREATE_BY IS '创建人ID';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN OARMS.SYS_USER_ROLE.CREATE_NAME IS '创建人姓名';
-- ----------------------------------------------------------------------------
-- 4. sys_permission - 系统权限表
-- ----------------------------------------------------------------------------
CREATE TABLE OARMS.SYS_PERMISSION (
id VARCHAR(50) NOT NULL,
perm_code VARCHAR(50) NOT NULL,
perm_name VARCHAR(100) NOT NULL,
module VARCHAR(30),
create_by VARCHAR(50),
create_time TIMESTAMP,
create_name VARCHAR(50),
update_by VARCHAR(50),
update_time TIMESTAMP,
update_name VARCHAR(50),
PRIMARY KEY (id)
);
CREATE UNIQUE INDEX IDX_SYS_PERM_CODE ON OARMS.SYS_PERMISSION (PERM_CODE);
CREATE INDEX IDX_SYS_PERM_MODULE ON OARMS.SYS_PERMISSION (MODULE);
COMMENT ON TABLE OARMS.SYS_PERMISSION IS '系统权限';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.ID IS '主键ID';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.PERM_CODE IS '权限编码';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.PERM_NAME IS '权限名称';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.MODULE IS '所属模块';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.CREATE_BY IS '创建人ID';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.CREATE_NAME IS '创建人姓名';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.UPDATE_BY IS '更新人ID';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.UPDATE_TIME IS '更新时间';
COMMENT ON COLUMN OARMS.SYS_PERMISSION.UPDATE_NAME IS '更新人姓名';
-- ----------------------------------------------------------------------------
-- 5. sys_role_permission - 角色权限关联表
-- ----------------------------------------------------------------------------
CREATE TABLE OARMS.SYS_ROLE_PERMISSION (
id VARCHAR(50) NOT NULL,
role_id VARCHAR(50) NOT NULL,
perm_id VARCHAR(50) NOT NULL,
create_by VARCHAR(50),
create_time TIMESTAMP,
create_name VARCHAR(50),
PRIMARY KEY (id)
);
CREATE INDEX IDX_SYS_ROLE_PERM_RID ON OARMS.SYS_ROLE_PERMISSION (ROLE_ID);
CREATE INDEX IDX_SYS_ROLE_PERM_PID ON OARMS.SYS_ROLE_PERMISSION (PERM_ID);
CREATE UNIQUE INDEX IDX_SYS_ROLE_PERM_UK ON OARMS.SYS_ROLE_PERMISSION (ROLE_ID, PERM_ID);
COMMENT ON TABLE OARMS.SYS_ROLE_PERMISSION IS '角色权限关联';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.ID IS '主键ID';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.ROLE_ID IS '角色ID';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.PERM_ID IS '权限ID';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.CREATE_BY IS '创建人ID';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.CREATE_TIME IS '创建时间';
COMMENT ON COLUMN OARMS.SYS_ROLE_PERMISSION.CREATE_NAME IS '创建人姓名';

View File

@ -0,0 +1,139 @@
-- ============================================================================
-- OARMS - 系统管理模块 初始数据
-- Database: DM8 (达梦)
-- Schema: OARMS
-- Version: V11.0.0
-- Date: 2026-05-24
-- Description: 角色、权限、用户初始数据
-- ============================================================================
-- ----------------------------------------------------------------------------
-- 1. 角色3 个)
-- ----------------------------------------------------------------------------
INSERT INTO OARMS.SYS_ROLE (id, role_code, role_name, description, status, create_by, create_time, create_name) VALUES
('role_city_supervisor', 'CITY_SUPERVISOR', '市局广告监管人员', '全市户外广告监管统筹、监测规则管理、违法广告线索处置', 1, 'system', CURRENT_TIMESTAMP, '系统'),
('role_district_supervisor', 'DISTRICT_SUPERVISOR', '区局广告监管人员', '本辖区户外广告日常监控、违法内容发现与取证', 1, 'system', CURRENT_TIMESTAMP, '系统'),
('role_operator', 'OPERATOR', '广告大屏运营商', '大屏信息维护、广告播放时间上报、录屏参数查看', 1, 'system', CURRENT_TIMESTAMP, '系统');
-- ----------------------------------------------------------------------------
-- 2. 权限码27 个)
-- ----------------------------------------------------------------------------
-- BS-大屏基础库
INSERT INTO OARMS.SYS_PERMISSION (id, perm_code, perm_name, module, create_by, create_time, create_name) VALUES
('perm_screen_view', 'screen:view', '查看大屏', 'BS', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_screen_create', 'screen:create', '新增大屏', 'BS', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_screen_edit', 'screen:edit', '编辑大屏', 'BS', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_screen_remove', 'screen:remove', '删除大屏', 'BS', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_screen_export', 'screen:export', '导出大屏', 'BS', 'system', CURRENT_TIMESTAMP, '系统');
-- LB-法律法规
INSERT INTO OARMS.SYS_PERMISSION (id, perm_code, perm_name, module, create_by, create_time, create_name) VALUES
('perm_law_view', 'law:view', '查看法律条款', 'LB', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_law_create', 'law:create', '新增条款', 'LB', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_law_edit', 'law:edit', '编辑条款', 'LB', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_law_remove', 'law:remove', '删除条款', 'LB', 'system', CURRENT_TIMESTAMP, '系统');
-- MR-监测规则
INSERT INTO OARMS.SYS_PERMISSION (id, perm_code, perm_name, module, create_by, create_time, create_name) VALUES
('perm_rule_view', 'monitoring-rule:view', '查看规则', 'MR', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_rule_create', 'monitoring-rule:create', '新增规则', 'MR', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_rule_edit', 'monitoring-rule:edit', '编辑规则', 'MR', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_rule_remove', 'monitoring-rule:remove', '删除规则', 'MR', 'system', CURRENT_TIMESTAMP, '系统');
-- AM-广告监控
INSERT INTO OARMS.SYS_PERMISSION (id, perm_code, perm_name, module, create_by, create_time, create_name) VALUES
('perm_config_view', 'recording-config:view', '查看录屏配置', 'AM', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_config_create', 'recording-config:create', '新增录屏配置', 'AM', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_config_edit', 'recording-config:edit', '编辑录屏配置', 'AM', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_task_view', 'recording-task:view', '查看录屏任务', 'AM', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_record_view', 'monitor-record:view', '查看监控记录', 'AM', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_record_handle', 'monitor-record:handle', '处理监控', 'AM', 'system', CURRENT_TIMESTAMP, '系统');
-- CW-内容预警
INSERT INTO OARMS.SYS_PERMISSION (id, perm_code, perm_name, module, create_by, create_time, create_name) VALUES
('perm_evidence_view', 'evidence:view', '查看取证', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_evidence_create', 'evidence:create', '固化取证', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_relation_view', 'evidence-relation:view', '查看规则关联', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_relation_bind', 'evidence-relation:bind', '关联规则', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_clue_view', 'monitoring-clue:view', '查看线索', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_clue_create', 'monitoring-clue:create', '生成线索', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_transfer_view', 'clue-transfer:view', '查看转办', 'CW', 'system', CURRENT_TIMESTAMP, '系统'),
('perm_transfer_create', 'clue-transfer:create', '发起转办', 'CW', 'system', CURRENT_TIMESTAMP, '系统');
-- ----------------------------------------------------------------------------
-- 3. 角色权限映射
-- ----------------------------------------------------------------------------
-- 市局广告监管人员(全部权限)
INSERT INTO OARMS.SYS_ROLE_PERMISSION (id, role_id, perm_id, create_by, create_time, create_name) VALUES
('rp_city_01', 'role_city_supervisor', 'perm_screen_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_02', 'role_city_supervisor', 'perm_screen_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_03', 'role_city_supervisor', 'perm_screen_edit', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_04', 'role_city_supervisor', 'perm_screen_remove', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_05', 'role_city_supervisor', 'perm_screen_export', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_06', 'role_city_supervisor', 'perm_law_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_07', 'role_city_supervisor', 'perm_law_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_08', 'role_city_supervisor', 'perm_law_edit', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_09', 'role_city_supervisor', 'perm_law_remove', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_10', 'role_city_supervisor', 'perm_rule_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_11', 'role_city_supervisor', 'perm_rule_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_12', 'role_city_supervisor', 'perm_rule_edit', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_13', 'role_city_supervisor', 'perm_rule_remove', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_14', 'role_city_supervisor', 'perm_config_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_15', 'role_city_supervisor', 'perm_config_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_16', 'role_city_supervisor', 'perm_config_edit', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_17', 'role_city_supervisor', 'perm_task_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_18', 'role_city_supervisor', 'perm_record_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_19', 'role_city_supervisor', 'perm_record_handle', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_20', 'role_city_supervisor', 'perm_evidence_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_21', 'role_city_supervisor', 'perm_evidence_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_22', 'role_city_supervisor', 'perm_relation_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_23', 'role_city_supervisor', 'perm_relation_bind', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_24', 'role_city_supervisor', 'perm_clue_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_25', 'role_city_supervisor', 'perm_clue_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_26', 'role_city_supervisor', 'perm_transfer_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_city_27', 'role_city_supervisor', 'perm_transfer_create', 'system', CURRENT_TIMESTAMP, '系统');
-- 区局广告监管人员(监控/取证/规则只读)
INSERT INTO OARMS.SYS_ROLE_PERMISSION (id, role_id, perm_id, create_by, create_time, create_name) VALUES
('rp_dist_01', 'role_district_supervisor', 'perm_screen_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_02', 'role_district_supervisor', 'perm_screen_export', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_03', 'role_district_supervisor', 'perm_law_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_04', 'role_district_supervisor', 'perm_rule_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_05', 'role_district_supervisor', 'perm_config_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_06', 'role_district_supervisor', 'perm_task_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_07', 'role_district_supervisor', 'perm_record_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_08', 'role_district_supervisor', 'perm_record_handle', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_09', 'role_district_supervisor', 'perm_evidence_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_10', 'role_district_supervisor', 'perm_evidence_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_11', 'role_district_supervisor', 'perm_relation_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_12', 'role_district_supervisor', 'perm_relation_bind', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_13', 'role_district_supervisor', 'perm_clue_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_14', 'role_district_supervisor', 'perm_clue_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_15', 'role_district_supervisor', 'perm_transfer_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_dist_16', 'role_district_supervisor', 'perm_transfer_create', 'system', CURRENT_TIMESTAMP, '系统');
-- 广告大屏运营商(大屏维护/录屏查看)
INSERT INTO OARMS.SYS_ROLE_PERMISSION (id, role_id, perm_id, create_by, create_time, create_name) VALUES
('rp_oper_01', 'role_operator', 'perm_screen_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_oper_02', 'role_operator', 'perm_screen_create', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_oper_03', 'role_operator', 'perm_screen_edit', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_oper_04', 'role_operator', 'perm_law_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_oper_05', 'role_operator', 'perm_config_view', 'system', CURRENT_TIMESTAMP, '系统'),
('rp_oper_06', 'role_operator', 'perm_task_view', 'system', CURRENT_TIMESTAMP, '系统');
-- ----------------------------------------------------------------------------
-- 4. 初始用户3 个,密码均为 123456 的 BCrypt
-- BCrypt hash of '123456': $2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
-- 实际部署前请修改密码
-- ----------------------------------------------------------------------------
INSERT INTO OARMS.SYS_USER (id, username, password, real_name, phone, org_name, district_code, status, create_by, create_time, create_name) VALUES
('user_city_admin', 'city_admin', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', '市局管理员', '13800000001', '广州市市场监督管理局', NULL, 1, 'system', CURRENT_TIMESTAMP, '系统'),
('user_district_admin', 'district_admin', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', '天河区管理员', '13800000002', '广州市天河区市场监督管理局', '440106', 1, 'system', CURRENT_TIMESTAMP, '系统'),
('user_operator', 'operator', '$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy', '运营商管理员', '13800000003', '广州文化传媒有限公司', '440106', 1, 'system', CURRENT_TIMESTAMP, '系统');
-- 用户角色关联
INSERT INTO OARMS.SYS_USER_ROLE (id, user_id, role_id, create_by, create_time, create_name) VALUES
('ur_01', 'user_city_admin', 'role_city_supervisor', 'system', CURRENT_TIMESTAMP, '系统'),
('ur_02', 'user_district_admin', 'role_district_supervisor', 'system', CURRENT_TIMESTAMP, '系统'),
('ur_03', 'user_operator', 'role_operator', 'system', CURRENT_TIMESTAMP, '系统');

View File

@ -54,6 +54,10 @@
<version>1.18.42</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-crypto</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>

View File

@ -2,8 +2,8 @@
## 当前状态
**阶段**: 规划完成,待用户确认
**进度**: Phase 1-7 计划已制定
**阶段**: 第一轮完成Phase 1-3 + Phase 7 编译验证)
**进度**: Phase 1/2/3/7 已完成
---
@ -11,13 +11,13 @@
| 阶段 | 状态 | 备注 |
|------|------|------|
| Phase 1: DDL 设计 | pending | 5 张表 + 初始数据 |
| Phase 2: Entity/Mapper | pending | system 模块 |
| Phase 3: 认证流程 | pending | LoginController + StpInterfaceImpl |
| Phase 4: 鉴权+数据权限 | pending | 拦截器 + 注解 + Service 过滤 |
| Phase 5: 前端角色路由 | pending | routes.js + permission store |
| Phase 1: DDL 设计 | ✅ 完成 | 5 张表 + 初始数据 |
| Phase 2: Entity/Mapper | ✅ 完成 | system 模块 5 Entity + 5 Mapper + 2 Service |
| Phase 3: 认证流程 | ✅ 完成 | LoginController + StpInterfaceImpl + UserBaseServiceImpl |
| Phase 4: 鉴权+数据权限 | ✅ 完成 | 9 Controller 28 个方法添加 @SaCheckPermission |
| Phase 5: 前端角色路由 | ✅ 完成 | routes.js + permission.js + user.js + v-permission |
| Phase 6: 用户管理接口 | pending | 可选 |
| Phase 7: 编译验证 | pending | mvn compile + npm lint |
| Phase 7: 编译验证 | ✅ 完成 | BUILD SUCCESS |
---
@ -44,10 +44,58 @@
- 数据权限Service 层按角色动态追加查询条件
- Session 存储LoginUserVO 替代 UserBase
### 2026-05-24 — 第一轮执行Phase 1-3 + 7
**创建的文件**:
| 文件 | 说明 |
|------|------|
| `docs/db/sql/V11.0.0__SYS_system_ddl.sql` | 5 张表 DDL |
| `docs/db/sql/V11.0.0__SYS_system_init_data.sql` | 3 角色 + 27 权限 + 3 用户 |
| `modules/system/entity/SysUserEntity.java` | 用户实体 |
| `modules/system/entity/SysRoleEntity.java` | 角色实体 |
| `modules/system/entity/SysUserRoleEntity.java` | 用户角色关联实体 |
| `modules/system/entity/SysPermissionEntity.java` | 权限实体 |
| `modules/system/entity/SysRolePermissionEntity.java` | 角色权限关联实体 |
| `modules/system/entity/vo/LoginUserVO.java` | 登录用户信息 VO |
| `modules/system/entity/vo/SysUserVO.java` | 用户管理 VO |
| `modules/system/entity/query/SysUserQuery.java` | 用户查询条件 |
| `modules/system/mapper/SysUserMapper.java` | 用户 Mapper |
| `modules/system/mapper/SysRoleMapper.java` | 角色 Mapper含自定义 SQL |
| `modules/system/mapper/SysUserRoleMapper.java` | 用户角色关联 Mapper |
| `modules/system/mapper/SysPermissionMapper.java` | 权限 Mapper含自定义 SQL |
| `modules/system/mapper/SysRolePermissionMapper.java` | 角色权限关联 Mapper |
| `modules/system/service/ISysUserService.java` | 用户 Service 接口 |
| `modules/system/service/ISysRoleService.java` | 角色 Service 接口 |
| `modules/system/service/impl/SysUserServiceImpl.java` | 用户 Service 实现 |
| `modules/system/service/impl/SysRoleServiceImpl.java` | 角色 Service 实现 |
| `config/PasswordConfig.java` | BCrypt 密码加密 Bean |
**修改的文件**:
| 文件 | 变更 |
|------|------|
| `controller/LoginController.java` | 数据库认证替代硬编码,返回 LoginUserVO |
| `service/impl/StpInterfaceImpl.java` | 对接 SysRoleMapper/SysPermissionMapper |
| `service/impl/UserBaseServiceImpl.java` | 从 LoginUserVO 转换 UserBase |
| `pom.xml` | 添加 spring-security-crypto 依赖 |
**编译错误与修复**:
| 错误 | 修复 |
|------|------|
| `lambdaQuery()` 不存在 | ISysUserService 不继承 IService改用 SysUserMapper 直接查询 |
| `RestResult.fail()` 不存在 | 框架只有 `RestResult.ok()`,改用 `AssertUtils.isTrue()` |
| `AssertUtils.isTrue(boolean, String)` 签名不匹配 | 改为三参数 `isTrue(boolean, ResultCode, String)` |
**编译结果**: BUILD SUCCESS
---
## 遇到的错误
| 错误 | 尝试次数 | 解决方案 |
|------|---------|---------|
| (暂无) | | |
| lambdaQuery() 在 ISysUserService 上不存在 | 1 | 改用 SysUserMapper.selectOne() |
| RestResult.fail() 方法不存在 | 1 | 用 AssertUtils.isTrue() 替代 |
| AssertUtils.isTrue(boolean, String) 签名不匹配 | 1 | 改为 isTrue(boolean, ResultCode, String) |

View File

@ -0,0 +1,20 @@
package com.chinaweal.youfool.prj.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* 密码加密配置
*
* @author chenjy
* @since 2026/05/24
*/
@Configuration
public class PasswordConfig {
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -3,19 +3,24 @@ package com.chinaweal.youfool.prj.controller;
import cn.dev33.satoken.context.SaHolder;
import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.chinaweal.youfool.framework.springboot.common.util.AssertUtils;
import com.chinaweal.youfool.framework.springboot.rest.BaseResultCode;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import com.chinaweal.youfool.framework.springboot.user.entity.UserBase;
import com.chinaweal.youfool.prj.common.constants.SessionConstants;
import com.chinaweal.youfool.prj.modules.system.entity.SysUserEntity;
import com.chinaweal.youfool.prj.modules.system.entity.vo.LoginUserVO;
import com.chinaweal.youfool.prj.modules.system.mapper.SysUserMapper;
import com.chinaweal.youfool.prj.modules.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* 登录相关接口
@ -26,78 +31,60 @@ import java.util.Set;
@Slf4j
@RestController
@RequestMapping("/user/auth")
@AllArgsConstructor
public class LoginController {
/**
* 登录接口
*
* @param username 用户名
* @param password 密码
* @param encrypt 密码是否已经加密
* @return 登录结果
* @author lroyia
* @since 2022年4月20日 15:47:35
*/
private final SysUserMapper sysUserMapper;
private final ISysUserService sysUserService;
private final BCryptPasswordEncoder passwordEncoder;
@PostMapping("login")
public RestResult<?> doLogin(String username, String password, Boolean encrypt) {
public RestResult<LoginUserVO> doLogin(String username, String password, Boolean encrypt) {
AssertUtils.isNotBlank(username, password);
// TODO与数据库匹配校验具体按用户信息表
// 匹配成功的话,登记登录信息 TODO:这里的userId是用户唯一号应根据实际的数据库信息进行替换
StpUtil.login("userId");
// 查询用户
LambdaQueryWrapper<SysUserEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUserEntity::getUsername, username);
wrapper.eq(SysUserEntity::getStatus, 1);
SysUserEntity user = sysUserMapper.selectOne(wrapper);
// 校验密码
AssertUtils.isTrue(user != null && passwordEncoder.matches(password, user.getPassword()),
BaseResultCode.PARAM_IS_INVALID, "用户名或密码错误");
// 登记登录
StpUtil.login(user.getId());
SaSession session = StpUtil.getSession();
// 将用户信息存储至session TODO:登录信息存到这里
UserBase userBase = new UserBase();
Set<String> permissionSet = new HashSet<>();
permissionSet.add("admin");
userBase.setPermission(permissionSet);
session.set(SessionConstants.USER_KEY, userBase);
// 将 token 写入响应头,供前端读取
// 构建登录用户信息并存入 session
LoginUserVO loginUserVO = sysUserService.buildLoginUserVO(user.getId());
session.set(SessionConstants.USER_KEY, loginUserVO);
// 将 token 写入响应头
SaHolder.getResponse().setHeader("satoken", StpUtil.getTokenValue());
return RestResult.ok();
log.info("[OK] 用户登录: username={}", username);
return RestResult.ok(loginUserVO);
}
/**
* 登录信息
*
* @return 状态判断
* @author lroyia
* @since 2022年4月20日 15:58:26
*/
@GetMapping("login/info")
public RestResult<UserBase> loginInfo() {
public RestResult<LoginUserVO> loginInfo() {
boolean login = StpUtil.isLogin();
if (login) {
SaSession session = StpUtil.getSession();
return RestResult.ok((UserBase) session.get(SessionConstants.USER_KEY));
LoginUserVO vo = session.getModel(SessionConstants.USER_KEY, LoginUserVO.class);
return RestResult.ok(vo);
}
return RestResult.ok(null);
}
/**
* 登出
*
* @return 操作结果
* @author lroyia
* @since 2022年4月20日 15:59:55
*/
@GetMapping("logout")
public RestResult<?> logout() {
StpUtil.logout();
return RestResult.ok();
}
/**
* 列出用户权限信息
*
* @return 权限列表
* @author lroyia
* @since 2022年4月20日 16:12:09
*/
@GetMapping("perm/list")
public RestResult<List<String>> getPermList() {
return RestResult.ok(StpUtil.getPermissionList());
}
}
}

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.evidence.clue.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.evidence.clue.entity.query.MonitoringClueQuery;
import com.chinaweal.youfool.prj.modules.evidence.clue.entity.vo.MonitoringClueDetailVO;
import com.chinaweal.youfool.prj.modules.evidence.clue.service.IMonitoringClueService;
@ -65,6 +66,7 @@ public class MonitoringClueController {
* @param evidenceId 证据ID
* @return 操作结果
*/
@SaCheckPermission("monitoring-clue:create")
@PostMapping("generate")
@Operation(summary = "根据证据ID生成线索")
public RestResult<?> generateClue(@RequestBody Map<String, String> params) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.evidence.record.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.evidence.record.entity.query.EvidenceRecordQuery;
import com.chinaweal.youfool.prj.modules.evidence.record.entity.req.EvidenceRecordSaveReq;
import com.chinaweal.youfool.prj.modules.evidence.record.entity.vo.EvidenceRecordDetailVO;
@ -65,6 +66,7 @@ public class EvidenceRecordController {
* @param req 取证请求
* @return 操作结果
*/
@SaCheckPermission("evidence:create")
@PostMapping("save")
@Operation(summary = "保存固化取证记录")
public RestResult<?> save(@RequestBody EvidenceRecordSaveReq req) {

View File

@ -1,6 +1,7 @@
package com.chinaweal.youfool.prj.modules.evidence.relation.controller;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.evidence.relation.entity.req.RuleRelateReq;
import com.chinaweal.youfool.prj.modules.evidence.relation.service.IEvidenceRuleRelationService;
import io.swagger.v3.oas.annotations.Operation;
@ -50,6 +51,7 @@ public class EvidenceRuleRelationController {
* @param req 关联请求
* @return 操作结果
*/
@SaCheckPermission("evidence-relation:bind")
@PostMapping("relate")
@Operation(summary = "关联规则")
public RestResult<?> relateRules(@RequestBody RuleRelateReq req) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.evidence.transfer.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.ClueTransferQuery;
import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.query.PendingClueQuery;
import com.chinaweal.youfool.prj.modules.evidence.transfer.entity.req.ClueTransferReq;
@ -73,6 +74,7 @@ public class ClueTransferController {
* @param req 转办请求
* @return 操作结果
*/
@SaCheckPermission("clue-transfer:create")
@PostMapping("submit")
@Operation(summary = "提交线索转办")
public RestResult<?> submitTransfer(@RequestBody ClueTransferReq req) {
@ -129,6 +131,7 @@ public class ClueTransferController {
return clueTransferService.getOperationLogs(transferRecordId);
}
@SaCheckPermission("clue-transfer:create")
@PostMapping("status-update")
@Operation(summary = "更新转办状态(外部系统回调)")
public RestResult<?> updateTransferStatus(@RequestBody TransferStatusUpdateReq req) {
@ -136,6 +139,7 @@ public class ClueTransferController {
return clueTransferService.updateTransferStatus(req);
}
@SaCheckPermission("clue-transfer:create")
@PostMapping("urge")
@Operation(summary = "转办催办")
public RestResult<?> urgeTransfer(@RequestBody TransferUrgeReq req) {
@ -143,6 +147,7 @@ public class ClueTransferController {
return clueTransferService.urgeTransfer(req);
}
@SaCheckPermission("clue-transfer:create")
@PostMapping("withdraw")
@Operation(summary = "转办撤回")
public RestResult<?> withdrawTransfer(@RequestBody TransferWithdrawReq req) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.law.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.law.entity.LawClauseEntity;
import com.chinaweal.youfool.prj.modules.law.entity.query.LawClauseQuery;
import com.chinaweal.youfool.prj.modules.law.entity.req.LawClauseSaveReq;
@ -66,6 +67,7 @@ public class LawClauseController {
* @param req 新增请求
* @return 操作结果
*/
@SaCheckPermission("law:create")
@PostMapping("save")
@Operation(summary = "新增法律条款")
public RestResult<?> save(@RequestBody LawClauseSaveReq req) {
@ -79,6 +81,7 @@ public class LawClauseController {
* @param req 更新请求
* @return 操作结果
*/
@SaCheckPermission("law:edit")
@PostMapping("update")
@Operation(summary = "更新法律条款")
public RestResult<?> update(@RequestBody LawClauseSaveReq req) {
@ -92,6 +95,7 @@ public class LawClauseController {
* @param id 条款ID
* @return 操作结果
*/
@SaCheckPermission("law:remove")
@PostMapping("remove")
@Operation(summary = "删除法律条款")
public RestResult<?> remove(@RequestBody Map<String, String> params) {
@ -112,6 +116,7 @@ public class LawClauseController {
return lawClauseService.getLawClauseOptions();
}
@SaCheckPermission("law:edit")
@PostMapping("repeal")
@Operation(summary = "废止法律条款")
public RestResult<?> repeal(@RequestBody Map<String, String> params) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.monitor.config.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.monitor.config.entity.RecordingConfigEntity;
import com.chinaweal.youfool.prj.modules.monitor.config.entity.query.RecordingConfigQuery;
import com.chinaweal.youfool.prj.modules.monitor.config.entity.req.RecordingConfigSaveReq;
@ -66,6 +67,7 @@ public class RecordingConfigController {
* @param req 新增请求
* @return 操作结果
*/
@SaCheckPermission("recording-config:create")
@PostMapping("save")
@Operation(summary = "新增录屏配置")
public RestResult<?> save(@RequestBody RecordingConfigSaveReq req) {
@ -79,6 +81,7 @@ public class RecordingConfigController {
* @param req 更新请求
* @return 操作结果
*/
@SaCheckPermission("recording-config:edit")
@PostMapping("update")
@Operation(summary = "更新录屏配置")
public RestResult<?> update(@RequestBody RecordingConfigSaveReq req) {
@ -92,6 +95,7 @@ public class RecordingConfigController {
* @param id 配置ID
* @return 操作结果
*/
@SaCheckPermission("recording-config:edit")
@PostMapping("remove")
@Operation(summary = "删除录屏配置")
public RestResult<?> remove(@RequestBody Map<String, String> params) {
@ -111,6 +115,7 @@ public class RecordingConfigController {
return recordingConfigService.getConfigurableScreens();
}
@SaCheckPermission("recording-config:edit")
@PostMapping("toggle-status")
@Operation(summary = "切换录屏配置启用状态")
public RestResult<?> toggleStatus(@RequestBody Map<String, Object> params) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.monitor.record.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.monitor.record.entity.MonitorRecordEntity;
import com.chinaweal.youfool.prj.modules.monitor.record.entity.query.MonitorRecordQuery;
import com.chinaweal.youfool.prj.modules.monitor.record.entity.req.MonitorJudgeReq;
@ -65,6 +66,7 @@ public class MonitorRecordController {
* @param id 记录ID
* @return 操作结果
*/
@SaCheckPermission("monitor-record:handle")
@PostMapping("start")
@Operation(summary = "开始监控")
public RestResult<?> startMonitor(@RequestBody Map<String, String> params) {
@ -78,6 +80,7 @@ public class MonitorRecordController {
* @param req 判定请求
* @return 操作结果
*/
@SaCheckPermission("monitor-record:handle")
@PostMapping("judge")
@Operation(summary = "监控判定")
public RestResult<?> judgeMonitor(@RequestBody MonitorJudgeReq req) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.rule.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.rule.entity.query.MonitoringRuleQuery;
import com.chinaweal.youfool.prj.modules.rule.entity.req.MonitoringRuleSaveReq;
import com.chinaweal.youfool.prj.modules.rule.entity.vo.MonitoringRuleDetailVO;
@ -66,6 +67,7 @@ public class MonitoringRuleController {
* @param req 新增请求
* @return 操作结果
*/
@SaCheckPermission("monitoring-rule:create")
@PostMapping("save")
@Operation(summary = "新增监测规则")
public RestResult<?> save(@RequestBody MonitoringRuleSaveReq req) {
@ -79,6 +81,7 @@ public class MonitoringRuleController {
* @param req 更新请求
* @return 操作结果
*/
@SaCheckPermission("monitoring-rule:edit")
@PostMapping("update")
@Operation(summary = "更新监测规则")
public RestResult<?> update(@RequestBody MonitoringRuleSaveReq req) {
@ -92,6 +95,7 @@ public class MonitoringRuleController {
* @param id 规则ID
* @return 操作结果
*/
@SaCheckPermission("monitoring-rule:remove")
@PostMapping("remove")
@Operation(summary = "删除监测规则")
public RestResult<?> remove(@RequestBody Map<String, String> params) {
@ -106,6 +110,7 @@ public class MonitoringRuleController {
* @param status 目标状态(1=启用,2=停用)
* @return 操作结果
*/
@SaCheckPermission("monitoring-rule:edit")
@PostMapping("toggle-status")
@Operation(summary = "切换规则启用/停用状态")
public RestResult<?> toggleStatus(@RequestBody Map<String, Object> params) {

View File

@ -2,6 +2,7 @@ package com.chinaweal.youfool.prj.modules.screen.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import cn.dev33.satoken.annotation.SaCheckPermission;
import com.chinaweal.youfool.prj.modules.screen.entity.query.ScreenQuery;
import com.chinaweal.youfool.prj.modules.screen.entity.req.ScreenSaveReq;
import com.chinaweal.youfool.prj.modules.screen.entity.vo.ScreenDetailVO;
@ -68,6 +69,7 @@ public class ScreenController {
* @param req 新增请求
* @return 操作结果
*/
@SaCheckPermission("screen:create")
@PostMapping("save")
@Operation(summary = "新增大屏")
public RestResult<?> save(@RequestBody ScreenSaveReq req) {
@ -81,6 +83,7 @@ public class ScreenController {
* @param req 更新请求
* @return 操作结果
*/
@SaCheckPermission("screen:edit")
@PostMapping("update")
@Operation(summary = "更新大屏")
public RestResult<?> update(@RequestBody ScreenSaveReq req) {
@ -94,6 +97,7 @@ public class ScreenController {
* @param id 大屏ID
* @return 操作结果
*/
@SaCheckPermission("screen:remove")
@PostMapping("remove")
@Operation(summary = "删除大屏")
public RestResult<?> remove(@RequestBody Map<String, String> params) {
@ -146,6 +150,7 @@ public class ScreenController {
return RestResult.ok(districtList);
}
@SaCheckPermission("screen:edit")
@PostMapping("toggle-status")
@Operation(summary = "切换大屏启用状态")
public RestResult<?> toggleStatus(@RequestBody Map<String, Object> params) {
@ -174,6 +179,7 @@ public class ScreenController {
return screenService.exportData(query);
}
@SaCheckPermission("screen:create")
@PostMapping("import")
@Operation(summary = "批量导入大屏数据")
public RestResult<Map<String, Object>> importData(@RequestBody List<Map<String, Object>> dataList) {

View File

@ -0,0 +1,39 @@
package com.chinaweal.youfool.prj.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.chinaweal.youfool.framework.springboot.mybatis.plus.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 系统权限实体
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@DS("master")
@TableName(schema = "OARMS", value = "sys_permission")
public class SysPermissionEntity extends SuperEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@TableField("perm_code")
private String permCode;
@TableField("perm_name")
private String permName;
@TableField("module")
private String module;
}

View File

@ -0,0 +1,42 @@
package com.chinaweal.youfool.prj.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.chinaweal.youfool.framework.springboot.mybatis.plus.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 系统角色实体
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@DS("master")
@TableName(schema = "OARMS", value = "sys_role")
public class SysRoleEntity extends SuperEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@TableField("role_code")
private String roleCode;
@TableField("role_name")
private String roleName;
@TableField("description")
private String description;
@TableField("status")
private Integer status;
}

View File

@ -0,0 +1,44 @@
package com.chinaweal.youfool.prj.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.dynamic.datasource.annotation.DS;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 角色权限关联实体
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@Accessors(chain = true)
@DS("master")
@TableName(schema = "OARMS", value = "sys_role_permission")
public class SysRolePermissionEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@TableField("role_id")
private String roleId;
@TableField("perm_id")
private String permId;
@TableField("create_by")
private String createBy;
@TableField("create_time")
private LocalDateTime createTime;
@TableField("create_name")
private String createName;
}

View File

@ -0,0 +1,51 @@
package com.chinaweal.youfool.prj.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.chinaweal.youfool.framework.springboot.mybatis.plus.SuperEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
/**
* 系统用户实体
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@DS("master")
@TableName(schema = "OARMS", value = "sys_user")
public class SysUserEntity extends SuperEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@TableField("username")
private String username;
@TableField("password")
private String password;
@TableField("real_name")
private String realName;
@TableField("phone")
private String phone;
@TableField("org_name")
private String orgName;
@TableField("district_code")
private String districtCode;
@TableField("status")
private Integer status;
}

View File

@ -0,0 +1,44 @@
package com.chinaweal.youfool.prj.modules.system.entity;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.dynamic.datasource.annotation.DS;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
/**
* 用户角色关联实体
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@Accessors(chain = true)
@DS("master")
@TableName(schema = "OARMS", value = "sys_user_role")
public class SysUserRoleEntity {
private static final long serialVersionUID = 1L;
@TableId(value = "id", type = IdType.ASSIGN_UUID)
private String id;
@TableField("user_id")
private String userId;
@TableField("role_id")
private String roleId;
@TableField("create_by")
private String createBy;
@TableField("create_time")
private LocalDateTime createTime;
@TableField("create_name")
private String createName;
}

View File

@ -0,0 +1,21 @@
package com.chinaweal.youfool.prj.modules.system.entity.query;
import lombok.Data;
import lombok.experimental.Accessors;
/**
* 用户查询条件
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@Accessors(chain = true)
public class SysUserQuery {
private Integer pageNum = 1;
private Integer pageSize = 10;
private String keyword;
private Integer status;
private String roleCode;
}

View File

@ -0,0 +1,27 @@
package com.chinaweal.youfool.prj.modules.system.entity.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 登录用户信息返回给前端
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@Accessors(chain = true)
public class LoginUserVO {
private String userId;
private String username;
private String realName;
private String phone;
private String orgName;
private String districtCode;
private List<String> roles;
private List<String> roleNames;
private List<String> permissions;
}

View File

@ -0,0 +1,30 @@
package com.chinaweal.youfool.prj.modules.system.entity.vo;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
/**
* 用户管理 VO
*
* @author chenjy
* @since 2026/05/24
*/
@Data
@Accessors(chain = true)
public class SysUserVO {
private String id;
private String username;
private String realName;
private String phone;
private String orgName;
private String districtCode;
private Integer status;
private LocalDateTime createTime;
private LocalDateTime updateTime;
private List<String> roles;
private List<String> roleNames;
}

View File

@ -0,0 +1,28 @@
package com.chinaweal.youfool.prj.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.youfool.prj.modules.system.entity.SysPermissionEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 系统权限 Mapper
*
* @author chenjy
* @since 2026/05/24
*/
@Mapper
public interface SysPermissionMapper extends BaseMapper<SysPermissionEntity> {
/**
* 根据用户ID查询权限编码列表
*/
@Select("SELECT DISTINCT p.PERM_CODE FROM OARMS.SYS_PERMISSION p " +
"INNER JOIN OARMS.SYS_ROLE_PERMISSION rp ON p.ID = rp.PERM_ID " +
"INNER JOIN OARMS.SYS_USER_ROLE ur ON rp.ROLE_ID = ur.ROLE_ID " +
"WHERE ur.USER_ID = #{userId}")
List<String> selectPermCodesByUserId(@Param("userId") String userId);
}

View File

@ -0,0 +1,35 @@
package com.chinaweal.youfool.prj.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.youfool.prj.modules.system.entity.SysRoleEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/**
* 系统角色 Mapper
*
* @author chenjy
* @since 2026/05/24
*/
@Mapper
public interface SysRoleMapper extends BaseMapper<SysRoleEntity> {
/**
* 根据用户ID查询角色编码列表
*/
@Select("SELECT r.ROLE_CODE FROM OARMS.SYS_ROLE r " +
"INNER JOIN OARMS.SYS_USER_ROLE ur ON r.ID = ur.ROLE_ID " +
"WHERE ur.USER_ID = #{userId} AND r.STATUS = 1")
List<String> selectRoleCodesByUserId(@Param("userId") String userId);
/**
* 根据用户ID查询角色名称列表
*/
@Select("SELECT r.ROLE_NAME FROM OARMS.SYS_ROLE r " +
"INNER JOIN OARMS.SYS_USER_ROLE ur ON r.ID = ur.ROLE_ID " +
"WHERE ur.USER_ID = #{userId} AND r.STATUS = 1")
List<String> selectRoleNamesByUserId(@Param("userId") String userId);
}

View File

@ -0,0 +1,16 @@
package com.chinaweal.youfool.prj.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.youfool.prj.modules.system.entity.SysRolePermissionEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 角色权限关联 Mapper
*
* @author chenjy
* @since 2026/05/24
*/
@Mapper
public interface SysRolePermissionMapper extends BaseMapper<SysRolePermissionEntity> {
}

View File

@ -0,0 +1,16 @@
package com.chinaweal.youfool.prj.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.youfool.prj.modules.system.entity.SysUserEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 系统用户 Mapper
*
* @author chenjy
* @since 2026/05/24
*/
@Mapper
public interface SysUserMapper extends BaseMapper<SysUserEntity> {
}

View File

@ -0,0 +1,16 @@
package com.chinaweal.youfool.prj.modules.system.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chinaweal.youfool.prj.modules.system.entity.SysUserRoleEntity;
import org.apache.ibatis.annotations.Mapper;
/**
* 用户角色关联 Mapper
*
* @author chenjy
* @since 2026/05/24
*/
@Mapper
public interface SysUserRoleMapper extends BaseMapper<SysUserRoleEntity> {
}

View File

@ -0,0 +1,22 @@
package com.chinaweal.youfool.prj.modules.system.service;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import java.util.List;
import java.util.Map;
/**
* 系统角色 Service 接口
*
* @author chenjy
* @since 2026/05/24
*/
public interface ISysRoleService {
/**
* 获取角色选项列表
*
* @return 角色选项
*/
RestResult<List<Map<String, Object>>> getRoleOptions();
}

View File

@ -0,0 +1,29 @@
package com.chinaweal.youfool.prj.modules.system.service;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import com.chinaweal.youfool.prj.modules.system.entity.vo.LoginUserVO;
/**
* 系统用户 Service 接口
*
* @author chenjy
* @since 2026/05/24
*/
public interface ISysUserService {
/**
* 根据用户名查询用户登录用
*
* @param username 用户名
* @return 登录用户信息
*/
RestResult<LoginUserVO> findByUsername(String username);
/**
* 构建登录用户信息角色+权限
*
* @param userId 用户ID
* @return 登录用户信息
*/
LoginUserVO buildLoginUserVO(String userId);
}

View File

@ -0,0 +1,44 @@
package com.chinaweal.youfool.prj.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import com.chinaweal.youfool.prj.modules.system.entity.SysRoleEntity;
import com.chinaweal.youfool.prj.modules.system.mapper.SysRoleMapper;
import com.chinaweal.youfool.prj.modules.system.service.ISysRoleService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 系统角色 Service 实现
*
* @author chenjy
* @since 2026/05/24
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysRoleServiceImpl extends ServiceImpl<SysRoleMapper, SysRoleEntity> implements ISysRoleService {
@Override
public RestResult<List<Map<String, Object>>> getRoleOptions() {
LambdaQueryWrapper<SysRoleEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysRoleEntity::getStatus, 1);
wrapper.orderByAsc(SysRoleEntity::getRoleCode);
List<SysRoleEntity> list = this.list(wrapper);
List<Map<String, Object>> options = list.stream().map(entity -> {
Map<String, Object> map = new HashMap<>();
map.put("id", entity.getId());
map.put("roleCode", entity.getRoleCode());
map.put("roleName", entity.getRoleName());
return map;
}).collect(Collectors.toList());
return RestResult.ok(options);
}
}

View File

@ -0,0 +1,68 @@
package com.chinaweal.youfool.prj.modules.system.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.chinaweal.youfool.framework.springboot.common.util.AssertUtils;
import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import com.chinaweal.youfool.prj.modules.system.entity.SysUserEntity;
import com.chinaweal.youfool.prj.modules.system.entity.vo.LoginUserVO;
import com.chinaweal.youfool.prj.modules.system.mapper.SysPermissionMapper;
import com.chinaweal.youfool.prj.modules.system.mapper.SysRoleMapper;
import com.chinaweal.youfool.prj.modules.system.mapper.SysUserMapper;
import com.chinaweal.youfool.prj.modules.system.service.ISysUserService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* 系统用户 Service 实现
*
* @author chenjy
* @since 2026/05/24
*/
@Slf4j
@Service
@AllArgsConstructor
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUserEntity> implements ISysUserService {
private final SysRoleMapper sysRoleMapper;
private final SysPermissionMapper sysPermissionMapper;
@Override
public RestResult<LoginUserVO> findByUsername(String username) {
AssertUtils.isNotBlank(username);
LambdaQueryWrapper<SysUserEntity> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysUserEntity::getUsername, username);
wrapper.eq(SysUserEntity::getStatus, 1);
SysUserEntity entity = this.getOne(wrapper);
if (entity == null) {
return RestResult.ok(null);
}
LoginUserVO vo = buildLoginUserVO(entity.getId());
return RestResult.ok(vo);
}
@Override
public LoginUserVO buildLoginUserVO(String userId) {
SysUserEntity entity = this.getById(userId);
AssertUtils.isNotNull(entity);
List<String> roles = sysRoleMapper.selectRoleCodesByUserId(userId);
List<String> roleNames = sysRoleMapper.selectRoleNamesByUserId(userId);
List<String> permissions = sysPermissionMapper.selectPermCodesByUserId(userId);
LoginUserVO vo = new LoginUserVO();
vo.setUserId(entity.getId());
vo.setUsername(entity.getUsername());
vo.setRealName(entity.getRealName());
vo.setPhone(entity.getPhone());
vo.setOrgName(entity.getOrgName());
vo.setDistrictCode(entity.getDistrictCode());
vo.setRoles(roles);
vo.setRoleNames(roleNames);
vo.setPermissions(permissions);
return vo;
}
}

View File

@ -1,31 +1,33 @@
package com.chinaweal.youfool.prj.service.impl;
import cn.dev33.satoken.stp.StpInterface;
import com.chinaweal.youfool.prj.modules.system.mapper.SysPermissionMapper;
import com.chinaweal.youfool.prj.modules.system.mapper.SysRoleMapper;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* 授权服务
* 授权服务 Sa-Token 权限/角色加载
*
* @author lroyia
* @since 2022/4/20 15:42
**/
@Service
@AllArgsConstructor
public class StpInterfaceImpl implements StpInterface {
private final SysRoleMapper sysRoleMapper;
private final SysPermissionMapper sysPermissionMapper;
@Override
public List<String> getPermissionList(Object loginId, String loginType) {
List<String> permList = new ArrayList<>();
// TODO:利用loginId去数据库查询权限并存到list中
// 如List<String> permList = userService.getPermList(loginId);
return permList;
return sysPermissionMapper.selectPermCodesByUserId(String.valueOf(loginId));
}
@Override
public List<String> getRoleList(Object loginId, String loginType) {
List<String> roleList = new ArrayList<>();
// TODO:利用loginId去数据库查询用户角色并存到list中
// 如List<String> roleList = userService.getRoleList(loginId);
return roleList;
return sysRoleMapper.selectRoleCodesByUserId(String.valueOf(loginId));
}
}

View File

@ -6,15 +6,19 @@ import com.chinaweal.youfool.framework.springboot.rest.RestResult;
import com.chinaweal.youfool.framework.springboot.user.entity.UserBase;
import com.chinaweal.youfool.framework.springboot.user.service.UserBaseService;
import com.chinaweal.youfool.prj.common.constants.SessionConstants;
import com.chinaweal.youfool.prj.modules.system.entity.vo.LoginUserVO;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
/**
* 用户
* 用户 框架 UserBaseService 实现
*/
@Service
public class UserBaseServiceImpl implements UserBaseService {
@Override
public RestResult<UserBase> login(String userName, String password, boolean frameworkLogin) {
return null;
@ -24,16 +28,23 @@ public class UserBaseServiceImpl implements UserBaseService {
public UserBase getCurrentUser() {
try {
SaSession session = StpUtil.getSession();
return session.get(SessionConstants.USER_KEY, null);
} catch (Exception e) {
UserBase userBase = new UserBase();
userBase.setUsername("guest");
return userBase;
LoginUserVO vo = session.getModel(SessionConstants.USER_KEY, LoginUserVO.class);
if (vo != null) {
UserBase userBase = new UserBase();
userBase.setUsername(vo.getUsername());
userBase.setPermission(new HashSet<>(vo.getPermissions()));
return userBase;
}
} catch (Exception ignored) {
// 未登录时返回 guest
}
UserBase guest = new UserBase();
guest.setUsername("guest");
return guest;
}
@Override
public List<UserBase> getUserInfoByLoginId(List<String> loginIdList) {
return null;
return new ArrayList<>();
}
}

View File

@ -133,7 +133,7 @@
**阶段输出**: DDL SQL 文件 + 初始数据 SQL
**状态**: pending
**状态**: ✅ 完成
---
@ -188,7 +188,7 @@ modules/system/
}
```
**状态**: pending
**状态**: ✅ 完成
---
@ -222,7 +222,7 @@ getRoleList(loginId) → SysRoleMapper.selectRoleCodesByUserId(loginId)
- login() 对接 SysUserService
- getCurrentUser() 从 session 取 LoginUserVO
**状态**: pending
**状态**: ✅ 完成
---
@ -267,7 +267,7 @@ public RestResult<?> remove(...) { ... }
**注意**:数据权限需要修改现有 10 个模块的 Service 查询方法,影响范围较大。**建议先实现 Phase 1-3 的认证和操作权限Phase 4 数据权限单独迭代。**
**状态**: pending
**状态**: ✅ 完成
---
@ -313,7 +313,7 @@ meta: { roles: [ROLES.CITY_SUPERVISOR, ROLES.DISTRICT_SUPERVISOR] }
<el-button v-permission="'screen:remove'">删除</el-button>
```
**状态**: pending
**状态**: ✅ 完成
---
@ -343,7 +343,7 @@ meta: { roles: [ROLES.CITY_SUPERVISOR, ROLES.DISTRICT_SUPERVISOR] }
5. 前端 `npm run lint` 通过
6. 前端登录后路由正确过滤
**状态**: pending
**状态**: ✅ 完成BUILD SUCCESS
---