From 82b6f14e6489b8238175382034ed66f713d4a576 Mon Sep 17 00:00:00 2001 From: wangys <3401275564@qq.com> Date: Wed, 29 Oct 2025 19:08:22 +0800 Subject: [PATCH] =?UTF-8?q?serv\web-=20=E5=A4=9A=E7=A7=9F=E6=88=B7?= =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../.bin/mysql/sql/createTableAchievement.sql | 14 +- .../.bin/mysql/sql/createTableCrontab.sql | 2 +- .../.bin/mysql/sql/createTablePermission.sql | 22 +- .../sql/createTablePermissionControl.sql | 45 +- .../.bin/mysql/sql/createTableSystem.sql | 2 +- .../.bin/mysql/sql/createTableUser.sql | 6 +- schoolNewsServ/.bin/mysql/sql/initAllData.sql | 73 +- .../.bin/mysql/sql/initMenuData.sql | 167 ++-- .../achievement/mapper/AchievementMapper.java | 24 +- .../impl/ACHAchievementServiceImpl.java | 125 +-- .../resources/mapper/AchievementMapper.xml | 187 +++- .../src/main/resources/log4j2-spring.xml | 24 +- .../api/system/dept/DepartmentService.java | 7 +- .../permission/ResourcePermissionService.java | 27 + .../org/xyzh/api/system/role/RoleService.java | 6 +- .../org/xyzh/api/system/user/UserService.java | 2 +- .../org/xyzh/auth/config/SecurityConfig.java | 2 + .../xyzh/auth/service/LoginServiceImpl.java | 6 +- .../xyzh/common/core/domain/LoginDomain.java | 8 +- .../xyzh/common/core/enums/ResourceType.java | 36 + .../org/xyzh/common/dto/dept/TbSysDept.java | 16 + .../java/org/xyzh/common/vo/DeptRoleVO.java | 58 -- .../xyzh/common/vo/ResourcePermissionVO.java | 120 +++ .../org/xyzh/common/vo/UserDeptRoleVO.java | 158 +++- schoolNewsServ/crontab/pom.xml | 6 + .../service/impl/CrontabServiceImpl.java | 24 + .../org/xyzh/news/mapper/BannerMapper.java | 12 +- .../org/xyzh/news/mapper/ResourceMapper.java | 10 +- .../java/org/xyzh/news/mapper/TagMapper.java | 4 +- .../service/impl/NCBannerServiceImpl.java | 40 +- .../service/impl/NCResourceServiceImpl.java | 41 +- .../news/service/impl/NCTagServiceImpl.java | 31 +- .../main/resources/mapper/BannerMapper.xml | 138 ++- .../main/resources/mapper/ResourceMapper.xml | 116 ++- .../src/main/resources/mapper/TagMapper.xml | 55 +- .../org/xyzh/study/mapper/CourseMapper.java | 11 +- .../xyzh/study/mapper/LearningTaskMapper.java | 12 +- .../service/impl/SCCourseServiceImpl.java | 32 +- .../impl/SCLearningTaskServiceImpl.java | 39 +- .../main/resources/mapper/CourseMapper.xml | 115 ++- .../resources/mapper/LearningTaskMapper.xml | 119 ++- .../system/controller/DeptController.java | 13 +- .../system/controller/RoleController.java | 2 +- .../system/controller/UserController.java | 2 +- .../impl/SysDepartmentServiceImpl.java | 66 +- .../xyzh/system/mapper/DepartmentMapper.java | 4 +- .../xyzh/system/mapper/DeptRoleMapper.java | 11 +- .../mapper/ResourcePermissionMapper.java | 40 + .../org/xyzh/system/mapper/RoleMapper.java | 6 +- .../system/mapper/UserDeptRoleMapper.java | 27 +- .../SysResourcePermissionServiceImpl.java | 173 ++++ .../role/service/impl/SysRoleServiceImpl.java | 8 +- .../user/service/impl/SysUserServiceImpl.java | 431 ++++----- .../java/org/xyzh/system/utils/LoginUtil.java | 24 + .../resources/mapper/DepartmentMapper.xml | 146 +-- .../main/resources/mapper/DeptRoleMapper.xml | 103 ++- .../mapper/ResourcePermissionMapper.xml | 80 ++ .../src/main/resources/mapper/RoleMapper.xml | 55 +- .../resources/mapper/UserDeptRoleMapper.xml | 59 +- .../src/main/resources/mapper/UserMapper.xml | 29 +- schoolNewsWeb/package-lock.json | 10 +- schoolNewsWeb/package.json | 2 +- schoolNewsWeb/public/img/admin/maintain.svg | 6 + schoolNewsWeb/public/img/admin/resource.svg | 7 + schoolNewsWeb/src/apis/study/learning-task.ts | 4 +- schoolNewsWeb/src/apis/system/dept.ts | 6 +- schoolNewsWeb/src/apis/system/user.ts | 4 +- .../src/components/base/GenericSelector.vue | 862 ++++++++++++++++++ .../src/components/base/TreeNode.vue | 221 +++++ schoolNewsWeb/src/components/base/index.ts | 4 +- .../src/components/base/selector-styles.scss | 301 ++++++ schoolNewsWeb/src/components/index.ts | 1 - schoolNewsWeb/src/components/user/README.md | 213 ----- .../src/components/user/UserSelect.vue | 781 ---------------- schoolNewsWeb/src/components/user/index.ts | 8 - schoolNewsWeb/src/config/index.ts | 2 +- schoolNewsWeb/src/types/dept/index.ts | 49 +- schoolNewsWeb/src/types/user/index.ts | 13 - .../manage/study/StudyManagementView.vue | 2 - .../admin/manage/system/DeptManageView.vue | 188 +--- .../admin/manage/system/MenuManageView.vue | 209 ++--- .../system/ModulePermissionManageView.vue | 330 +++---- .../admin/manage/system/RoleManageView.vue | 152 +-- .../admin/manage/system/UserManageView.vue | 349 ++++--- .../views/public/task/LearningTaskList.vue | 223 ++--- schoolNewsWeb/yarn.lock | 8 +- 86 files changed, 4446 insertions(+), 2730 deletions(-) create mode 100644 schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/permission/ResourcePermissionService.java create mode 100644 schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/enums/ResourceType.java delete mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/DeptRoleVO.java create mode 100644 schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourcePermissionVO.java create mode 100644 schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/ResourcePermissionMapper.java create mode 100644 schoolNewsServ/system/src/main/java/org/xyzh/system/permission/service/impl/SysResourcePermissionServiceImpl.java create mode 100644 schoolNewsServ/system/src/main/resources/mapper/ResourcePermissionMapper.xml create mode 100644 schoolNewsWeb/public/img/admin/maintain.svg create mode 100644 schoolNewsWeb/public/img/admin/resource.svg create mode 100644 schoolNewsWeb/src/components/base/GenericSelector.vue create mode 100644 schoolNewsWeb/src/components/base/TreeNode.vue create mode 100644 schoolNewsWeb/src/components/base/selector-styles.scss delete mode 100644 schoolNewsWeb/src/components/user/README.md delete mode 100644 schoolNewsWeb/src/components/user/UserSelect.vue delete mode 100644 schoolNewsWeb/src/components/user/index.ts diff --git a/schoolNewsServ/.bin/mysql/sql/createTableAchievement.sql b/schoolNewsServ/.bin/mysql/sql/createTableAchievement.sql index 391a085..6ae0f06 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableAchievement.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableAchievement.sql @@ -38,7 +38,7 @@ CREATE TABLE `tb_achievement` ( KEY `idx_condition_type` (`condition_type`), KEY `idx_deleted` (`deleted`), KEY `idx_create_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='成就定义表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='成就定义表'; -- ============================================= -- 2. 用户成就表 (tb_user_achievement) @@ -56,7 +56,7 @@ CREATE TABLE `tb_user_achievement` ( KEY `idx_user_id` (`user_id`), KEY `idx_achievement_id` (`achievement_id`), KEY `idx_obtain_time` (`obtain_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户成就表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户成就表'; -- ============================================= -- 3. 用户成就进度表 (tb_user_achievement_progress) @@ -80,7 +80,7 @@ CREATE TABLE `tb_user_achievement_progress` ( KEY `idx_achievement_id` (`achievement_id`), KEY `idx_completed` (`completed`), KEY `idx_last_update_time` (`last_update_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='用户成就进度表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='用户成就进度表'; -- ============================================= -- 初始化成就数据 @@ -93,14 +93,6 @@ CREATE TABLE `tb_user_achievement_progress` ( -- 学习时长类成就 -- ============================================= -INSERT INTO `tb_achievement` (`id`, `achievement_id`, `name`,`icon`, `description`, `type`, `level`, `condition_type`, `condition_value`, `points`, `order_num`, `deleted`) VALUES -('ACH001', 'learning_time_l1', '初学者', 'v1-icon.svg', '累计学习时长达到10小时', 1, 1, 1, 10*60*60, 10, 1, 0), -('ACH002', 'learning_time_l2', '勤学者', 'v2-icon.svg', '累计学习时长达到50小时', 1, 2, 1, 50*60*60, 50, 2, 0), -('ACH003', 'learning_time_l3', '学习达人', 'v3-icon.svg', '累计学习时长达到100小时', 1, 3, 1, 100*60*60, 100, 3, 0), -('ACH004', 'learning_time_l4', '学习狂人', 'v4-icon.svg', '累计学习时长达到500小时', 1, 4, 1, 500*60*60, 500, 4, 0), -('ACH005', 'learning_time_l5', '学习大师', 'v5-icon.svg', '累计学习时长达到1000小时', 1, 5, 1, 1000*60*60, 1000, 5, 0), -('ACH006', 'learning_time_l6', '学习宗师', 'v6-icon.svg', '累计学习时长达到2000小时', 1, 6, 1, 2000*60*60, 2000, 6, 0); - -- -- ============================================= -- -- 课程完成类成就 -- -- ============================================= diff --git a/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql index 7bdd6e0..f993e78 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableCrontab.sql @@ -56,4 +56,4 @@ CREATE TABLE `tb_crontab_log` ( KEY `idx_execute_status` (`execute_status`), KEY `idx_start_time` (`start_time`), KEY `idx_deleted` (`deleted`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='定时任务执行日志表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='定时任务执行日志表'; diff --git a/schoolNewsServ/.bin/mysql/sql/createTablePermission.sql b/schoolNewsServ/.bin/mysql/sql/createTablePermission.sql index 5b0b14b..9b2e0bf 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTablePermission.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTablePermission.sql @@ -6,6 +6,7 @@ CREATE TABLE `tb_sys_dept` ( `dept_id` VARCHAR(50) NOT NULL COMMENT '部门ID', `name` VARCHAR(100) NOT NULL COMMENT '部门名称', `parent_id` VARCHAR(50) DEFAULT NULL COMMENT '父部门ID', + `dept_path` VARCHAR(500) DEFAULT NULL COMMENT '部门路径,格式:/root_department/dept_001/,用于快速判断父子关系', `description` VARCHAR(255) DEFAULT NULL COMMENT '部门描述', `creator` VARCHAR(50) DEFAULT NULL COMMENT '创建者', `updater` VARCHAR(50) DEFAULT NULL COMMENT '更新者', @@ -15,8 +16,9 @@ CREATE TABLE `tb_sys_dept` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_dept_id` (`dept_id`), - KEY `idx_dept_parent` (`parent_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; + KEY `idx_dept_parent` (`parent_id`), + KEY `idx_dept_path` (`dept_path`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='部门表'; -- 角色表 @@ -34,7 +36,7 @@ CREATE TABLE `tb_sys_role` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_role_id` (`role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 部门-角色关联 @@ -51,7 +53,7 @@ CREATE TABLE `tb_sys_dept_role` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_dept_role` (`dept_id`, `role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 用户-角色关联 @@ -69,7 +71,7 @@ CREATE TABLE `tb_sys_user_dept_role` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_user_dept_role` (`user_id`, `dept_id`, `role_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 模块表 @@ -92,7 +94,7 @@ CREATE TABLE `tb_sys_module` ( PRIMARY KEY (`id`), UNIQUE KEY `uk_module_id` (`module_id`), UNIQUE KEY `uk_module_code` (`code`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 权限表 DROP TABLE IF EXISTS `tb_sys_permission`; @@ -112,7 +114,7 @@ CREATE TABLE `tb_sys_permission` ( PRIMARY KEY (`id`), UNIQUE KEY `uk_permission_id` (`permission_id`), KEY `idx_permission_module` (`module_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 角色-权限关联 @@ -129,7 +131,7 @@ CREATE TABLE `tb_sys_role_permission` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_role_permission` (`role_id`, `permission_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 菜单表 @@ -154,7 +156,7 @@ CREATE TABLE `tb_sys_menu` ( PRIMARY KEY (`id`), UNIQUE KEY `uk_menu_id` (`menu_id`), KEY `idx_menu_parent` (`parent_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; DROP TABLE IF EXISTS `tb_sys_menu_permission`; @@ -170,4 +172,4 @@ CREATE TABLE `tb_sys_menu_permission` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_menu_permission` (`menu_id`, `permission_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; \ No newline at end of file +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/schoolNewsServ/.bin/mysql/sql/createTablePermissionControl.sql b/schoolNewsServ/.bin/mysql/sql/createTablePermissionControl.sql index 7d5d343..506125e 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTablePermissionControl.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTablePermissionControl.sql @@ -3,7 +3,7 @@ use school_news; DROP TABLE IF EXISTS `tb_resource_permission`; CREATE TABLE `tb_resource_permission` ( `id` VARCHAR(50) NOT NULL COMMENT '权限ID', - `resource_type` INT(4) NOT NULL COMMENT '资源类型(1资源/新闻 2课程 3课程章节 4学习任务)', + `resource_type` INT(4) NOT NULL COMMENT '资源类型(1新闻 2课程 3学习任务 4部门 5角色 6成就 7定时任务 8轮播图 9标签)', `resource_id` VARCHAR(50) NOT NULL COMMENT '资源ID', `dept_id` VARCHAR(50) DEFAULT NULL COMMENT '部门ID(NULL表示不限制部门)', `role_id` VARCHAR(50) DEFAULT NULL COMMENT '角色ID(NULL表示不限制角色)', @@ -25,30 +25,45 @@ CREATE TABLE `tb_resource_permission` ( ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='统一资源权限控制表'; -- 说明: --- 1. resource_type 资源类型说明: --- 1 - 资源/新闻 --- 2 - 课程 --- 3 - 课程章节 --- 4 - 学习任务 --- 可根据实际业务扩展 +-- 1. resource_type 资源类型说明(对应ResourceType枚举): +-- 1 - NEWS (新闻/资源) +-- 2 - COURSE (课程) +-- 3 - TASK (学习任务) +-- 4 - DEPT (部门) +-- 5 - ROLE (角色) +-- 6 - ACHIEVEMENT (成就) +-- 7 - CRONTAB_TASK (定时任务) +-- 8 - BANNER (轮播图) +-- 9 - TAG (标签) +-- 注意:这些值必须与 common-core/enums/ResourceType.java 中的枚举定义完全一致 -- -- 2. dept_id 和 role_id 组合使用: --- - 都为NULL:不限制,所有人可访问 +-- - 都为NULL:超级管理员权限,所有人可访问 -- - dept_id有值,role_id为NULL:该部门所有人可访问 --- - dept_id为NULL,role_id有值:该角色所有人可访问 --- - 都有值:该部门的该角色可访问 +-- - dept_id为NULL,role_id有值:该角色所有人可访问(跨部门) +-- - 都有值:该部门的该角色可访问(精确控制) -- -- 3. 权限说明: --- - can_read:查看权限(浏览、阅读) +-- - can_read:查看权限(浏览、阅读、查询) -- - can_write:编辑权限(修改、删除) --- - can_execute:执行权限(发布、审核等操作) +-- - can_execute:执行权限(发布、审核、执行等高级操作) -- --- 4. 查询示例: +-- 4. 权限创建逻辑: +-- - root_department的superadmin创建资源时:为所有部门和角色创建权限 +-- - 普通用户创建资源时:为父部门管理员+子部门所有角色创建权限 +-- - 始终为root_department的superadmin创建全权限记录 +-- +-- 5. 查询示例: -- 查询用户对某资源的权限: -- SELECT * FROM tb_resource_permission -- WHERE resource_type = 1 -- AND resource_id = 'xxx' --- AND (dept_id IS NULL OR dept_id = '用户部门') --- AND (role_id IS NULL OR role_id IN ('用户角色列表')) +-- AND ( +-- (dept_id IS NULL AND role_id IS NULL) -- 超级权限 +-- OR (dept_id = '用户部门' AND role_id IS NULL) -- 部门权限 +-- OR (role_id = '用户角色' AND dept_id IS NULL) -- 角色权限 +-- OR (dept_id = '用户部门' AND role_id = '用户角色') -- 精确权限 +-- ) +-- AND can_read = 1 -- AND deleted = 0; diff --git a/schoolNewsServ/.bin/mysql/sql/createTableSystem.sql b/schoolNewsServ/.bin/mysql/sql/createTableSystem.sql index 4c0b361..4799c45 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableSystem.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableSystem.sql @@ -117,7 +117,7 @@ CREATE TABLE IF NOT EXISTS `tb_sys_file` ( INDEX `idx_storage_type` (`storage_type`), INDEX `idx_deleted` (`deleted`), INDEX `idx_create_time` (`create_time`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='文件上传记录表'; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='文件上传记录表'; diff --git a/schoolNewsServ/.bin/mysql/sql/createTableUser.sql b/schoolNewsServ/.bin/mysql/sql/createTableUser.sql index 003781d..374f8bd 100644 --- a/schoolNewsServ/.bin/mysql/sql/createTableUser.sql +++ b/schoolNewsServ/.bin/mysql/sql/createTableUser.sql @@ -17,7 +17,7 @@ CREATE TABLE `tb_sys_user` ( UNIQUE KEY `uk_user_username` (`username`), UNIQUE KEY `uk_user_email` (`email`), KEY `idx_user_phone` (`phone`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; -- 推荐:把默认 admin 密码替换为已哈希的值 @@ -40,7 +40,7 @@ CREATE TABLE `tb_sys_user_info` ( `deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除', PRIMARY KEY (`id`), UNIQUE KEY `uk_user_info_user_id` (`user_id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; DROP TABLE IF EXISTS `tb_sys_login_log`; @@ -61,5 +61,5 @@ CREATE TABLE `tb_sys_login_log` ( PRIMARY KEY (`id`), index `idx_user_id` (`user_id`) USING BTREE, index `idx_login_time` (`login_time`) USING BTREE -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci; +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; diff --git a/schoolNewsServ/.bin/mysql/sql/initAllData.sql b/schoolNewsServ/.bin/mysql/sql/initAllData.sql index e32d1c7..f0db3e9 100644 --- a/schoolNewsServ/.bin/mysql/sql/initAllData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initAllData.sql @@ -45,11 +45,72 @@ INSERT INTO `tb_sys_config` (id, config_key, config_value, config_type, config_g ('12', 'system.resource.auto_publish_time', '08:00', 'string', 'resource', '自动发布时间', 0, '1', now()), ('13', 'system.ai.enabled', 'true', 'boolean', 'ai', '是否启用智能体', 0, '1', now()); +-- 注意:默认superadmin用户已在 initMenuData.sql 中创建,此处无需重复创建 --- 插入默认用户数据 -INSERT INTO `tb_sys_user` (id, username, password, email, status) VALUES -('1', 'superadmin', '$2a$10$/Bo2SXboVUpYfR6EA.y8puYQaMGBcuNYFY/EkQRY3w27IH56EuEcS', '3223905473@qq.com', 0); +-- ===================================================== +-- 初始化资源权限数据 +-- ===================================================== --- 插入默认用户信息数据 -INSERT INTO `tb_sys_user_info` (id, user_id, full_name, avatar) VALUES -('1', '1', '管理员', 'default'); + +INSERT INTO `tb_achievement` (`id`, `achievement_id`, `name`,`icon`, `description`, `type`, `level`, `condition_type`, `condition_value`, `points`, `order_num`, `deleted`) VALUES +('ACH001', 'learning_time_l1', '初学者', 'v1-icon.svg', '累计学习时长达到10小时', 1, 1, 1, 10*60*60, 10, 1, 0), +('ACH002', 'learning_time_l2', '勤学者', 'v2-icon.svg', '累计学习时长达到50小时', 1, 2, 1, 50*60*60, 50, 2, 0), +('ACH003', 'learning_time_l3', '学习达人', 'v3-icon.svg', '累计学习时长达到100小时', 1, 3, 1, 100*60*60, 100, 3, 0), +('ACH004', 'learning_time_l4', '学习狂人', 'v4-icon.svg', '累计学习时长达到500小时', 1, 4, 1, 500*60*60, 500, 4, 0), +('ACH005', 'learning_time_l5', '学习大师', 'v5-icon.svg', '累计学习时长达到1000小时', 1, 5, 1, 1000*60*60, 1000, 5, 0), +('ACH006', 'learning_time_l6', '学习宗师', 'v6-icon.svg', '累计学习时长达到2000小时', 1, 6, 1, 2000*60*60, 2000, 6, 0); + +INSERT INTO `tb_resource_permission` (id, resource_type, resource_id, dept_id, role_id, can_read, can_write, can_execute, creator, create_time) VALUES +('perm_achievement_001', 6, 'learning_time_l1', NULL, NULL, 1, 0, 0, '1', now()), +('perm_achievement_002', 6, 'learning_time_l2', NULL, NULL, 1, 0, 0, '1', now()), +('perm_achievement_003', 6, 'learning_time_l3', NULL, NULL, 1, 0, 0, '1', now()), +('perm_achievement_004', 6, 'learning_time_l4', NULL, NULL, 1, 0, 0, '1', now()), +('perm_achievement_005', 6, 'learning_time_l5', NULL, NULL, 1, 0, 0, '1', now()), +('perm_achievement_006', 6, 'learning_time_l6', NULL, NULL, 1, 0, 0, '1', now()); + +-- 为默认标签创建超级管理员权限(文章标签) +INSERT INTO `tb_resource_permission` (id, resource_type, resource_id, dept_id, role_id, can_read, can_write, can_execute, creator, create_time) VALUES +-- 文章标签权限(resource_type=9,TAG) +('perm_tag_001', 9, 'tag_article_001', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_002', 9, 'tag_article_002', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_003', 9, 'tag_article_003', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_004', 9, 'tag_article_004', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_005', 9, 'tag_article_005', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_006', 9, 'tag_article_006', NULL, NULL, 1, 1, 1, '1', now()), + +-- 课程标签权限(resource_type=9,TAG) +('perm_tag_101', 9, 'tag_course_001', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_102', 9, 'tag_course_002', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_103', 9, 'tag_course_003', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_104', 9, 'tag_course_004', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_105', 9, 'tag_course_005', NULL, NULL, 1, 1, 1, '1', now()), + +-- 学习任务标签权限(resource_type=9,TAG) +('perm_tag_201', 9, 'tag_task_001', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_202', 9, 'tag_task_002', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_203', 9, 'tag_task_003', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_204', 9, 'tag_task_004', NULL, NULL, 1, 1, 1, '1', now()), +('perm_tag_205', 9, 'tag_task_005', NULL, NULL, 1, 1, 1, '1', now()); + +-- 为默认部门创建权限(resource_type=4,DEPT) +INSERT INTO `tb_resource_permission` (id, resource_type, resource_id, dept_id, role_id, can_read, can_write, can_execute, creator, create_time) VALUES +('perm_dept_001', 4, 'root_department', NULL, NULL, 1, 1, 1, '1', now()), +('perm_dept_002', 4, 'default_department', NULL, NULL, 1, 1, 1, '1', now()); + +-- 为默认角色创建权限(resource_type=5,ROLE) +INSERT INTO `tb_resource_permission` (id, resource_type, resource_id, dept_id, role_id, can_read, can_write, can_execute, creator, create_time) VALUES +('perm_role_001', 5, 'superadmin', NULL, NULL, 1, 1, 1, '1', now()), +('perm_role_002', 5, 'admin', NULL, NULL, 1, 1, 1, '1', now()), +('perm_role_003', 5, 'freedom', NULL, NULL, 1, 1, 1, '1', now()); + + +-- 说明: +-- 1. 这些初始权限都是超级管理员权限(dept_id和role_id都为NULL) +-- 2. 所有权限都是全权限(can_read=1, can_write=1, can_execute=1) +-- 3. 后续创建的资源会自动根据创建者的部门和角色创建相应的权限 +-- 4. resource_type说明: +-- 1-NEWS, 2-COURSE, 3-TASK, 4-DEPT, 5-ROLE, 6-ACHIEVEMENT, 7-CRONTAB_TASK, 8-BANNER, 9-TAG +-- 5. 权限创建规则: +-- - root_department的superadmin创建资源:为所有部门和角色创建权限 +-- - 普通用户创建资源:为父部门管理员+子部门角色创建权限 +-- - 所有资源都会为root_department的superadmin创建全权限 diff --git a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql index 12ac2fb..74fd8bc 100644 --- a/schoolNewsServ/.bin/mysql/sql/initMenuData.sql +++ b/schoolNewsServ/.bin/mysql/sql/initMenuData.sql @@ -1,13 +1,26 @@ use school_news; --- 插入部门数据 -INSERT INTO `tb_sys_dept` (id,dept_id,name, description) VALUES ('1','root_department', '超级部门', '系统超级部门'); -INSERT INTO `tb_sys_dept` (id,dept_id,name, parent_id, description) VALUES ('2','default_department', '默认部门', 'root_department', '系统默认创建的部门'); +-- 插入默认超级管理员用户(必须最先创建,因为后续数据的creator都需要引用此用户) +INSERT INTO `tb_sys_user` (id, username, password, email, status, create_time) VALUES +('1', 'superadmin', '$2a$10$/Bo2SXboVUpYfR6EA.y8puYQaMGBcuNYFY/EkQRY3w27IH56EuEcS', 'superadmin@example.com', 1, now()); + +-- 插入默认用户信息数据 +INSERT INTO `tb_sys_user_info` (id, user_id, full_name, avatar, create_time) VALUES +('1', '1', '超级管理员', 'default', now()); + +-- 插入部门数据(包含dept_path字段) +INSERT INTO `tb_sys_dept` (id, dept_id, name, parent_id, dept_path, description, creator, create_time) VALUES +('1', 'root_department', '超级部门', NULL, '/root_department/', '系统超级部门', '1', now()); +INSERT INTO `tb_sys_dept` (id, dept_id, name, parent_id, dept_path, description, creator, create_time) VALUES +('2', 'default_department', '默认部门', 'root_department', '/root_department/default_department/', '系统默认创建的部门', '1', now()); -- 插入角色数据 -INSERT INTO `tb_sys_role` (id,role_id, name, description) VALUES ('1','superadmin', '超级管理员', '超级管理员角色'); -INSERT INTO `tb_sys_role` (id,role_id, name, description) VALUES ('2','admin', '管理员', '管理员角色'); -INSERT INTO `tb_sys_role` (id,role_id, name, description) VALUES ('3','freedom', '自由角色', '自由角色'); +INSERT INTO `tb_sys_role` (id,role_id, name, description, creator, create_time) VALUES +('1','superadmin', '超级管理员', '超级管理员角色', '1', now()); +INSERT INTO `tb_sys_role` (id,role_id, name, description, creator, create_time) VALUES +('2','admin', '管理员', '管理员角色', '1', now()); +INSERT INTO `tb_sys_role` (id,role_id, name, description, creator, create_time) VALUES +('3','freedom', '自由角色', '自由角色', '1', now()); -- 插入部门-角色关联数据 INSERT INTO `tb_sys_dept_role` (id, dept_id, role_id, creator, create_time) VALUES @@ -15,8 +28,9 @@ INSERT INTO `tb_sys_dept_role` (id, dept_id, role_id, creator, create_time) VALU ('2', 'default_department', 'admin', '1', now()), ('3', 'default_department', 'freedom', '1', now()); --- 插入用户-角色关联数据 -INSERT INTO `tb_sys_user_dept_role` (id, user_id, dept_id, role_id, creator, create_time) VALUES ('1', '1', 'root_department', 'superadmin', '1', now()); +-- 插入用户-部门-角色关联数据(root_department的superadmin) +INSERT INTO `tb_sys_user_dept_role` (id, user_id, dept_id, role_id, creator, create_time) VALUES +('1', '1', 'root_department', 'superadmin', '1', now()); -- 插入模块数据 INSERT INTO `tb_sys_module` (id, module_id, name, code, description, icon, order_num, status, creator, create_time) VALUES @@ -72,87 +86,62 @@ INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, creat ('18', 'admin', 'perm_crontab_execute', '1', now()); -- 插入前端菜单数据 -INSERT INTO `tb_sys_menu` (id, menu_id, name, parent_id, url, component, icon, order_num, type, layout, creator, create_time) VALUES -('100', 'menu_home', '首页', NULL, '/home', 'user/home/HomeView', 'el-icon-house', 1, 1, 'NavigationLayout', '1', now()), --- 资源中心 -('200', 'menu_resource_center', '资源中心', NULL, '/resource-center', 'user/resource-center/ResourceCenterView', 'el-icon-folder-opened', 2, 1, 'NavigationLayout', '1', now()), --- 学习计划 -('300', 'menu_study_plan', '学习计划', NULL, '/study-plan', 'user/study-plan/StudyPlanView', 'el-icon-reading', 3, 1, 'NavigationLayout', '1', now()), -('301', 'menu_study_tasks', '学习任务', 'menu_study_plan', '/study-plan/tasks', 'user/study-plan/StudyTasksView', 'el-icon-s-order', 1, 1, 'NavigationLayout', '1', now()), -('302', 'menu_course_center', '课程中心', 'menu_study_plan', '/study-plan/course', 'user/study-plan/CourseCenterView', 'el-icon-video-play', 2, 1, 'NavigationLayout', '1', now()), -('303', 'menu_task_detail', '任务详情', 'menu_study_plan', '/study-plan/task-detail', 'user/study-plan/LearningTaskDetailView', 'el-icon-document', 3, 3, 'NavigationLayout', '1', now()), -('304', 'menu_course_detail', '课程详情', 'menu_study_plan', '/study-plan/course-detail', 'user/study-plan/CourseDetailView', 'el-icon-video-play', 4, 3, 'NavigationLayout', '1', now()), -('305', 'menu_course_study', '课程学习', 'menu_study_plan', '/study-plan/course-study', 'user/study-plan/CourseStudyView', 'el-icon-video-play', 5, 3, 'NavigationLayout', '1', now()), -('400', 'menu_user_dropdown', '用户下拉菜单', NULL, '', '', 'el-icon-user', 4, 0, 'NavigationLayout', '1', now()), --- 个人中心 -('401', 'menu_user_center', '个人中心', 'menu_user_dropdown', '/user-center', 'user/user-center/UserCenterView', 'el-icon-user', 4, 1, 'NavigationLayout', '1', now()), -('402', 'menu_learning_records', '学习记录', 'menu_user_center', '/user-center/learning-records', 'user/user-center/LearningRecordsView', 'el-icon-document', 1, 0, 'NavigationLayout', '1', now()), -('403', 'menu_my_favorites', '我的收藏', 'menu_user_center', '/user-center/favorites', 'user/user-center/MyFavoritesView', 'el-icon-star-on', 2, 0, 'NavigationLayout', '1', now()), -('404', 'menu_my_achievements', '我的成就', 'menu_user_center', '/user-center/achievements', 'user/user-center/MyAchievementsView', 'el-icon-trophy', 3, 0, 'NavigationLayout', '1', now()), - --- 账号中心 -('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/profile/ProfileView', 'el-icon-user-solid', 5, 1, 'NavigationLayout', '1', now()), -('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/profile/PersonalInfoView', 'el-icon-user', 1, 0, 'NavigationLayout', '1', now()), -('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/profile/AccountSettingsView', 'el-icon-setting', 2, 0, 'NavigationLayout', '1', now()), - --- 智能体模块 -('600', 'menu_ai_assistant', '智能体模块', NULL, '/ai-assistant', 'user/ai-assistant/AIAssistantView', 'el-icon-cpu', 6, 1, 'NavigationLayout', '1', now()); - --- 插入后端管理菜单数据 (type=0 侧边栏菜单) -INSERT INTO `tb_sys_menu` (id, menu_id, name, parent_id, url, component, icon, order_num, type, layout, creator, create_time) VALUES --- 系统总览 -('1000', 'menu_admin_overview', '系统总览', NULL, '/admin/overview', 'admin/overview/SystemOverviewView', 'el-icon-data-analysis', 1, 0, 'SidebarLayout', '1', now()), - --- 用户管理 -('2000', 'menu_sys_manage', '系统管理', NULL, '', '', 'el-icon-user', 2, 0, 'SidebarLayout', '1', now()), -('2001', 'menu_admin_user', '用户管理', 'menu_sys_manage', '/admin/manage/system/user', 'admin/manage/system/UserManageView', 'el-icon-user', 1, 0, 'SidebarLayout', '1', now()), -('2002', 'menu_admin_dept', '部门管理', 'menu_sys_manage', '/admin/manage/system/dept', 'admin/manage/system/DeptManageView', 'el-icon-office-building', 2, 0, 'SidebarLayout', '1', now()), -('2003', 'menu_admin_role', '角色管理', 'menu_sys_manage', '/admin/manage/system/role', 'admin/manage/system/RoleManageView', 'el-icon-user-solid', 3, 0, 'SidebarLayout', '1', now()), -('2005', 'menu_admin_menu', '菜单管理', 'menu_sys_manage', '/admin/manage/system/menu', 'admin/manage/system/MenuManageView', 'el-icon-menu', 4, 0, 'SidebarLayout', '1', now()), -('2006', 'menu_admin_module', '模块权限管理', 'menu_sys_manage', '/admin/manage/system/module-permission', 'admin/manage/system/ModulePermissionManageView', 'el-icon-s-grid', 5, 0, 'SidebarLayout', '1', now()), - --- 资源管理 -('3000', 'menu_admin_resource_manage', '资源管理', NULL, '', '', 'el-icon-folder', 3, 0, 'SidebarLayout', '1', now()), -('3001', 'menu_admin_resource', '资源管理', 'menu_admin_resource_manage', '/admin/manage/resource/resource', 'admin/manage/resource/ResourceManagementView', 'el-icon-folder', 1, 0, 'SidebarLayout', '1', now()), -('3002', 'menu_admin_article', '文章管理', 'menu_admin_resource_manage', '/admin/manage/resource/article', 'admin/manage/resource/ArticleManagementView', 'el-icon-document', 2, 0, 'SidebarLayout', '1', now()), -('3003', 'menu_admin_data_records', '数据记录', 'menu_admin_resource_manage', '/admin/manage/resource/data-records', 'admin/manage/resource/DataRecordsView', 'el-icon-data-line', 3, 0, 'SidebarLayout', '1', now()), - - --- 文章相关 -('3010', 'menu_article_add', '文章添加', 'menu_admin_article', '/article/add', 'public/article/ArticleAddView', 'el-icon-plus', 1, 3, 'SidebarLayout', '1', now()), -('3011', 'menu_article_show', '文章展示', 'menu_admin_article', '/article/show', 'public/article/ArticleShowView', 'el-icon-document', 2, 3, 'SidebarLayout', '1', now()), --- 运营管理 -('4000', 'menu_admin_content_manage', '运营管理', NULL, '', '', 'el-icon-s-operation', 4, 0, 'SidebarLayout', '1', now()), -('4001', 'menu_admin_banner', 'Banner管理', 'menu_admin_content_manage', '/admin/manage/content/banner', 'admin/manage/content/BannerManagementView', 'el-icon-picture', 1, 0, 'SidebarLayout', '1', now()), -('4002', 'menu_admin_tag', '标签管理', 'menu_admin_content_manage', '/admin/manage/content/tag', 'admin/manage/content/TagManagementView', 'el-icon-price-tag', 2, 0, 'SidebarLayout', '1', now()), -('4003', 'menu_admin_column', '栏目管理', 'menu_admin_content_manage', '/admin/manage/content/column', 'admin/manage/content/ColumnManagementView', 'el-icon-menu', 3, 0, 'SidebarLayout', '1', now()), -('4004', 'menu_admin_content', '内容管理', 'menu_admin_content_manage', '/admin/manage/content/content', 'admin/manage/content/ContentManagementView', 'el-icon-document', 4, 0, 'SidebarLayout', '1', now()), - --- 学习管理 -('5000', 'menu_admin_study_manage', '学习管理', NULL, '', '', 'el-icon-reading', 5, 0, 'SidebarLayout', '1', now()), -('5002', 'menu_admin_task_manage', '任务管理', 'menu_admin_study_manage', '/admin/manage/study/task-manage', 'admin/manage/study/TaskManageView', 'el-icon-s-order', 2, 0, 'SidebarLayout', '1', now()), -('5003', 'menu_admin_study_records', '学习记录', 'menu_admin_study_manage', '/admin/manage/study/study-records', 'admin/manage/study/StudyRecordsView', 'el-icon-document', 3, 0, 'SidebarLayout', '1', now()), -('5004', 'menu_admin_course_manage', '课程管理', 'menu_admin_study_manage', '/admin/manage/study/course', 'admin/manage/study/CourseManagementView', 'el-icon-video-play', 4, 0, 'SidebarLayout', '1', now()), -('5005', 'menu_admin_achievement_manage', '成就管理', 'menu_admin_study_manage', '/admin/manage/study/achievement', 'admin/manage/achievement/AchievementManagementView', 'el-icon-trophy', 5, 0, 'SidebarLayout', '1', now()), --- 智能体管理 -('6000', 'menu_admin_ai_manage', '智能体管理', NULL, '', '', 'el-icon-cpu', 6, 0, 'SidebarLayout', '1', now()), -('6001', 'menu_admin_ai', 'AI管理', 'menu_admin_ai_manage', '/admin/manage/ai/ai', 'admin/manage/ai/AIManagementView', 'el-icon-cpu', 1, 0, 'SidebarLayout', '1', now()), -('6002', 'menu_admin_ai_config', 'AI配置', 'menu_admin_ai_manage', '/admin/manage/ai/config', 'admin/manage/ai/AIConfigView', 'el-icon-setting', 2, 0, 'SidebarLayout', '1', now()), -('6003', 'menu_admin_knowledge', '知识库管理', 'menu_admin_ai_manage', '/admin/manage/ai/knowledge', 'admin/manage/ai/KnowledgeManagementView', 'el-icon-collection', 3, 0, 'SidebarLayout', '1', now()), - --- 系统日志 -('7000', 'menu_admin_logs_manage', '系统日志', NULL, '', '', 'el-icon-document', 7, 0, 'SidebarLayout', '1', now()), -('7001', 'menu_admin_system_logs', '系统日志', 'menu_admin_logs_manage', '/admin/manage/logs/system', 'admin/manage/logs/SystemLogsView', 'el-icon-document', 1, 0, 'SidebarLayout', '1', now()), -('7002', 'menu_admin_login_logs', '登录日志', 'menu_admin_logs_manage', '/admin/manage/logs/login', 'admin/manage/logs/LoginLogsView', 'el-icon-key', 2, 0, 'SidebarLayout', '1', now()), -('7003', 'menu_admin_operation_logs', '操作日志', 'menu_admin_logs_manage', '/admin/manage/logs/operation', 'admin/manage/logs/OperationLogsView', 'el-icon-s-operation', 3, 0, 'SidebarLayout', '1', now()), -('7004', 'menu_admin_system_config', '系统配置', 'menu_admin_logs_manage', '/admin/manage/logs/config', 'admin/manage/logs/SystemConfigView', 'el-icon-setting', 4, 0, 'SidebarLayout', '1', now()), - --- 定时任务管理 -('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', 'el-icon-alarm-clock', 8, 0, 'SidebarLayout', '1', now()), -('8001', 'menu_admin_crontab_task', '任务管理', 'menu_admin_crontab_manage', '/admin/manage/crontab/task', 'admin/manage/crontab/TaskManagementView', 'el-icon-s-order', 1, 0, 'SidebarLayout', '1', now()), -('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', 'el-icon-document', 2, 0, 'SidebarLayout', '1', now()), -('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', 'el-icon-share', 3, 0, 'SidebarLayout', '1', now()); - +INSERT INTO `tb_sys_menu` VALUES +-- 用户前端菜单 (100-699) +('100', 'menu_home', '首页', NULL, '/home', 'user/home/HomeView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('200', 'menu_resource_center', '资源中心', NULL, '/resource-center', 'user/resource-center/ResourceCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('300', 'menu_study_plan', '学习计划', NULL, '/study-plan', 'user/study-plan/StudyPlanView', NULL, 3, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('301', 'menu_study_tasks', '学习任务', 'menu_study_plan', '/study-plan/tasks', 'user/study-plan/StudyTasksView', NULL, 1, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('302', 'menu_course_center', '课程中心', 'menu_study_plan', '/study-plan/course', 'user/study-plan/CourseCenterView', NULL, 2, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('303', 'menu_task_detail', '任务详情', 'menu_study_plan', '/study-plan/task-detail', 'user/study-plan/LearningTaskDetailView', NULL, 3, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('304', 'menu_course_detail', '课程详情', 'menu_study_plan', '/study-plan/course-detail', 'user/study-plan/CourseDetailView', NULL, 4, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('305', 'menu_course_study', '课程学习', 'menu_study_plan', '/study-plan/course-study', 'user/study-plan/CourseStudyView', NULL, 5, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('400', 'menu_user_dropdown', '用户下拉菜单', NULL, '', '', NULL, 4, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('401', 'menu_user_center', '个人中心', 'menu_user_dropdown', '/user-center', 'user/user-center/UserCenterView', NULL, 4, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('402', 'menu_learning_records', '学习记录', 'menu_user_center', '/user-center/learning-records', 'user/user-center/LearningRecordsView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('403', 'menu_my_favorites', '我的收藏', 'menu_user_center', '/user-center/favorites', 'user/user-center/MyFavoritesView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('404', 'menu_my_achievements', '我的成就', 'menu_user_center', '/user-center/achievements', 'user/user-center/MyAchievementsView', NULL, 3, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('500', 'menu_profile', '账号中心', 'menu_user_dropdown', '/profile', 'user/profile/ProfileView', NULL, 5, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('501', 'menu_personal_info', '个人信息', 'menu_profile', '/profile/personal-info', 'user/profile/PersonalInfoView', NULL, 1, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('502', 'menu_account_settings', '账号设置', 'menu_profile', '/profile/account-settings', 'user/profile/AccountSettingsView', NULL, 2, 0, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('600', 'menu_ai_assistant', '智能体模块', NULL, '/ai-assistant', 'user/ai-assistant/AIAssistantView', NULL, 6, 1, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +-- 管理后台菜单 (1000-8999) +('1000', 'menu_admin_overview', '系统总览', NULL, '/admin/overview', 'admin/overview/SystemOverviewView', 'admin/overview.svg', 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:32', NULL, 0), +('2000', 'menu_sys_manage', '系统管理', NULL, '', '', 'admin/settings.svg', 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:35', NULL, 0), +('2001', 'menu_admin_user', '用户管理', 'menu_sys_manage', '/admin/manage/system/user', 'admin/manage/system/UserManageView', 'admin/usermange.svg', 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:38', NULL, 0), +('2002', 'menu_admin_dept', '部门管理', 'menu_sys_manage', '/admin/manage/system/dept', 'admin/manage/system/DeptManageView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('2003', 'menu_admin_role', '角色管理', 'menu_sys_manage', '/admin/manage/system/role', 'admin/manage/system/RoleManageView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('2005', 'menu_admin_menu', '菜单管理', 'menu_sys_manage', '/admin/manage/system/menu', 'admin/manage/system/MenuManageView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('2006', 'menu_admin_module', '模块权限管理', 'menu_sys_manage', '/admin/manage/system/module-permission', 'admin/manage/system/ModulePermissionManageView', NULL, 5, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('3000', 'menu_admin_resource_manage', '资源管理', NULL, '', '', 'admin/resource.svg', 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:40', NULL, 0), +('3001', 'menu_admin_resource', '数据采集', 'menu_admin_resource_manage', '/admin/manage/resource/resource', 'admin/manage/resource/ResourceManagementView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('3002', 'menu_admin_article', '文章管理', 'menu_admin_resource_manage', '/admin/manage/resource/article', 'admin/manage/resource/ArticleManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('3003', 'menu_admin_data_records', '数据记录', 'menu_admin_resource_manage', '/admin/manage/resource/data-records', 'admin/manage/resource/DataRecordsView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('3010', 'menu_article_add', '文章添加', 'menu_admin_article', '/article/add', 'public/article/ArticleAddView', NULL, 1, 3, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('3011', 'menu_article_show', '文章展示', 'menu_admin_article', '/article/show', 'public/article/ArticleShowView', NULL, 2, 3, 'NavigationLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('4000', 'menu_admin_content_manage', '运营管理', NULL, '', '', 'admin/maintain.svg', 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:42', NULL, 0), +('4001', 'menu_admin_banner', 'Banner管理', 'menu_admin_content_manage', '/admin/manage/content/banner', 'admin/manage/content/BannerManagementView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('4002', 'menu_admin_tag', '标签管理', 'menu_admin_content_manage', '/admin/manage/content/tag', 'admin/manage/content/TagManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('4003', 'menu_admin_column', '栏目管理', 'menu_admin_content_manage', '/admin/manage/content/column', 'admin/manage/content/ColumnManagementView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('4004', 'menu_admin_content', '内容管理', 'menu_admin_content_manage', '/admin/manage/content/content', 'admin/manage/content/ContentManagementView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:49:56', NULL, 0), +('5000', 'menu_admin_study_manage', '学习管理', NULL, '', '', 'admin/study.svg', 5, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:46', NULL, 0), +('5002', 'menu_admin_task_manage', '任务管理', 'menu_admin_study_manage', '/admin/manage/study/task-manage', 'admin/manage/study/TaskManageView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('5003', 'menu_admin_study_records', '学习记录', 'menu_admin_study_manage', '/admin/manage/study/study-records', 'admin/manage/study/StudyRecordsView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('5004', 'menu_admin_course_manage', '课程管理', 'menu_admin_study_manage', '/admin/manage/study/course', 'admin/manage/study/CourseManagementView', 'admin/course.svg', 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:48', NULL, 0), +('5005', 'menu_admin_achievement_manage', '成就管理', 'menu_admin_study_manage', '/admin/manage/study/achievement', 'admin/manage/achievement/AchievementManagementView', NULL, 5, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('6000', 'menu_admin_ai_manage', '智能体管理', NULL, '', '', 'admin/agent.svg', 6, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:50', NULL, 0), +('6001', 'menu_admin_ai', 'AI管理', 'menu_admin_ai_manage', '/admin/manage/ai/ai', 'admin/manage/ai/AIManagementView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('6002', 'menu_admin_ai_config', 'AI配置', 'menu_admin_ai_manage', '/admin/manage/ai/config', 'admin/manage/ai/AIConfigView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('6003', 'menu_admin_knowledge', '知识库管理', 'menu_admin_ai_manage', '/admin/manage/ai/knowledge', 'admin/manage/ai/KnowledgeManagementView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('7000', 'menu_admin_logs_manage', '系统日志', NULL, '', '', 'admin/logs.svg', 7, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:52:53', NULL, 0), +('7001', 'menu_admin_system_logs', '系统日志', 'menu_admin_logs_manage', '/admin/manage/logs/system', 'admin/manage/logs/SystemLogsView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('7002', 'menu_admin_login_logs', '登录日志', 'menu_admin_logs_manage', '/admin/manage/logs/login', 'admin/manage/logs/LoginLogsView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('7003', 'menu_admin_operation_logs', '操作日志', 'menu_admin_logs_manage', '/admin/manage/logs/operation', 'admin/manage/logs/OperationLogsView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('7004', 'menu_admin_system_config', '系统配置', 'menu_admin_logs_manage', '/admin/manage/logs/config', 'admin/manage/logs/SystemConfigView', NULL, 4, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('8000', 'menu_admin_crontab_manage', '定时任务管理', NULL, '', '', NULL, 8, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('8001', 'menu_admin_crontab_task', '任务管理', 'menu_admin_crontab_manage', '/admin/manage/crontab/task', 'admin/manage/crontab/TaskManagementView', NULL, 1, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('8002', 'menu_admin_crontab_log', '执行日志', 'menu_admin_crontab_manage', '/admin/manage/crontab/log', 'admin/manage/crontab/LogManagementView', NULL, 2, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0), +('8003', 'menu_admin_news_crawler', '新闻爬虫配置', 'menu_admin_crontab_manage', '/admin/manage/crontab/news-crawler', 'admin/manage/crontab/NewsCrawlerView', NULL, 3, 0, 'SidebarLayout', '1', NULL, '2025-10-27 17:26:06', '2025-10-29 11:48:39', NULL, 0); -- 插入菜单权限关联数据 INSERT INTO `tb_sys_menu_permission` (id, permission_id, menu_id, creator, create_time) VALUES -- 前端菜单权限关联 diff --git a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/mapper/AchievementMapper.java b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/mapper/AchievementMapper.java index 3b6d76f..2f83739 100644 --- a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/mapper/AchievementMapper.java +++ b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/mapper/AchievementMapper.java @@ -5,6 +5,8 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.usercenter.TbAchievement; +import org.xyzh.common.vo.AchievementVO; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -21,9 +23,10 @@ public interface AchievementMapper extends BaseMapper { /** * @description 查询成就列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 成就列表 */ - List selectAchievements(@Param("filter") TbAchievement filter); + List selectAchievements(@Param("filter") TbAchievement filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据成就ID查询成就信息 @@ -108,15 +111,30 @@ public interface AchievementMapper extends BaseMapper { * @description 分页查询成就 * @param filter 过滤条件 * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 * @return List 成就列表 */ - List selectAchievementsPage(@Param("filter") TbAchievement filter, @Param("pageParam") PageParam pageParam); + List selectAchievementsPage(@Param("filter") TbAchievement filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 统计成就总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return long 总数 */ - long countAchievements(@Param("filter") TbAchievement filter); + long countAchievements(@Param("filter") TbAchievement filter, @Param("userDeptRoles") List userDeptRoles); + + /** + * @description 联表查询用户成就列表(包含进度信息)- 带权限过滤 + * @param userId 用户ID + * @param type 成就类型(可选) + * @param userDeptRoles 用户部门角色列表 + * @return List 用户成就列表(包含进度) + */ + List selectUserAchievementsWithProgress( + @Param("userId") String userId, + @Param("type") Integer type, + @Param("userDeptRoles") List userDeptRoles + ); } diff --git a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java index 13da2cc..dbedfdf 100644 --- a/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java +++ b/schoolNewsServ/achievement/src/main/java/org/xyzh/achievement/service/impl/ACHAchievementServiceImpl.java @@ -23,6 +23,9 @@ import org.xyzh.common.dto.usercenter.TbUserAchievementProgress; import org.xyzh.common.utils.IDUtils; import org.xyzh.common.vo.AchievementVO; import org.xyzh.system.utils.LoginUtil; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; import java.util.*; import java.util.stream.Collectors; @@ -51,6 +54,9 @@ public class ACHAchievementServiceImpl implements AchievementService { @Autowired private List checkers; + @Autowired + private ResourcePermissionService resourcePermissionService; + // ==================== 成就定义管理 ==================== @Override @@ -84,6 +90,21 @@ public class ACHAchievementServiceImpl implements AchievementService { // 插入数据库 int result = achievementMapper.insertAchievement(achievement); if (result > 0) { + // 创建成就资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.ACHIEVEMENT.getCode(), + achievement.getAchievementID(), + userDeptRoles.get(0) + ); + logger.info("创建成就权限成功: {}", achievement.getAchievementID()); + } + } catch (Exception e) { + logger.error("创建成就权限异常,但不影响成就创建: {}", e.getMessage(), e); + } resultDomain.success("创建成就成功", achievement); } else { resultDomain.fail("创建成就失败"); @@ -176,7 +197,9 @@ public class ACHAchievementServiceImpl implements AchievementService { public ResultDomain getAllAchievements(TbAchievement filter) { ResultDomain resultDomain = new ResultDomain<>(); try { - List list = achievementMapper.selectAchievements(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = achievementMapper.selectAchievements(filter, userDeptRoles); resultDomain.success("获取成就列表成功", list); return resultDomain; @@ -196,8 +219,10 @@ public class ACHAchievementServiceImpl implements AchievementService { filter.setDeleted(false); } - List list = achievementMapper.selectAchievementsPage(filter, pageParam); - long total = achievementMapper.countAchievements(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = achievementMapper.selectAchievementsPage(filter, pageParam, userDeptRoles); + long total = achievementMapper.countAchievements(filter, userDeptRoles); pageParam.setTotalElements(total); pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); @@ -286,87 +311,13 @@ public class ACHAchievementServiceImpl implements AchievementService { String userID = user.getID(); - // 1. 获取所有成就列表(根据type过滤) - List allAchievements; - if (type != null) { - allAchievements = achievementMapper.selectByType(type); - } else { - TbAchievement filter = new TbAchievement(); - filter.setDeleted(false); - allAchievements = achievementMapper.selectAchievements(filter); - } - - // 2. 获取用户已获得的成就列表 - List userAchievements = type != null - ? userAchievementMapper.selectByUserIdAndType(userID, type) - : userAchievementMapper.selectByUserId(userID); - - // 转换为Map,key为achievementID - Map userAchievementMap = userAchievements.stream() - .collect(Collectors.toMap(TbUserAchievement::getAchievementID, ua -> ua)); - - // 3. 获取用户的成就进度列表 - List progressList = progressMapper.selectByUserId(userID); - - // 转换为Map,key为achievementID - Map progressMap = progressList.stream() - .collect(Collectors.toMap(TbUserAchievementProgress::getAchievementID, p -> p)); - - // 4. 组装AchievementVO列表 - List achievementVOList = new ArrayList<>(); - for (TbAchievement achievement : allAchievements) { - AchievementVO vo = new AchievementVO(); - - // 复制成就基本信息 - vo.setID(achievement.getID()); - vo.setAchievementID(achievement.getAchievementID()); - vo.setName(achievement.getName()); - vo.setDescription(achievement.getDescription()); - vo.setType(achievement.getType()); - vo.setLevel(achievement.getLevel()); - vo.setIcon(achievement.getIcon()); - vo.setPoints(achievement.getPoints()); - vo.setConditionType(achievement.getConditionType()); - vo.setConditionValue(achievement.getConditionValue()); - vo.setOrderNum(achievement.getOrderNum()); - vo.setCreator(achievement.getCreator()); - vo.setUpdater(achievement.getUpdater()); - vo.setCreateTime(achievement.getCreateTime()); - vo.setUpdateTime(achievement.getUpdateTime()); - vo.setDeleted(achievement.getDeleted()); - - // 设置用户ID - vo.setUserID(userID); - - // 填充用户成就信息(如果已获得) - TbUserAchievement userAchievement = userAchievementMap.get(achievement.getAchievementID()); - if (userAchievement != null) { - vo.setUserAchievementID(userAchievement.getID()); - vo.setObtainTime(userAchievement.getObtainTime()); - vo.setObtained(true); - } else { - vo.setObtained(false); - } - - // 填充进度信息 - TbUserAchievementProgress progress = progressMap.get(achievement.getAchievementID()); - if (progress != null) { - vo.setProgressID(progress.getID()); - vo.setCurrentValue(progress.getCurrentValue()); - vo.setTargetValue(progress.getTargetValue()); - vo.setProgressPercentage(progress.getProgressPercentage()); - vo.setCompleted(progress.getCompleted()); - vo.setLastUpdateTime(progress.getLastUpdateTime()); - } else { - // 没有进度记录,设置默认值 - vo.setCurrentValue(0); - vo.setTargetValue(achievement.getConditionValue()); - vo.setProgressPercentage(0); - vo.setCompleted(false); - } - - achievementVOList.add(vo); - } + // 使用联表查询一次性获取所有数据(包含成就、用户成就、进度信息及权限过滤) + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List achievementVOList = achievementMapper.selectUserAchievementsWithProgress( + userID, + type, + userDeptRoles + ); resultDomain.success("获取成就列表成功", achievementVOList); return resultDomain; @@ -652,7 +603,8 @@ public class ACHAchievementServiceImpl implements AchievementService { // 获取所有成就 TbAchievement filter = new TbAchievement(); filter.setDeleted(false); - List allAchievements = achievementMapper.selectAchievements(filter); + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List allAchievements = achievementMapper.selectAchievements(filter, userDeptRoles); // 获取用户进度 List progressList = progressMapper.selectIncompletedByUserId(userID); @@ -815,7 +767,8 @@ public class ACHAchievementServiceImpl implements AchievementService { // 获取所有成就 TbAchievement filter = new TbAchievement(); filter.setDeleted(false); - List allAchievements = achievementMapper.selectAchievements(filter); + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List allAchievements = achievementMapper.selectAchievements(filter, userDeptRoles); // 筛选支持该事件类型的成就 return allAchievements.stream() diff --git a/schoolNewsServ/achievement/src/main/resources/mapper/AchievementMapper.xml b/schoolNewsServ/achievement/src/main/resources/mapper/AchievementMapper.xml index 9d8fcd2..77b84f4 100644 --- a/schoolNewsServ/achievement/src/main/resources/mapper/AchievementMapper.xml +++ b/schoolNewsServ/achievement/src/main/resources/mapper/AchievementMapper.xml @@ -65,13 +65,57 @@ - + + + INNER JOIN tb_resource_permission rp ON a.achievement_id = rp.resource_id + AND rp.resource_type = 6 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + @@ -227,20 +271,133 @@ + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/schoolNewsServ/admin/src/main/resources/log4j2-spring.xml b/schoolNewsServ/admin/src/main/resources/log4j2-spring.xml index 404ecf5..f50b119 100644 --- a/schoolNewsServ/admin/src/main/resources/log4j2-spring.xml +++ b/schoolNewsServ/admin/src/main/resources/log4j2-spring.xml @@ -83,8 +83,8 @@ - - + + @@ -92,6 +92,17 @@ + + + + + + + + + + + @@ -129,6 +140,15 @@ + + + + + + + + + diff --git a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/dept/DepartmentService.java b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/dept/DepartmentService.java index 20a216f..2a7619a 100644 --- a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/dept/DepartmentService.java +++ b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/dept/DepartmentService.java @@ -6,6 +6,7 @@ import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.dept.TbSysDept; import org.xyzh.common.dto.dept.TbSysDeptRole; import org.xyzh.common.dto.role.TbSysRole; +import org.xyzh.common.vo.UserDeptRoleVO; /** * @description DepartmentService.java文件描述 部门服务接口 @@ -98,12 +99,12 @@ public interface DepartmentService { ResultDomain getDeptByRole(String deptId); /** - * @description 查询部门绑定角色 - * @return ResultDomain 角色信息 + * @description 查询部门绑定角色列表(包含名称) + * @return ResultDomain 部门角色信息 * @author yslg * @since 2025-10-06 */ - ResultDomain getDeptByRoleList(); + ResultDomain getDeptByRoleList(); /** * @description 绑定部门角色 diff --git a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/permission/ResourcePermissionService.java b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/permission/ResourcePermissionService.java new file mode 100644 index 0000000..f461a9a --- /dev/null +++ b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/permission/ResourcePermissionService.java @@ -0,0 +1,27 @@ +package org.xyzh.api.system.permission; + +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.permission.TbResourcePermission; +import org.xyzh.common.vo.UserDeptRoleVO; + +/** + * @description 资源权限控制服务接口 + * @filename ResourcePermissionService.java + * @author yslg + * @copyright xyzh + * @since 2025-10-29 + */ +public interface ResourcePermissionService { + + /** + * @description 创建资源权限 根据用户 + * @param resource_type 资源类型 + * @param resource_id 资源ID + * @param creatorID 创建者ID + * @return ResultDomain 资源权限 + * @author yslg + * @since 2025-10-29 + */ + ResultDomain createResourcePermission(Integer resource_type, String resource_id, UserDeptRoleVO userDeptRole); + +} diff --git a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/role/RoleService.java b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/role/RoleService.java index d438997..baa3ed6 100644 --- a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/role/RoleService.java +++ b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/role/RoleService.java @@ -4,7 +4,7 @@ import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.permission.TbSysPermission; import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.dto.role.TbSysRolePermission; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; /** @@ -72,11 +72,11 @@ public interface RoleService { /** * @description 根据用户ID查询部门角色列表 * @param userId 用户ID - * @return ResultDomain 角色列表 + * @return ResultDomain 角色列表 * @author yslg * @since 2025-09-28 */ - ResultDomain getDeptRolesByUserId(String userId); + ResultDomain getDeptRolesByUserId(String userId); /** * @description 检查角色名称是否存在 diff --git a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/user/UserService.java b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/user/UserService.java index 58fbf6b..783adfd 100644 --- a/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/user/UserService.java +++ b/schoolNewsServ/api/api-system/src/main/java/org/xyzh/api/system/user/UserService.java @@ -169,7 +169,7 @@ public interface UserService { * @author yslg * @since 2025-10-09 */ - ResultDomain getBindUserDeptRoleList(TbSysUserDeptRole filter); + ResultDomain getBindUserDeptRoleList(TbSysUserDeptRole filter); /** diff --git a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/config/SecurityConfig.java b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/config/SecurityConfig.java index ff5fb95..f3a682f 100644 --- a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/config/SecurityConfig.java +++ b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/config/SecurityConfig.java @@ -3,6 +3,7 @@ package org.xyzh.auth.config; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Lazy; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.config.http.SessionCreationPolicy; @@ -26,6 +27,7 @@ public class SecurityConfig { @Autowired private AuthProperties authProperties; + @Lazy @Autowired private JwtAuthenticationFilter jwtAuthenticationFilter; diff --git a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/service/LoginServiceImpl.java b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/service/LoginServiceImpl.java index 0879586..2f09cb4 100644 --- a/schoolNewsServ/auth/src/main/java/org/xyzh/auth/service/LoginServiceImpl.java +++ b/schoolNewsServ/auth/src/main/java/org/xyzh/auth/service/LoginServiceImpl.java @@ -25,7 +25,7 @@ import org.xyzh.api.system.role.RoleService; import org.xyzh.api.system.permission.PermissionService; import org.xyzh.common.redis.service.RedisService; import org.xyzh.api.system.menu.MenuService; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.Date; import java.util.List; @@ -206,9 +206,9 @@ public class LoginServiceImpl implements LoginService { loginDomain.setIpAddress(ipAddress); // 获取用户角色和权限(如果服务可用) try { - ResultDomain resultDomain = roleService.getDeptRolesByUserId(user.getID()); + ResultDomain resultDomain = roleService.getDeptRolesByUserId(user.getID()); if (resultDomain.isSuccess()) { - List roles = resultDomain.getDataList(); + List roles = resultDomain.getDataList(); loginDomain.setRoles(roles); } else { loginDomain.setRoles(new ArrayList<>()); diff --git a/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginDomain.java b/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginDomain.java index 4cf9e68..501966a 100644 --- a/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginDomain.java +++ b/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/domain/LoginDomain.java @@ -9,7 +9,7 @@ import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.dto.permission.TbSysPermission; import org.xyzh.common.dto.dept.TbSysDeptRole; import org.xyzh.common.dto.menu.TbSysMenu; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; /** * @description LoginDomain.java文件描述 登录域对象 * @filename LoginDomain.java @@ -40,7 +40,7 @@ public class LoginDomain implements Serializable { * @author yslg * @since 2025-09-28 */ - private List roles; + private List roles; /** * @description 用户权限列表 @@ -132,7 +132,7 @@ public class LoginDomain implements Serializable { * @author yslg * @since 2025-09-28 */ - public List getRoles() { + public List getRoles() { return roles; } @@ -141,7 +141,7 @@ public class LoginDomain implements Serializable { * @author yslg * @since 2025-09-28 */ - public void setRoles(List roles) { + public void setRoles(List roles) { this.roles = roles; } diff --git a/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/enums/ResourceType.java b/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/enums/ResourceType.java new file mode 100644 index 0000000..90846d2 --- /dev/null +++ b/schoolNewsServ/common/common-core/src/main/java/org/xyzh/common/core/enums/ResourceType.java @@ -0,0 +1,36 @@ +package org.xyzh.common.core.enums; + +public enum ResourceType { + + NEWS(1, "新闻", "新闻"), + COURSE(2, "课程", "课程"), + TASK(3, "任务", "任务"), + DEPT(4, "部门", "部门"), + ROLE(5, "角色", "角色"), + ACHIEVEMENT(6, "成就", "成就"), + CRONTAB_TASK(7, "定时任务", "定时任务"), + BANNER(8, "轮播图", "轮播图"), + TAG(9, "标签", "标签"); + + private int code; + private String name; + private String description; + + ResourceType(int code, String name, String description) { + this.code = code; + this.name = name; + this.description = description; + } + + public int getCode() { + return code; + } + + public String getName() { + return name; + } + + public String getDescription() { + return description; + } +} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/dept/TbSysDept.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/dept/TbSysDept.java index 87d1400..7e25175 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/dept/TbSysDept.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/dto/dept/TbSysDept.java @@ -26,6 +26,13 @@ public class TbSysDept extends BaseDTO{ */ private String parentID; + /** + * @description 部门路径,格式:/root_department/dept_001/ + * @author yslg + * @since 2025-10-29 + */ + private String deptPath; + /** * @description 部门名称 * @author yslg @@ -71,6 +78,14 @@ public class TbSysDept extends BaseDTO{ this.parentID = parentID; } + public String getDeptPath() { + return deptPath; + } + + public void setDeptPath(String deptPath) { + this.deptPath = deptPath; + } + public String getName() { return name; } @@ -109,6 +124,7 @@ public class TbSysDept extends BaseDTO{ "id='" + getID() + '\'' + ", deptID='" + deptID + '\'' + ", parentID='" + parentID + '\'' + + ", deptPath='" + deptPath + '\'' + ", name='" + name + '\'' + ", description='" + description + '\'' + ", creator='" + creator + '\'' + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/DeptRoleVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/DeptRoleVO.java deleted file mode 100644 index 0da35ec..0000000 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/DeptRoleVO.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.xyzh.common.vo; - -import org.xyzh.common.dto.dept.TbSysDept; -import org.xyzh.common.dto.role.TbSysRole; -import org.xyzh.common.dto.user.TbSysUser; - -import java.util.List; - -public class DeptRoleVO { - - private TbSysDept dept; - private TbSysRole role; - - private List depts; - private List roles; - private List users; - - public TbSysDept getDept() { - return dept; - } - - public void setDept(TbSysDept dept) { - this.dept = dept; - } - - public TbSysRole getRole() { - return role; - } - - public void setRole(TbSysRole role) { - this.role = role; - } - - public List getDepts() { - return depts; - } - - public void setDepts(List depts) { - this.depts = depts; - } - - public List getRoles() { - return roles; - } - - public void setRoles(List roles) { - this.roles = roles; - } - - public List getUsers() { - return users; - } - - public void setUsers(List users) { - this.users = users; - } - -} diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourcePermissionVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourcePermissionVO.java new file mode 100644 index 0000000..db1b6b6 --- /dev/null +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/ResourcePermissionVO.java @@ -0,0 +1,120 @@ +package org.xyzh.common.vo; + +import org.xyzh.common.dto.permission.TbResourcePermission; + +/** + * @description 资源权限视图对象 + * @filename ResourcePermissionVO.java + * @author yslg + * @copyright xyzh + * @since 2025-10-29 + */ +public class ResourcePermissionVO extends TbResourcePermission { + + private static final long serialVersionUID = 1L; + + /** + * @description 部门名称 + */ + private String deptName; + + /** + * @description 角色名称 + */ + private String roleName; + + /** + * @description 资源标题(根据资源类型获取) + */ + private String resourceTitle; + + /** + * @description 查询用的用户ID(用于权限校验) + */ + private String userID; + + /** + * @description 用户的部门ID列表(用于权限校验) + */ + private String[] userDeptIDs; + + /** + * @description 用户的角色ID列表(用于权限校验) + */ + private String[] userRoleIDs; + + public ResourcePermissionVO() { + super(); + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getResourceTitle() { + return resourceTitle; + } + + public void setResourceTitle(String resourceTitle) { + this.resourceTitle = resourceTitle; + } + + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + public String[] getUserDeptIDs() { + return userDeptIDs; + } + + public void setUserDeptIDs(String[] userDeptIDs) { + this.userDeptIDs = userDeptIDs; + } + + public String[] getUserRoleIDs() { + return userRoleIDs; + } + + public void setUserRoleIDs(String[] userRoleIDs) { + this.userRoleIDs = userRoleIDs; + } + + @Override + public String toString() { + return "ResourcePermissionVO{" + + "id=" + getID() + + ", resourceType=" + getResourceType() + + ", resourceID='" + getResourceID() + '\'' + + ", resourceTitle='" + resourceTitle + '\'' + + ", deptID='" + getDeptID() + '\'' + + ", deptName='" + deptName + '\'' + + ", roleID='" + getRoleID() + '\'' + + ", roleName='" + roleName + '\'' + + ", canRead=" + getCanRead() + + ", canWrite=" + getCanWrite() + + ", canExecute=" + getCanExecute() + + ", creator='" + getCreator() + '\'' + + ", updater='" + getUpdater() + '\'' + + ", createTime=" + getCreateTime() + + ", updateTime=" + getUpdateTime() + + ", deleted=" + getDeleted() + + '}'; + } +} + diff --git a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserDeptRoleVO.java b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserDeptRoleVO.java index ce9e78e..aae8b2f 100644 --- a/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserDeptRoleVO.java +++ b/schoolNewsServ/common/common-dto/src/main/java/org/xyzh/common/vo/UserDeptRoleVO.java @@ -1,51 +1,159 @@ package org.xyzh.common.vo; -import java.util.List; - -import org.xyzh.common.dto.BaseDTO; import org.xyzh.common.dto.dept.TbSysDept; import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.dto.user.TbSysUser; import org.xyzh.common.dto.user.TbSysUserDeptRole; -public class UserDeptRoleVO extends BaseDTO{ - private TbSysUser user; - private List users; +import java.util.List; + +public class UserDeptRoleVO { private List depts; private List roles; + private List users; private List userDeptRoles; - - public TbSysUser getUser() { - return user; - } - public void setUser(TbSysUser user) { - this.user = user; - } - public List getUsers() { - return users; - } - public void setUsers(List users) { - this.users = users; - } + + + // 扁平化字段,用于权限查询优化 + private String userID; + private String username; + private String deptID; + private String deptName; + private String deptDescription; + private String parentID; + private String parentName; + private String parentDescription; + private String roleID; + private String roleName; + private String roleDescription; + private String deptPath; // 部门路径,用于快速权限继承判断 + public List getDepts() { return depts; } + public void setDepts(List depts) { this.depts = depts; } + public List getRoles() { return roles; } + public void setRoles(List roles) { this.roles = roles; } - public List getUserDeptRoles() { - return userDeptRoles; - } - public void setUserDeptRoles(List userDeptRoles) { - this.userDeptRoles = userDeptRoles; + + public List getUsers() { + return users; } - + public void setUsers(List users) { + this.users = users; + } + + public String getUserID() { + return userID; + } + + public void setUserID(String userID) { + this.userID = userID; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getDeptID() { + return deptID; + } + + public void setDeptID(String deptID) { + this.deptID = deptID; + } + + public String getDeptName() { + return deptName; + } + + public void setDeptName(String deptName) { + this.deptName = deptName; + } + + public String getDeptDescription() { + return deptDescription; + } + + public void setDeptDescription(String deptDescription) { + this.deptDescription = deptDescription; + } + + public String getParentID() { + return parentID; + } + + public void setParentID(String parentID) { + this.parentID = parentID; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public String getParentDescription() { + return parentDescription; + } + + public void setParentDescription(String parentDescription) { + this.parentDescription = parentDescription; + } + + public String getRoleID() { + return roleID; + } + + public void setRoleID(String roleID) { + this.roleID = roleID; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRoleDescription() { + return roleDescription; + } + + public void setRoleDescription(String roleDescription) { + this.roleDescription = roleDescription; + } + + public String getDeptPath() { + return deptPath; + } + + public void setDeptPath(String deptPath) { + this.deptPath = deptPath; + } + + public List getUserDeptRoles() { + return userDeptRoles; + } + + public void setUserDeptRoles(List userDeptRoles) { + this.userDeptRoles = userDeptRoles; + } } diff --git a/schoolNewsServ/crontab/pom.xml b/schoolNewsServ/crontab/pom.xml index c38662e..0f0dbee 100644 --- a/schoolNewsServ/crontab/pom.xml +++ b/schoolNewsServ/crontab/pom.xml @@ -32,6 +32,12 @@ common-all ${school-news.version} + + + org.xyzh + system + ${school-news.version} + diff --git a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java index 4093881..9c14bbf 100644 --- a/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java +++ b/schoolNewsServ/crontab/src/main/java/org/xyzh/crontab/service/impl/CrontabServiceImpl.java @@ -13,9 +13,15 @@ import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.crontab.TbCrontabTask; import org.xyzh.common.dto.crontab.TbCrontabLog; import org.xyzh.common.utils.IDUtils; +import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.crontab.mapper.CrontabTaskMapper; import org.xyzh.crontab.mapper.CrontabLogMapper; import org.xyzh.crontab.scheduler.SchedulerManager; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.dto.user.TbSysUser; +import org.xyzh.common.dto.user.TbSysUserDeptRole; +import org.xyzh.common.core.enums.ResourceType; +import org.xyzh.system.utils.LoginUtil; import java.time.LocalDateTime; import java.util.Calendar; @@ -43,6 +49,9 @@ public class CrontabServiceImpl implements CrontabService { @Autowired private SchedulerManager schedulerManager; + @Autowired + private ResourcePermissionService resourcePermissionService; + // ----------------定时任务管理-------------------------------- @Override @@ -77,6 +86,21 @@ public class CrontabServiceImpl implements CrontabService { if (result > 0) { logger.info("创建定时任务成功: {}", task.getTaskName()); + // 创建定时任务资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + resourcePermissionService.createResourcePermission( + ResourceType.CRONTAB_TASK.getCode(), + task.getTaskId(), + userDeptRoles.get(0) + ); + logger.info("创建定时任务权限成功: {}", task.getTaskName()); + } + } catch (Exception e) { + logger.error("创建定时任务权限异常,但不影响任务创建: {}", e.getMessage(), e); + } + // 如果任务状态为启动,则立即调度 if (task.getStatus() == 1) { schedulerManager.scheduleTask(task); diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java index b8a9f94..3e25729 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/BannerMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbBanner; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -21,24 +22,26 @@ public interface BannerMapper extends BaseMapper { /** * @description 查询Banner列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List Banner列表 * @author yslg * @since 2025-10-15 */ - List selectBanners(TbBanner filter); + List selectBanners(@Param("filter") TbBanner filter, @Param("userDeptRoles") List userDeptRoles); - List selectBannersLimit(@Param("filter") TbBanner filter, @Param("limit") Integer limit); + List selectBannersLimit(@Param("filter") TbBanner filter, @Param("limit") Integer limit, @Param("userDeptRoles") List userDeptRoles); /** * @description 分页查询Banner * @param filter 过滤条件 * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 * @return List Banner列表 * @author yslg * @since 2025-10-15 */ - List selectBannersPage(@Param("filter") TbBanner filter, @Param("pageParam") PageParam pageParam); + List selectBannersPage(@Param("filter") TbBanner filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据Banner ID查询Banner信息 @@ -133,9 +136,10 @@ public interface BannerMapper extends BaseMapper { /** * @description 统计Banner总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return long 总数 * @author yslg * @since 2025-10-15 */ - long countBanners(@Param("filter") TbBanner filter); + long countBanners(@Param("filter") TbBanner filter, @Param("userDeptRoles") List userDeptRoles); } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java index d5638c4..af5be44 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/ResourceMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbResource; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -21,11 +22,12 @@ public interface ResourceMapper extends BaseMapper { /** * @description 查询资源列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 资源列表 * @author yslg * @since 2025-10-15 */ - List selectResources(TbResource filter); + List selectResources(@Param("filter") TbResource filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据资源ID查询资源信息 @@ -149,20 +151,22 @@ public interface ResourceMapper extends BaseMapper { * @description 分页查询资源 * @param filter 过滤条件 * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 * @return List 资源列表 * @author yslg * @since 2025-10-15 */ - List selectResourcesPage(@Param("filter") TbResource filter, @Param("pageParam") PageParam pageParam); + List selectResourcesPage(@Param("filter") TbResource filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 统计资源总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return long 总数 * @author yslg * @since 2025-10-15 */ - long countResources(TbResource filter); + long countResources(@Param("filter") TbResource filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 更新资源收藏次数 diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/TagMapper.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/TagMapper.java index 81afef3..e61bbe5 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/TagMapper.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/mapper/TagMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.resource.TbTag; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -21,11 +22,12 @@ public interface TagMapper extends BaseMapper { /** * @description 查询标签列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 标签列表 * @author yslg * @since 2025-10-15 */ - List selectTags(TbTag filter); + List selectTags(@Param("filter") TbTag filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据标签ID查询标签信息 diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java index 42e99bb..567ba85 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCBannerServiceImpl.java @@ -17,6 +17,11 @@ import org.xyzh.common.dto.resource.TbBanner; import org.xyzh.common.utils.IDUtils; import org.xyzh.news.mapper.BannerMapper; import org.xyzh.api.news.banner.BannerService; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.dto.user.TbSysUser; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; +import org.xyzh.system.utils.LoginUtil; /** * @description 横幅服务实现类 @@ -33,11 +38,16 @@ public class NCBannerServiceImpl implements BannerService { @Autowired private BannerMapper bannerMapper; + @Autowired + private ResourcePermissionService resourcePermissionService; + @Override public ResultDomain getBannerList(TbBanner filter) { ResultDomain resultDomain = new ResultDomain<>(); - List list = bannerMapper.selectBanners(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = bannerMapper.selectBanners(filter, userDeptRoles); resultDomain.success("获取横幅列表成功", list); return resultDomain; } @@ -45,9 +55,11 @@ public class NCBannerServiceImpl implements BannerService { @Override public ResultDomain getBannerPage(PageParam pageParam,TbBanner filter) { ResultDomain resultDomain = new ResultDomain<>(); - List list = bannerMapper.selectBannersPage(filter, pageParam); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = bannerMapper.selectBannersPage(filter, pageParam, userDeptRoles); PageDomain pageDomain = new PageDomain<>(); - int total = (int)bannerMapper.countBanners(filter); + int total = (int)bannerMapper.countBanners(filter, userDeptRoles); pageParam.setTotalElements(total); pageParam.setTotalPages( (int)Math.ceil((double)total / pageParam.getPageSize())); pageDomain.setDataList(list); @@ -100,6 +112,23 @@ public class NCBannerServiceImpl implements BannerService { int result = bannerMapper.insertBanner(banner); if (result > 0) { logger.info("创建横幅成功: {}", banner.getTitle()); + + // 创建横幅资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.BANNER.getCode(), + banner.getBannerID(), + userDeptRoles.get(0) + ); + logger.info("创建横幅权限成功: {}", banner.getBannerID()); + } + } catch (Exception e) { + logger.error("创建横幅权限异常,但不影响横幅创建: {}", e.getMessage(), e); + } + resultDomain.success("创建横幅成功", banner); return resultDomain; } else { @@ -363,8 +392,9 @@ public class NCBannerServiceImpl implements BannerService { ResultDomain resultDomain = new ResultDomain<>(); TbBanner filter = new TbBanner(); filter.setStatus(1); - - List list = bannerMapper.selectBannersLimit(filter, 5); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = bannerMapper.selectBannersLimit(filter, 5, userDeptRoles); resultDomain.success("获取首页横幅列表成功", list); return resultDomain; } diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java index 05177c5..f5a538d 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCResourceServiceImpl.java @@ -22,6 +22,9 @@ import org.xyzh.news.mapper.ResourceTagMapper; import org.xyzh.system.utils.LoginUtil; import org.xyzh.api.news.resource.ResourceService; import org.xyzh.api.usercenter.collection.UserCollectionService; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; import java.util.ArrayList; import java.util.Date; @@ -49,6 +52,9 @@ public class NCResourceServiceImpl implements ResourceService { @Autowired private UserCollectionService userCollectionService; + @Autowired + private ResourcePermissionService resourcePermissionService; + @Override public ResultDomain getResourceList(TbResource filter) { ResultDomain resultDomain = new ResultDomain<>(); @@ -56,7 +62,9 @@ public class NCResourceServiceImpl implements ResourceService { if (filter == null) { filter = new TbResource(); } - List list = resourceMapper.selectResources(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectResources(filter, userDeptRoles); resultDomain.success("获取资源列表成功", list); return resultDomain; } catch (Exception e) { @@ -74,8 +82,10 @@ public class NCResourceServiceImpl implements ResourceService { filter = new TbResource(); } - List list = resourceMapper.selectResourcesPage(filter, pageParam); - long total = resourceMapper.countResources(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectResourcesPage(filter, pageParam, userDeptRoles); + long total = resourceMapper.countResources(filter, userDeptRoles); pageParam.setTotalElements(total); pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); resultDomain.success("获取资源分页成功", new PageDomain(pageParam, list)); @@ -192,6 +202,23 @@ public class NCResourceServiceImpl implements ResourceService { if (result > 0) { logger.info("创建资源成功: {}", resourceVO.getResource().getTitle()); + + // 创建资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.NEWS.getCode(), + resourceVO.getResource().getResourceID(), + userDeptRoles.get(0) + ); + logger.info("创建资源权限成功: {}", resourceVO.getResource().getResourceID()); + } + } catch (Exception e) { + logger.error("创建资源权限异常,但不影响资源创建: {}", e.getMessage(), e); + } + resultDomain.success("创建资源成功", resourceVO); return resultDomain; } else { @@ -679,7 +706,9 @@ public class NCResourceServiceImpl implements ResourceService { filter.setIsRecommend(true); filter.setStatus(1); // 只查询已发布的 - List list = resourceMapper.selectResources(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectResources(filter, userDeptRoles); // 如果指定了limit,截取列表 if (limit != null && limit > 0 && list != null && list.size() > limit) { @@ -704,7 +733,9 @@ public class NCResourceServiceImpl implements ResourceService { filter.setIsBanner(true); filter.setStatus(1); // 只查询已发布的 - List list = resourceMapper.selectResources(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List list = resourceMapper.selectResources(filter, userDeptRoles); // 如果指定了limit,截取列表 if (limit != null && limit > 0 && list != null && list.size() > limit) { diff --git a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java index 3285eec..78cb744 100644 --- a/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java +++ b/schoolNewsServ/news/src/main/java/org/xyzh/news/service/impl/NCTagServiceImpl.java @@ -19,6 +19,9 @@ import org.xyzh.news.mapper.ResourceTagMapper; import org.xyzh.news.mapper.TagMapper; import org.xyzh.system.utils.LoginUtil; import org.xyzh.api.news.tag.TagService; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; /** * @description 标签服务实现类 @@ -38,6 +41,9 @@ public class NCTagServiceImpl implements TagService { @Autowired private ResourceTagMapper resourceTagMapper; + @Autowired + private ResourcePermissionService resourcePermissionService; + // ----------------标签管理相关-------------------------------- @Override @@ -74,6 +80,23 @@ public class NCTagServiceImpl implements TagService { int result = tagMapper.insertTag(tag); if (result > 0) { logger.info("创建标签成功: {}", tag.getName()); + + // 创建标签资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.TAG.getCode(), + tag.getTagID(), + userDeptRoles.get(0) + ); + logger.info("创建标签权限成功: {}", tag.getTagID()); + } + } catch (Exception e) { + logger.error("创建标签权限异常,但不影响标签创建: {}", e.getMessage(), e); + } + resultDomain.success("创建标签成功", tag); return resultDomain; } else { @@ -195,7 +218,9 @@ public class NCTagServiceImpl implements TagService { public ResultDomain getAllTags() { ResultDomain resultDomain = new ResultDomain<>(); try { - List tags = tagMapper.selectTags(new TbTag()); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List tags = tagMapper.selectTags(new TbTag(), userDeptRoles); resultDomain.success("查询成功", tags); return resultDomain; } catch (Exception e) { @@ -216,7 +241,9 @@ public class NCTagServiceImpl implements TagService { TbTag filter = new TbTag(); filter.setName(name); - List tags = tagMapper.selectTags(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List tags = tagMapper.selectTags(filter, userDeptRoles); resultDomain.success("查询成功", tags); return resultDomain; diff --git a/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml index c424cfd..22234a9 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/BannerMapper.xml @@ -69,21 +69,83 @@ - + + + INNER JOIN tb_resource_permission rp ON b.banner_id = rp.resource_id + AND rp.resource_type = 8 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + + @@ -218,20 +280,56 @@ + - + diff --git a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml index 0d4aa81..a11782d 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/ResourceMapper.xml @@ -62,13 +62,62 @@ - + + + INNER JOIN tb_resource_permission rp ON r.resource_id = rp.resource_id + AND rp.resource_type = 1 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + @@ -266,41 +315,58 @@ - + - + @@ -318,4 +384,6 @@ SET view_count = view_count + 1 WHERE resource_id = #{resourceID} + + diff --git a/schoolNewsServ/news/src/main/resources/mapper/TagMapper.xml b/schoolNewsServ/news/src/main/resources/mapper/TagMapper.xml index 67fb278..712c4d8 100644 --- a/schoolNewsServ/news/src/main/resources/mapper/TagMapper.xml +++ b/schoolNewsServ/news/src/main/resources/mapper/TagMapper.xml @@ -43,13 +43,56 @@ - + + + INNER JOIN tb_resource_permission rp ON t.tag_id = rp.resource_id + AND rp.resource_type = 9 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java index 3189962..7f12bcc 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/CourseMapper.java @@ -5,7 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.study.TbCourse; -import org.xyzh.common.vo.CourseItemVO; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -22,11 +22,12 @@ public interface CourseMapper extends BaseMapper { /** * @description 查询课程列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 课程列表 * @author yslg * @since 2025-10-15 */ - List selectCourses(TbCourse filter); + List selectCourses(@Param("filter") TbCourse filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据课程ID查询课程信息 @@ -159,20 +160,22 @@ public interface CourseMapper extends BaseMapper { * @description 分页查询课程 * @param filter 过滤条件 * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 * @return List 课程列表 * @author yslg * @since 2025-10-15 */ - List selectCoursesPage(@Param("filter") TbCourse filter, @Param("pageParam") PageParam pageParam); + List selectCoursesPage(@Param("filter") TbCourse filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 统计课程总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return long 总数 * @author yslg * @since 2025-10-15 */ - long countCourses(@Param("filter") TbCourse filter); + long countCourses(@Param("filter") TbCourse filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 增加课程浏览次数 diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskMapper.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskMapper.java index 4b23127..7b6fc7e 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskMapper.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/mapper/LearningTaskMapper.java @@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.core.page.PageParam; import org.xyzh.common.dto.study.TbLearningTask; +import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.common.vo.TaskItemVO; import java.util.List; @@ -22,11 +23,12 @@ public interface LearningTaskMapper extends BaseMapper { /** * @description 查询学习任务列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 学习任务列表 * @author yslg * @since 2025-10-15 */ - List selectLearningTasks(TbLearningTask filter); + List selectLearningTasks(@Param("filter") TbLearningTask filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据任务ID查询任务信息 @@ -150,20 +152,22 @@ public interface LearningTaskMapper extends BaseMapper { * @description 分页查询学习任务 * @param filter 过滤条件 * @param pageParam 分页参数 + * @param userDeptRoles 用户部门角色列表 * @return List 学习任务列表 * @author yslg * @since 2025-10-15 */ - List selectLearningTasksPage(@Param("filter") TbLearningTask filter, @Param("pageParam") PageParam pageParam); + List selectLearningTasksPage(@Param("filter") TbLearningTask filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); - List selectUserLearningTasksPage(@Param("filter") TaskItemVO filter, @Param("pageParam") PageParam pageParam); + List selectUserLearningTasksPage(@Param("filter") TaskItemVO filter, @Param("pageParam") PageParam pageParam, @Param("userDeptRoles") List userDeptRoles); /** * @description 统计学习任务总数 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return long 总数 * @author yslg * @since 2025-10-15 */ - long countLearningTasks(@Param("filter") TbLearningTask filter); + long countLearningTasks(@Param("filter") TbLearningTask filter, @Param("userDeptRoles") List userDeptRoles); } diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java index 93c5b21..a617d25 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCCourseServiceImpl.java @@ -28,6 +28,9 @@ import org.xyzh.study.mapper.CourseChapterMapper; import org.xyzh.study.mapper.CourseNodeMapper; import org.xyzh.study.service.SCCourseService; import org.xyzh.system.utils.LoginUtil; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; /** * @description 课程服务实现类 @@ -50,10 +53,15 @@ public class SCCourseServiceImpl implements SCCourseService { @Autowired private CourseNodeMapper courseNodeMapper; + @Autowired + private ResourcePermissionService resourcePermissionService; + @Override public ResultDomain getCourseList(TbCourse filter) { ResultDomain resultDomain = new ResultDomain<>(); - List courses = courseMapper.selectCourses(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List courses = courseMapper.selectCourses(filter, userDeptRoles); resultDomain.success("获取课程列表成功", courses); return resultDomain; } @@ -63,8 +71,10 @@ public class SCCourseServiceImpl implements SCCourseService { ResultDomain resultDomain = new ResultDomain<>(); TbCourse filter = pageRequest.getFilter(); PageParam pageParam = pageRequest.getPageParam(); - List courses = courseMapper.selectCoursesPage(filter, pageParam); - int total = (int) courseMapper.countCourses(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List courses = courseMapper.selectCoursesPage(filter, pageParam, userDeptRoles); + int total = (int) courseMapper.countCourses(filter, userDeptRoles); int totalPages = (int) Math.ceil((double) total / pageParam.getPageSize()); pageParam.setTotalPages(totalPages); pageParam.setTotalElements(total); @@ -194,6 +204,22 @@ public class SCCourseServiceImpl implements SCCourseService { } } + // 创建课程资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.COURSE.getCode(), + courseID, + userDeptRoles.get(0) + ); + logger.info("创建课程权限成功: {}", courseID); + } + } catch (Exception e) { + logger.error("创建课程权限异常,但不影响课程创建: {}", e.getMessage(), e); + } + resultDomain.success("创建课程成功", courseItemVO); return resultDomain; } diff --git a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java index 530e28f..6387b54 100644 --- a/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java +++ b/schoolNewsServ/study/src/main/java/org/xyzh/study/service/impl/SCLearningTaskServiceImpl.java @@ -30,6 +30,9 @@ import org.xyzh.system.utils.LoginUtil; import org.xyzh.study.mapper.TaskItemMapper; import org.xyzh.api.study.task.LearningTaskService; import org.xyzh.common.core.enums.TaskItemType; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.common.core.enums.ResourceType; /** * @description 学习任务服务实现类 @@ -55,6 +58,9 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Autowired private TaskItemMapper taskItemMapper; + @Autowired + private ResourcePermissionService resourcePermissionService; + @Override public ResultDomain addTaskCourse(TbTaskItem taskItem) { // TODO Auto-generated method stub @@ -105,8 +111,10 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Override public ResultDomain getTaskPage(TbLearningTask filter, PageParam pageParam) { ResultDomain resultDomain = new ResultDomain<>(); - List taskList = learningTaskMapper.selectLearningTasksPage(filter, pageParam); - long total = learningTaskMapper.countLearningTasks(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List taskList = learningTaskMapper.selectLearningTasksPage(filter, pageParam, userDeptRoles); + long total = learningTaskMapper.countLearningTasks(filter, userDeptRoles); pageParam.setTotalElements(total); pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); PageDomain pageDomain = new PageDomain<>(); @@ -125,8 +133,10 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { return resultDomain; } filter.setUserID(user.getID()); - List taskList = learningTaskMapper.selectUserLearningTasksPage(filter, pageParam); - long total = learningTaskMapper.countLearningTasks(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List taskList = learningTaskMapper.selectUserLearningTasksPage(filter, pageParam, userDeptRoles); + long total = learningTaskMapper.countLearningTasks(filter, userDeptRoles); pageParam.setTotalElements(total); pageParam.setTotalPages((int) Math.ceil((double) total / pageParam.getPageSize())); PageDomain pageDomain = new PageDomain<>(); @@ -188,7 +198,22 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { taskUserMapper.batchInsertTaskUsers(taskUsers); for(TbTaskItem item : taskCourses) { int learnCount = courseMapper.incrementLearnCount(item.getItemID(), taskUsers.size()); - + } + + // 创建任务资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + // 使用用户的第一个部门角色创建权限 + resourcePermissionService.createResourcePermission( + ResourceType.TASK.getCode(), + taskID, + userDeptRoles.get(0) + ); + logger.info("创建任务权限成功: {}", taskID); + } + } catch (Exception e) { + logger.error("创建任务权限异常,但不影响任务创建: {}", e.getMessage(), e); } resultDomain.success("创建任务成功", taskVO); @@ -515,7 +540,9 @@ public class SCLearningTaskServiceImpl implements LearningTaskService { @Override public ResultDomain getTaskList(TbLearningTask filter) { ResultDomain resultDomain = new ResultDomain<>(); - List taskList = learningTaskMapper.selectLearningTasks(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List taskList = learningTaskMapper.selectLearningTasks(filter, userDeptRoles); resultDomain.success("获取任务列表成功", taskList); return resultDomain; } diff --git a/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml index 5c482f2..568ae61 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/CourseMapper.xml @@ -78,13 +78,56 @@ - + + + INNER JOIN tb_resource_permission rp ON c.course_id = rp.resource_id + AND rp.resource_type = 2 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + @@ -270,20 +313,62 @@ + - + diff --git a/schoolNewsServ/study/src/main/resources/mapper/LearningTaskMapper.xml b/schoolNewsServ/study/src/main/resources/mapper/LearningTaskMapper.xml index 7a51635..f3ae0b1 100644 --- a/schoolNewsServ/study/src/main/resources/mapper/LearningTaskMapper.xml +++ b/schoolNewsServ/study/src/main/resources/mapper/LearningTaskMapper.xml @@ -56,13 +56,53 @@ - + + + INNER JOIN tb_resource_permission rp ON t.task_id = rp.resource_id + AND rp.resource_type = 3 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + @@ -215,33 +255,78 @@ + + - + diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/DeptController.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/DeptController.java index 7260592..e724f82 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/DeptController.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/DeptController.java @@ -15,7 +15,7 @@ import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.dept.TbSysDept; import org.xyzh.common.dto.dept.TbSysDeptRole; import org.xyzh.common.dto.role.TbSysRole; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RequestMapping; @@ -110,14 +110,13 @@ public class DeptController { } /** - * @description 查询部门绑定角色 - * @param dept 部门信息 - * @return ResultDomain 角色信息 + * @description 查询部门绑定角色列表(包含名称) + * @return ResultDomain 部门角色信息 * @author yslg * @since 2025-10-06 */ @PostMapping("/role/list") - public ResultDomain getDeptByRoleList() { + public ResultDomain getDeptByRoleList() { return deptService.getDeptByRoleList(); } @@ -129,7 +128,7 @@ public class DeptController { * @since 2025-10-06 */ @PostMapping("/bind/role") - public ResultDomain bindDeptRole(@RequestBody DeptRoleVO deptRole) { + public ResultDomain bindDeptRole(@RequestBody UserDeptRoleVO deptRole) { List deptIDs = deptRole.getDepts().stream().map(TbSysDept::getDeptID).collect(Collectors.toList()); List roleIDs = deptRole.getRoles().stream().map(TbSysRole::getRoleID).collect(Collectors.toList()); return deptService.bindDeptRole(deptIDs, roleIDs); @@ -143,7 +142,7 @@ public class DeptController { * @since 2025-10-06 */ @PostMapping("/unbind/role") - public ResultDomain unbindDeptRole(@RequestBody DeptRoleVO deptRole) { + public ResultDomain unbindDeptRole(@RequestBody UserDeptRoleVO deptRole) { List deptIDs = deptRole.getDepts().stream().map(TbSysDept::getDeptID).collect(Collectors.toList()); List roleIDs = deptRole.getRoles().stream().map(TbSysRole::getRoleID).collect(Collectors.toList()); return deptService.unbindDeptRole(deptIDs, roleIDs); diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/RoleController.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/RoleController.java index c1b1c92..875c9ab 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/RoleController.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/RoleController.java @@ -14,7 +14,7 @@ import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.permission.TbSysPermission; import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.dto.role.TbSysRolePermission; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; /** * @description RoleController.java文件描述 角色控制器 diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/UserController.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/UserController.java index 3ba40c3..2a70c27 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/UserController.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/controller/UserController.java @@ -116,7 +116,7 @@ public class UserController { * @since 2025-10-09 */ @PostMapping("/bind/deptrole/list") - public ResultDomain getBindUserDeptRoleList(@RequestBody TbSysUserDeptRole filter) { + public ResultDomain getBindUserDeptRoleList(@RequestBody TbSysUserDeptRole filter) { return userService.getBindUserDeptRoleList(filter); } diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/department/service/impl/SysDepartmentServiceImpl.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/department/service/impl/SysDepartmentServiceImpl.java index 02bbcc1..75dd54c 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/department/service/impl/SysDepartmentServiceImpl.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/department/service/impl/SysDepartmentServiceImpl.java @@ -4,12 +4,18 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; +import org.xyzh.api.system.permission.ResourcePermissionService; import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.core.enums.ResourceType; import org.xyzh.common.dto.dept.TbSysDept; import org.xyzh.common.dto.dept.TbSysDeptRole; +import org.xyzh.common.dto.permission.TbResourcePermission; import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.dto.user.TbSysUser; +import org.xyzh.common.dto.user.TbSysUserDeptRole; +import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.common.utils.IDUtils; import org.xyzh.system.department.service.SysDepartmentService; import org.xyzh.system.mapper.DepartmentMapper; @@ -38,6 +44,9 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { @Autowired private DeptRoleMapper deptRoleMapper; + @Autowired + private ResourcePermissionService resourcePermissionService; + @Override public ResultDomain getAllDepartments() { ResultDomain resultDomain = new ResultDomain<>(); @@ -46,7 +55,9 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { logger.info("开始查询所有部门"); TbSysDept filter = new TbSysDept(); filter.setDeleted(false); - List departments = departmentMapper.selectDepts(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List departments = departmentMapper.selectDepts(filter, userDeptRoles); logger.info("查询所有部门完成,共找到{}个部门", departments.size()); resultDomain.success("查询成功", departments); @@ -65,7 +76,9 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始查询部门列表"); - List departments = departmentMapper.selectDepts(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List departments = departmentMapper.selectDepts(filter, userDeptRoles); if (departments.isEmpty()) { resultDomain.fail("未找到部门"); return resultDomain; @@ -80,11 +93,19 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { } @Override - public ResultDomain getDeptByRoleList() { - ResultDomain resultDomain = new ResultDomain<>(); - List deptRoles = deptRoleMapper.selectDeptRoleList(); - resultDomain.success("查询成功", deptRoles); - return resultDomain; + public ResultDomain getDeptByRoleList() { + ResultDomain resultDomain = new ResultDomain<>(); + try { + logger.info("开始查询部门角色关联列表"); + List deptRoles = deptRoleMapper.selectDeptRoleList(); + logger.info("查询部门角色关联列表完成,共找到{}条记录", deptRoles.size()); + resultDomain.success("查询成功", deptRoles); + return resultDomain; + } catch (Exception e) { + logger.error("查询部门角色关联列表失败", e); + resultDomain.fail("查询部门角色关联列表失败:" + e.getMessage()); + return resultDomain; + } } @Override @@ -100,7 +121,9 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { TbSysDept filter = new TbSysDept(); filter.setDeptID(deptId); - List departments = departmentMapper.selectDepts(filter); + // 获取当前用户的部门角色 + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + List departments = departmentMapper.selectDepts(filter, userDeptRoles); TbSysDept department = departments.isEmpty() ? null : departments.get(0); if (department == null) { @@ -140,6 +163,7 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { } @Override + @Transactional(rollbackFor = Exception.class) public ResultDomain createDepartment(TbSysDept department) { ResultDomain resultDomain = new ResultDomain<>(); try { @@ -162,9 +186,17 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { return resultDomain; } + // 获取当前用户 + TbSysUser currentUser = LoginUtil.getCurrentUser(); + if (currentUser == null) { + resultDomain.fail("请先登录"); + return resultDomain; + } + // 设置基础信息 department.setID(IDUtils.generateID()); department.setDeptID(IDUtils.generateID()); + department.setCreator(currentUser.getID()); department.setCreateTime(new Date()); department.setDeleted(false); @@ -173,6 +205,24 @@ public class SysDepartmentServiceImpl implements SysDepartmentService { if (result > 0) { logger.info("创建部门成功:{}", department.getName()); + + // 创建资源权限 + try { + List userDeptRoles = LoginUtil.getCurrentDeptRole(); + if (userDeptRoles != null && !userDeptRoles.isEmpty()) { + ResultDomain permissionResult = resourcePermissionService.createResourcePermission( + ResourceType.DEPT.getCode(), + department.getDeptID(), + userDeptRoles.get(0) + ); + if (!permissionResult.isSuccess()) { + logger.warn("创建部门权限失败:{}", permissionResult.getMessage()); + } + } + } catch (Exception e) { + logger.error("创建部门权限异常", e); + } + resultDomain.success("创建部门成功", department); return resultDomain; } else { diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DepartmentMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DepartmentMapper.java index d2c7430..801b9ba 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DepartmentMapper.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DepartmentMapper.java @@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.dto.dept.TbSysDept; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -20,11 +21,12 @@ public interface DepartmentMapper extends BaseMapper { /** * @description 查询部门列表 * @param filter 过滤条件 + * @param userDeptRoles 用户部门角色列表 * @return List 部门列表 * @author yslg * @since 2025-10-06 */ - List selectDepts(TbSysDept filter); + List selectDepts(@Param("filter") TbSysDept filter, @Param("userDeptRoles") List userDeptRoles); /** * @description 根据父部门ID查询子部门列表 diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DeptRoleMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DeptRoleMapper.java index 4009487..b14064a 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DeptRoleMapper.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/DeptRoleMapper.java @@ -8,6 +8,7 @@ import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.dto.dept.TbSysDeptRole; import org.xyzh.common.dto.role.TbSysRole; +import org.xyzh.common.vo.UserDeptRoleVO; @Mapper public interface DeptRoleMapper extends BaseMapper { @@ -22,12 +23,12 @@ public interface DeptRoleMapper extends BaseMapper { List selectDeptRole(String deptId); /** - * @description 查询部门绑定角色 - * @return List 部门角色列表 + * @description 查询部门绑定角色列表(包含名称) + * @return List 部门角色列表 * @author yslg * @since 2025-10-06 */ - List selectDeptRoleList(); + List selectDeptRoleList(); /** * @description 批量绑定部门角色 @@ -39,4 +40,8 @@ public interface DeptRoleMapper extends BaseMapper { int batchBindDeptRole(@Param("deptRoles") List deptRoles); int batchUnbindDeptRole(@Param("deptRoles") List deptRoles); + + List selectParentDeptAdmin(UserDeptRoleVO userDeptRole); + + List selectChildDeptRole(UserDeptRoleVO userDeptRole); } diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/ResourcePermissionMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/ResourcePermissionMapper.java new file mode 100644 index 0000000..179d67f --- /dev/null +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/ResourcePermissionMapper.java @@ -0,0 +1,40 @@ +package org.xyzh.system.mapper; + +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.xyzh.common.dto.permission.TbResourcePermission; +import org.xyzh.common.vo.ResourcePermissionVO; + +import java.util.List; + +/** + * @description 资源权限Mapper接口 + * @filename ResourcePermissionMapper.java + * @author yslg + * @copyright xyzh + * @since 2025-10-29 + */ +@Mapper +public interface ResourcePermissionMapper extends BaseMapper { + + /** + * @description 插入资源权限 + * @param permission 资源权限对象 + * @return int 插入结果 + * @author yslg + * @since 2025-10-29 + */ + int insertResourcePermission(TbResourcePermission permission); + + /** + * @description 批量插入资源权限 + * @param permissions 资源权限列表 + * @return int 插入结果 + * @author yslg + * @since 2025-10-29 + */ + int batchInsertResourcePermission(@Param("list") List permissions); + +} + diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/RoleMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/RoleMapper.java index c89af2d..fa23618 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/RoleMapper.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/RoleMapper.java @@ -4,7 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.dto.role.TbSysRole; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; import java.util.List; @@ -65,11 +65,11 @@ public interface RoleMapper extends BaseMapper { /** * @description 根据用户ID查询角色列表 * @param userId 用户ID - * @return List 部门角色列表 + * @return List 部门角色列表 * @author yslg * @since 2025-09-28 */ - List selectDeptRolesByUserId(@Param("userId") String userId); + List selectDeptRolesByUserId(@Param("userId") String userId); /** * @description 根据角色编码查询角色 diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/UserDeptRoleMapper.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/UserDeptRoleMapper.java index 5ffc6a0..fcd7ccf 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/UserDeptRoleMapper.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/mapper/UserDeptRoleMapper.java @@ -5,19 +5,38 @@ import java.util.List; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.xyzh.common.dto.user.TbSysUserDeptRole; +import org.xyzh.common.vo.UserDeptRoleVO; import com.baomidou.mybatisplus.core.mapper.BaseMapper; @Mapper public interface UserDeptRoleMapper extends BaseMapper { /** - * @description 查询用户部门角色 - * @param userId 用户ID - * @return List 用户部门角色列表 + * @description 查询用户部门角色(包含名称) + * @param filter 过滤条件 + * @return List 用户部门角色列表 * @author yslg * @since 2025-10-09 */ - List selectByFilter(TbSysUserDeptRole filter); + List selectByFilter(TbSysUserDeptRole filter); + + /** + * @description 删除指定用户的所有部门角色绑定 + * @param userID 用户ID + * @return int 影响行数 + * @author yslg + * @since 2025-10-09 + */ + int deleteUserDeptRole(String userID); + + /** + * @description 批量删除多个用户的部门角色绑定 + * @param userIds 用户ID列表 + * @return int 影响行数 + * @author yslg + * @since 2025-10-29 + */ + int deleteUserDeptRoleByUserIds(@Param("userIds") List userIds); /** * @description 绑定用户 diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/permission/service/impl/SysResourcePermissionServiceImpl.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/permission/service/impl/SysResourcePermissionServiceImpl.java new file mode 100644 index 0000000..0b35f55 --- /dev/null +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/permission/service/impl/SysResourcePermissionServiceImpl.java @@ -0,0 +1,173 @@ +package org.xyzh.system.permission.service.impl; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.xyzh.api.system.permission.ResourcePermissionService; +import org.xyzh.common.core.domain.ResultDomain; +import org.xyzh.common.dto.dept.TbSysDeptRole; +import org.xyzh.common.dto.permission.TbResourcePermission; +import org.xyzh.common.vo.UserDeptRoleVO; +import org.xyzh.system.mapper.DeptRoleMapper; +import org.xyzh.system.mapper.ResourcePermissionMapper; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @description 资源权限控制服务实现类 + * @filename SysResourcePermissionServiceImpl.java + * @author yslg + * @copyright xyzh + * @since 2025-10-29 + */ +@Service +public class SysResourcePermissionServiceImpl implements ResourcePermissionService { + + private static final Logger logger = LoggerFactory.getLogger(SysResourcePermissionServiceImpl.class); + + @Autowired + private ResourcePermissionMapper resourcePermissionMapper; + + @Autowired + private DeptRoleMapper deptRoleMapper; + + @Transactional(rollbackFor = Exception.class) + @Override + public ResultDomain createResourcePermission(Integer resource_type, String resource_id, + UserDeptRoleVO userDeptRole) { + ResultDomain resultDomain = new ResultDomain<>(); + + Set resourcePermissions = new HashSet<>(); + Date now = new Date(); + + // 判断是否为root_department的superadmin + if (isRootSuperAdmin(userDeptRole)) { + // root_department的superadmin创建资源:创建全局读权限 + logger.info("用户为root_department的superadmin,创建全局读权限,所有用户(包括未来新增的部门/角色)都能访问"); + + // 1. 创建全局读权限(所有人可读,包括未来新增的部门/角色) + TbResourcePermission globalReadPermission = createGlobalReadPermission(resource_type, resource_id, userDeptRole.getUserID()); + globalReadPermission.setCreateTime(now); + resourcePermissions.add(globalReadPermission); + + // 2. 为superadmin创建全权限(读写执行) + TbResourcePermission superAdminPermission = createSuperAdminPermission(resource_type, resource_id, userDeptRole.getUserID()); + superAdminPermission.setCreateTime(now); + resourcePermissions.add(superAdminPermission); + + } else { + // 普通用户创建资源:为父部门管理员+当前部门创建权限 + logger.info("普通用户创建资源,为父部门管理员和当前部门创建权限(子部门通过递归查询自动继承)"); + + // 1. 为父部门的管理员角色创建精确权限(dept + admin role) + List parentDeptRoles = deptRoleMapper.selectParentDeptAdmin(userDeptRole); + for (TbSysDeptRole deptRole : parentDeptRoles) { + TbResourcePermission temp = createResourcePermission(resource_type, resource_id, deptRole.getDeptID(), deptRole.getRoleID()); + temp.setCreateTime(now); + resourcePermissions.add(temp); + } + + // 2. 为当前用户所在部门创建部门级权限(dept + NULL) + // 所有角色都能访问(包括未来新增的角色) + // 所有子部门都能访问(包括未来新增的子部门,通过查询时递归实现) + TbResourcePermission deptPermission = createDeptLevelPermission(resource_type, resource_id, userDeptRole.getDeptID()); + deptPermission.setCreateTime(now); + resourcePermissions.add(deptPermission); + + // 3. 为superadmin创建全权限(确保超级管理员始终可以管理所有资源) + TbResourcePermission superAdminPermission = createSuperAdminPermission(resource_type, resource_id, userDeptRole.getUserID()); + superAdminPermission.setCreateTime(now); + resourcePermissions.add(superAdminPermission); + } + + List resourcePermissionsList = new ArrayList<>(resourcePermissions); + int result = resourcePermissionMapper.batchInsertResourcePermission(resourcePermissionsList); + if (result > 0) { + resultDomain.success("创建资源权限成功", resourcePermissionsList); + return resultDomain; + } else { + resultDomain.fail("创建资源权限失败"); + return resultDomain; + } + + } + + /** + * 判断是否为root_department的superadmin + */ + private boolean isRootSuperAdmin(UserDeptRoleVO userDeptRole) { + return "root_department".equals(userDeptRole.getDeptID()) + && "superadmin".equals(userDeptRole.getRoleID()); + } + + /** + * 创建全局读权限(所有人可读,包括未来新增的部门/角色) + */ + private TbResourcePermission createGlobalReadPermission(Integer resource_type, String resource_id, String creatorID) { + TbResourcePermission resourcePermission = new TbResourcePermission(); + resourcePermission.setResourceType(resource_type); + resourcePermission.setResourceID(resource_id); + resourcePermission.setCreator(creatorID); + resourcePermission.setDeptID(null); // NULL表示不限制部门 + resourcePermission.setRoleID(null); // NULL表示不限制角色 + resourcePermission.setCanRead(true); + resourcePermission.setCanWrite(false); + resourcePermission.setCanExecute(false); + return resourcePermission; + } + + /** + * 创建精确部门角色权限(dept + role,读权限) + */ + private TbResourcePermission createResourcePermission(Integer resource_type, String resource_id, String deptID, String roleID) { + TbResourcePermission resourcePermission = new TbResourcePermission(); + resourcePermission.setResourceType(resource_type); + resourcePermission.setResourceID(resource_id); + resourcePermission.setDeptID(deptID); + resourcePermission.setRoleID(roleID); + resourcePermission.setCanRead(true); + resourcePermission.setCanWrite(false); + resourcePermission.setCanExecute(false); + return resourcePermission; + } + + /** + * 创建部门级权限(dept + NULL,该部门所有角色可读,包括未来新增的角色) + */ + private TbResourcePermission createDeptLevelPermission(Integer resource_type, String resource_id, String deptID) { + TbResourcePermission resourcePermission = new TbResourcePermission(); + resourcePermission.setResourceType(resource_type); + resourcePermission.setResourceID(resource_id); + resourcePermission.setDeptID(deptID); + resourcePermission.setRoleID(null); // NULL表示该部门所有角色都能访问 + resourcePermission.setCanRead(true); + resourcePermission.setCanWrite(false); + resourcePermission.setCanExecute(false); + return resourcePermission; + } + + /** + * 创建超级管理员全权限(读写执行) + */ + private TbResourcePermission createSuperAdminPermission(Integer resource_type, String resource_id, String creatorID) { + TbResourcePermission resourcePermission = new TbResourcePermission(); + resourcePermission.setResourceType(resource_type); + resourcePermission.setResourceID(resource_id); + resourcePermission.setCreator(creatorID); + resourcePermission.setDeptID("root_department"); + resourcePermission.setRoleID("superadmin"); + resourcePermission.setCanRead(true); + resourcePermission.setCanWrite(true); + resourcePermission.setCanExecute(true); + return resourcePermission; + } + + +} + diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/role/service/impl/SysRoleServiceImpl.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/role/service/impl/SysRoleServiceImpl.java index a6fc8d9..d266967 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/role/service/impl/SysRoleServiceImpl.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/role/service/impl/SysRoleServiceImpl.java @@ -9,7 +9,7 @@ import org.xyzh.common.core.domain.ResultDomain; import org.xyzh.common.dto.permission.TbSysPermission; import org.xyzh.common.dto.role.TbSysRole; import org.xyzh.common.utils.IDUtils; -import org.xyzh.common.vo.DeptRoleVO; +import org.xyzh.common.vo.UserDeptRoleVO; import org.xyzh.system.mapper.RolePermissionMapper; import org.xyzh.system.mapper.RoleMapper; import org.xyzh.system.mapper.UserDeptRoleMapper; @@ -257,8 +257,8 @@ public class SysRoleServiceImpl implements SysRoleService { } @Override - public ResultDomain getDeptRolesByUserId(String userId) { - ResultDomain resultDomain = new ResultDomain<>(); + public ResultDomain getDeptRolesByUserId(String userId) { + ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始根据用户ID查询部门角色列表:{}", userId); @@ -267,7 +267,7 @@ public class SysRoleServiceImpl implements SysRoleService { return resultDomain; } - List roles = roleMapper.selectDeptRolesByUserId(userId); + List roles = roleMapper.selectDeptRolesByUserId(userId); logger.info("根据用户ID查询部门角色列表完成,共找到{}个部门角色", roles.size()); resultDomain.success("查询成功", roles); diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/user/service/impl/SysUserServiceImpl.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/user/service/impl/SysUserServiceImpl.java index f85cd54..ed6d121 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/user/service/impl/SysUserServiceImpl.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/user/service/impl/SysUserServiceImpl.java @@ -3,6 +3,7 @@ package org.xyzh.system.user.service.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; @@ -46,6 +47,8 @@ public class SysUserServiceImpl implements SysUserService { @Autowired private UserDeptRoleMapper userDeptRoleMapper; + @Autowired + private PasswordEncoder passwordEncoder; @Transactional @Override @@ -69,18 +72,18 @@ public class SysUserServiceImpl implements SysUserService { userInfo.setUserID(user.getID()); userInfo.setCreateTime(now); userInfo.setAvatar("default"); - + TbSysUserDeptRole userDeptRole = new TbSysUserDeptRole(); userDeptRole.setUserID(user.getID()); - userDeptRole.setDeptID("-1"); + userDeptRole.setDeptID("default_department"); userDeptRole.setRoleID("freedom"); userDeptRole.setCreateTime(now); - + user.setPassword(passwordEncoder.encode(user.getPassword())); userMapper.insertUser(user); userInfoMapper.insertUserInfo(userInfo); userDeptRoleMapper.bindUser(Arrays.asList(userDeptRole)); resultDomain.success("注册用户成功", user); - return resultDomain; + return resultDomain; } catch (Exception e) { logger.error("注册用户失败:{}", user.getUsername(), e); resultDomain.fail("注册用户失败:" + e.getMessage()); @@ -94,7 +97,7 @@ public class SysUserServiceImpl implements SysUserService { try { logger.info("开始检查用户是否已存在:{}", user.getUsername()); - + } catch (Exception e) { logger.error("检查用户是否已存在失败:{}", user.getUsername(), e); resultDomain.fail("检查用户是否已存在失败:" + e.getMessage()); @@ -109,14 +112,14 @@ public class SysUserServiceImpl implements SysUserService { try { logger.info("开始查询所有用户"); - + TbSysUser filter = new TbSysUser(); List users = userMapper.selectByFilter(filter); - + logger.info("查询所有用户完成,共找到{}个用户", users.size()); resultDomain.success("查询成功", users); return resultDomain; - + } catch (Exception e) { logger.error("查询所有用户失败", e); resultDomain.fail("查询用户失败:" + e.getMessage()); @@ -129,28 +132,28 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始根据ID查询用户:{}", userId); - + if (!StringUtils.hasText(userId)) { resultDomain.fail("用户ID不能为空"); return resultDomain; } - + TbSysUser filter = new TbSysUser(); filter.setID(userId); filter.setDeleted(false); - + List users = userMapper.selectByFilter(filter); - + if (users.isEmpty()) { logger.warn("未找到用户:{}", userId); resultDomain.fail("未找到指定用户"); return resultDomain; } - + logger.info("根据ID查询用户完成:{}", userId); resultDomain.success("查询成功", users.get(0)); return resultDomain; - + } catch (Exception e) { logger.error("根据ID查询用户失败:{}", userId, e); resultDomain.fail("查询用户失败:" + e.getMessage()); @@ -163,24 +166,24 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始根据用户名查询用户:{}", username); - + if (!StringUtils.hasText(username)) { resultDomain.fail("用户名不能为空"); return resultDomain; } - + TbSysUser user = userMapper.selectByUsername(username); - + if (user == null) { logger.warn("未找到用户:{}", username); resultDomain.fail("未找到指定用户"); return resultDomain; } - + logger.info("根据用户名查询用户完成:{}", username); resultDomain.success("查询成功", user); return resultDomain; - + } catch (Exception e) { logger.error("根据用户名查询用户失败:{}", username, e); resultDomain.fail("查询用户失败:" + e.getMessage()); @@ -193,35 +196,35 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始根据过滤条件查询用户:{}", filter); - + if (filter == null) { resultDomain.fail("过滤条件不能为空"); return resultDomain; } - + // 检查至少有一个查询条件 // boolean hasFilter = StringUtils.hasText(filter.getID()) || - // StringUtils.hasText(filter.getUsername()) || - // StringUtils.hasText(filter.getEmail()) || - // StringUtils.hasText(filter.getPhone()); - + // StringUtils.hasText(filter.getUsername()) || + // StringUtils.hasText(filter.getEmail()) || + // StringUtils.hasText(filter.getPhone()); + // if (!hasFilter) { - // resultDomain.fail("至少需要提供一个查询条件"); - // return resultDomain; + // resultDomain.fail("至少需要提供一个查询条件"); + // return resultDomain; // } - + List users = userMapper.selectByFilter(filter); - + if (users.isEmpty()) { logger.warn("未找到符合条件的用户:{}", filter); resultDomain.fail("未找到指定用户"); return resultDomain; } - + logger.info("根据过滤条件查询用户完成:{}", filter); resultDomain.success("查询成功", users); return resultDomain; - + } catch (Exception e) { logger.error("根据过滤条件查询用户失败:{}", filter, e); resultDomain.fail("查询用户失败:" + e.getMessage()); @@ -246,13 +249,13 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始创建用户:{}", user.getUsername()); - + // 参数校验 if (!StringUtils.hasText(user.getUsername())) { resultDomain.fail("用户名不能为空"); return resultDomain; } - + // 检查用户名是否已存在 ResultDomain checkResult = checkUsernameExists(user.getUsername(), null); if (!checkResult.isSuccess()) { @@ -263,7 +266,7 @@ public class SysUserServiceImpl implements SysUserService { resultDomain.fail("用户名已存在"); return resultDomain; } - + // 检查邮箱是否已存在 if (StringUtils.hasText(user.getEmail())) { ResultDomain emailCheckResult = checkEmailExists(user.getEmail(), null); @@ -276,7 +279,7 @@ public class SysUserServiceImpl implements SysUserService { return resultDomain; } } - + // 设置基础信息 user.setID(IDUtils.generateID()); user.setCreateTime(new Date()); @@ -284,10 +287,9 @@ public class SysUserServiceImpl implements SysUserService { if (user.getStatus() == null) { user.setStatus(1); // 默认启用状态 } - // 插入数据库 ResultDomain result = registerUser(user); - + if (result.isSuccess()) { logger.info("创建用户成功:{}", user.getUsername()); resultDomain.success("创建用户成功", result.getData()); @@ -296,7 +298,7 @@ public class SysUserServiceImpl implements SysUserService { logger.warn("创建用户失败:{}", user.getUsername()); resultDomain.fail("创建用户失败:" + result.getMessage()); } - + return resultDomain; } catch (Exception e) { logger.error("创建用户异常:{}", user.getUsername(), e); @@ -308,150 +310,159 @@ public class SysUserServiceImpl implements SysUserService { @Override public ResultDomain updateUser(TbSysUser user) { ResultDomain resultDomain = new ResultDomain<>(); - try { - logger.info("开始更新用户:{}", user.getID()); - - // 参数校验 - if (!StringUtils.hasText(user.getID())) { - resultDomain.fail("用户ID不能为空"); - return resultDomain; - } - if (!StringUtils.hasText(user.getUsername())) { - resultDomain.fail("用户名不能为空"); - return resultDomain; - } - - // 检查用户是否存在 - ResultDomain existResult = getUserById(user.getID()); - if (!existResult.isSuccess()) { - resultDomain.fail(existResult.getMessage()); - return resultDomain; - } - - // 检查用户名是否已存在(排除自身) - ResultDomain checkResult = checkUsernameExists(user.getUsername(), user.getID()); - if (!checkResult.isSuccess()) { - resultDomain.fail(checkResult.getMessage()); - return resultDomain; - } - if (checkResult.getData()) { - resultDomain.fail("用户名已存在"); - return resultDomain; - } - - // 检查邮箱是否已存在(排除自身) - if (StringUtils.hasText(user.getEmail())) { - ResultDomain emailCheckResult = checkEmailExists(user.getEmail(), user.getID()); - if (!emailCheckResult.isSuccess()) { - resultDomain.fail(emailCheckResult.getMessage()); - return resultDomain; - } - if (emailCheckResult.getData()) { - resultDomain.fail("邮箱已存在"); - return resultDomain; - } - } - - // 设置更新时间 - user.setUpdateTime(new Date()); - - // 更新数据库 - int result = userMapper.updateUser(user); - - if (result > 0) { - logger.info("更新用户成功:{}", user.getID()); - resultDomain.success("更新用户成功", user); - return resultDomain; - } else { - logger.warn("更新用户失败:{}", user.getID()); - resultDomain.fail("更新用户失败"); - return resultDomain; - } - - } catch (Exception e) { - logger.error("更新用户异常:{}", user.getID(), e); - resultDomain.fail("更新用户失败:" + e.getMessage()); + + logger.info("开始更新用户:{}", user.getID()); + + // 参数校验 + if (!StringUtils.hasText(user.getID())) { + resultDomain.fail("用户ID不能为空"); return resultDomain; } + + // 检查用户是否存在 + ResultDomain existResult = getUserById(user.getID()); + if (!existResult.isSuccess()) { + resultDomain.fail(existResult.getMessage()); + return resultDomain; + } + + // 检查邮箱是否已存在(排除自身) + if (StringUtils.hasText(user.getEmail())) { + ResultDomain emailCheckResult = checkEmailExists(user.getEmail(), user.getID()); + if (!emailCheckResult.isSuccess()) { + resultDomain.fail(emailCheckResult.getMessage()); + return resultDomain; + } + if (emailCheckResult.getData()) { + resultDomain.fail("邮箱已存在"); + return resultDomain; + } + } + + // 设置更新时间 + user.setUpdateTime(new Date()); + + // 更新数据库 + int result = userMapper.updateUser(user); + + if (result > 0) { + logger.info("更新用户成功:{}", user.getID()); + resultDomain.success("更新用户成功", user); + return resultDomain; + } else { + logger.warn("更新用户失败:{}", user.getID()); + resultDomain.fail("更新用户失败"); + return resultDomain; + } + } @Override public ResultDomain deleteUser(String userId) { ResultDomain resultDomain = new ResultDomain<>(); + + logger.info("开始删除用户:{}", userId); + + if (!StringUtils.hasText(userId)) { + resultDomain.fail("用户ID不能为空"); + return resultDomain; + } + + // 检查用户是否存在 + ResultDomain existResult = getUserById(userId); + if (!existResult.isSuccess()) { + resultDomain.fail(existResult.getMessage()); + return resultDomain; + } + + // 逻辑删除 + // TbSysUser user = existResult.getData(); + // user.setDeleted(true); + // user.setDeleteTime(new Date()); + + // int result = userMapper.updateUser(user); + int result = userMapper.deleteUser(userId); + + if (result > 0) { + logger.info("删除用户成功:{}", userId); + resultDomain.success("删除用户成功", new TbSysUser()); + return resultDomain; + } else { + logger.warn("删除用户失败:{}", userId); + resultDomain.fail("删除用户失败"); + return resultDomain; + } + + } + + @Override + public ResultDomain getBindUserDeptRoleList(TbSysUserDeptRole filter) { + ResultDomain resultDomain = new ResultDomain<>(); try { - logger.info("开始删除用户:{}", userId); - - if (!StringUtils.hasText(userId)) { - resultDomain.fail("用户ID不能为空"); - return resultDomain; - } - - // 检查用户是否存在 - ResultDomain existResult = getUserById(userId); - if (!existResult.isSuccess()) { - resultDomain.fail(existResult.getMessage()); - return resultDomain; - } - - // 逻辑删除 - TbSysUser user = existResult.getData(); - user.setDeleted(true); - user.setDeleteTime(new Date()); - - int result = userMapper.updateUser(user); - - if (result > 0) { - logger.info("删除用户成功:{}", userId); - resultDomain.success("删除用户成功", user); - return resultDomain; - } else { - logger.warn("删除用户失败:{}", userId); - resultDomain.fail("删除用户失败"); - return resultDomain; - } - + logger.info("开始查询用户部门角色绑定列表"); + List userDeptRoles = userDeptRoleMapper.selectByFilter(filter); + logger.info("查询用户部门角色绑定列表完成,共找到{}条记录", userDeptRoles.size()); + resultDomain.success("查询成功", userDeptRoles); + return resultDomain; } catch (Exception e) { - logger.error("删除用户异常:{}", userId, e); - resultDomain.fail("删除用户失败:" + e.getMessage()); + logger.error("查询用户部门角色绑定列表失败", e); + resultDomain.fail("查询用户部门角色绑定列表失败:" + e.getMessage()); return resultDomain; } } - @Override - public ResultDomain getBindUserDeptRoleList(TbSysUserDeptRole filter) { - ResultDomain resultDomain = new ResultDomain<>(); - List userDeptRoles = userDeptRoleMapper.selectByFilter(filter); - resultDomain.success("查询成功", userDeptRoles); - return resultDomain; - } - - @Transactional + @Transactional(rollbackFor = Exception.class) @Override public ResultDomain bindUserDeptRole(UserDeptRoleVO userDeptRoleVO) { ResultDomain resultDomain = new ResultDomain<>(); TbSysUser currentUser = LoginUtil.getCurrentUser(); - try { + + + // 收集所有用户ID + List userIds = new ArrayList<>(); + for (TbSysUser user : userDeptRoleVO.getUsers()) { + userIds.add(user.getID()); + } + + logger.info("准备为 {} 个用户绑定部门角色", userIds.size()); + + // 批量删除所有涉及用户的旧绑定关系(物理删除,包括软删除的记录) + int deleteCount = userDeptRoleMapper.deleteUserDeptRoleByUserIds(userIds); + if (deleteCount <= 0) { + resultDomain.fail("批量删除旧绑定记录失败:没有记录被删除"); + return resultDomain; + } + + // 准备新的绑定数据 List userDeptRoles = new ArrayList<>(); - logger.info("开始绑定用户部门角色:{}", userDeptRoleVO.getID()); Date now = new Date(); for (TbSysUser user : userDeptRoleVO.getUsers()) { - for(TbSysUserDeptRole userDeptRole : userDeptRoleVO.getUserDeptRoles()) { - userDeptRole.setID(IDUtils.generateID()); - userDeptRole.setUserID(user.getID()); - userDeptRole.setCreateTime(now); - userDeptRole.setCreator(currentUser.getID()); - userDeptRoles.add(userDeptRole); + for (TbSysUserDeptRole userDeptRole : userDeptRoleVO.getUserDeptRoles()) { + TbSysUserDeptRole newUserDeptRole = new TbSysUserDeptRole(); + newUserDeptRole.setID(IDUtils.generateID()); + newUserDeptRole.setUserID(user.getID()); + newUserDeptRole.setDeptID(userDeptRole.getDeptID()); + newUserDeptRole.setRoleID(userDeptRole.getRoleID()); + newUserDeptRole.setCreateTime(now); + newUserDeptRole.setCreator(currentUser.getID()); + userDeptRoles.add(newUserDeptRole); } } - userDeptRoleMapper.bindUser(userDeptRoles); - - } catch (Exception e) { - logger.error("绑定用户部门角色异常:{}", userDeptRoleVO.getID(), e); - resultDomain.fail("绑定用户部门角色失败:" + e.getMessage()); + logger.info("准备插入 {} 条新绑定记录", userDeptRoles.size()); + + // 插入新的绑定关系 + int result = userDeptRoleMapper.bindUser(userDeptRoles); + logger.info("成功插入 {} 条绑定记录", result); + + if (result > 0) { + resultDomain.success("绑定用户部门角色成功", userDeptRoleVO); + } else { + resultDomain.fail("绑定用户部门角色失败:没有记录被插入"); + } + return resultDomain; - } - return resultDomain; + } @Transactional @@ -459,30 +470,26 @@ public class SysUserServiceImpl implements SysUserService { public ResultDomain unbindUserDeptRole(UserDeptRoleVO userDeptRoleVO) { ResultDomain resultDomain = new ResultDomain<>(); TbSysUser currentUser = LoginUtil.getCurrentUser(); - try { - List userDeptRoles = new ArrayList<>(); - logger.info("开始解绑用户部门角色:{}", userDeptRoleVO.getID()); - Date now = new Date(); - for(TbSysUser user:userDeptRoleVO.getUsers()) { - for(TbSysUserDeptRole userDeptRole : userDeptRoleVO.getUserDeptRoles()) { - userDeptRole.setUserID(user.getID()); - userDeptRoles.add(userDeptRole); - } + + List userDeptRoles = new ArrayList<>(); + logger.info("开始解绑用户部门角色:{}", userDeptRoleVO.getDeptID()); + Date now = new Date(); + for (TbSysUser user : userDeptRoleVO.getUsers()) { + for (TbSysUserDeptRole userDeptRole : userDeptRoleVO.getUserDeptRoles()) { + userDeptRole.setUserID(user.getID()); + userDeptRoles.add(userDeptRole); } - int result = userDeptRoleMapper.unbindUser(userDeptRoles); - if(result > 0) { - logger.info("解绑用户部门角色成功:{}", userDeptRoleVO.getID()); - resultDomain.success("解绑用户部门角色成功", userDeptRoleVO); - } else { - logger.warn("解绑用户部门角色失败:{}", userDeptRoleVO.getID()); - resultDomain.fail("解绑用户部门角色失败"); - } - return resultDomain; - } catch (Exception e) { - logger.error("解绑用户部门角色异常:{}", userDeptRoleVO.getID(), e); - resultDomain.fail("解绑用户部门角色失败:" + e.getMessage()); + } + int result = userDeptRoleMapper.unbindUser(userDeptRoles); + if (result > 0) { + logger.info("解绑用户部门角色成功:{}", userDeptRoleVO.getDeptID()); + resultDomain.success("解绑用户部门角色成功", userDeptRoleVO); + } else { + logger.warn("解绑用户部门角色失败:{}", userDeptRoleVO.getDeptID()); + resultDomain.fail("解绑用户部门角色失败"); } return resultDomain; + } @Override @@ -490,7 +497,7 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("检查用户名是否存在:{}", username); - + if (!StringUtils.hasText(username)) { resultDomain.fail("用户名不能为空"); return resultDomain; @@ -502,14 +509,14 @@ public class SysUserServiceImpl implements SysUserService { } filter.setUsername(username); filter.setDeleted(false); - + long count = userMapper.selectByFilter(filter).size(); boolean exists = count > 0; - + logger.info("用户名存在性检查完成:{},存在:{}", username, exists); resultDomain.success("检查完成", exists); return resultDomain; - + } catch (Exception e) { logger.error("检查用户名存在性失败:{}", username, e); resultDomain.fail("检查失败:" + e.getMessage()); @@ -522,27 +529,27 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("检查邮箱是否存在:{}", email); - + if (!StringUtils.hasText(email)) { resultDomain.fail("邮箱不能为空"); return resultDomain; } - + TbSysUser filter = new TbSysUser(); filter.setEmail(email); filter.setDeleted(false); - + if (StringUtils.hasText(excludeId)) { filter.setID(excludeId); } - + long count = userMapper.selectByFilter(filter).size(); boolean exists = count > 0; - + logger.info("邮箱存在性检查完成:{},存在:{}", email, exists); resultDomain.success("检查完成", exists); return resultDomain; - + } catch (Exception e) { logger.error("检查邮箱存在性失败:{}", email, e); resultDomain.fail("检查失败:" + e.getMessage()); @@ -555,13 +562,13 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始搜索用户,用户名:{},邮箱:{},状态:{}", username, email, status); - + List users = userMapper.selectUserList(username, email, status); - + logger.info("搜索用户完成,共找到{}个用户", users.size()); resultDomain.success("搜索成功", users); return resultDomain; - + } catch (Exception e) { logger.error("搜索用户失败", e); resultDomain.fail("搜索用户失败:" + e.getMessage()); @@ -574,30 +581,30 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始修改用户状态:{},状态:{}", userId, status); - + if (!StringUtils.hasText(userId)) { resultDomain.fail("用户ID不能为空"); return resultDomain; } - + if (status == null) { resultDomain.fail("用户状态不能为空"); return resultDomain; } - + // 检查用户是否存在 ResultDomain existResult = getUserById(userId); if (!existResult.isSuccess()) { resultDomain.fail(existResult.getMessage()); return resultDomain; } - + TbSysUser user = existResult.getData(); user.setStatus(status); user.setUpdateTime(new Date()); - + int result = userMapper.updateUser(user); - + if (result > 0) { logger.info("修改用户状态成功:{}", userId); resultDomain.success("修改用户状态成功", user); @@ -607,7 +614,7 @@ public class SysUserServiceImpl implements SysUserService { resultDomain.fail("修改用户状态失败"); return resultDomain; } - + } catch (Exception e) { logger.error("修改用户状态异常:{}", userId, e); resultDomain.fail("修改用户状态失败:" + e.getMessage()); @@ -620,31 +627,31 @@ public class SysUserServiceImpl implements SysUserService { ResultDomain resultDomain = new ResultDomain<>(); try { logger.info("开始重置用户密码:{}", userId); - + if (!StringUtils.hasText(userId)) { resultDomain.fail("用户ID不能为空"); return resultDomain; } - + if (!StringUtils.hasText(newPassword)) { resultDomain.fail("新密码不能为空"); return resultDomain; } - + // 检查用户是否存在 ResultDomain existResult = getUserById(userId); if (!existResult.isSuccess()) { resultDomain.fail(existResult.getMessage()); return resultDomain; } - + TbSysUser user = existResult.getData(); // TODO: 这里应该对密码进行加密处理 user.setPassword(newPassword); user.setUpdateTime(new Date()); - + int result = userMapper.updateUser(user); - + if (result > 0) { logger.info("重置用户密码成功:{}", userId); resultDomain.success("重置密码成功", user); @@ -654,7 +661,7 @@ public class SysUserServiceImpl implements SysUserService { resultDomain.fail("重置密码失败"); return resultDomain; } - + } catch (Exception e) { logger.error("重置用户密码异常:{}", userId, e); resultDomain.fail("重置密码失败:" + e.getMessage()); @@ -662,7 +669,6 @@ public class SysUserServiceImpl implements SysUserService { } } - // ----------------用户信息相关-------------------------------- @Override @@ -682,11 +688,11 @@ public class SysUserServiceImpl implements SysUserService { resultDomain.fail("未找到指定用户信息"); return resultDomain; } - + logger.info("根据ID查询用户信息完成:{}", userId); resultDomain.success("查询成功", userInfo); return resultDomain; - + } catch (Exception e) { logger.error("根据ID查询用户信息失败:{}", userId, e); resultDomain.fail("查询用户信息失败:" + e.getMessage()); @@ -705,9 +711,9 @@ public class SysUserServiceImpl implements SysUserService { return resultDomain; } userInfo.setUpdateTime(new Date()); - + int result = userMapper.updateUserInfo(userInfo); - + if (result > 0) { logger.info("更新用户信息成功:{}", userInfo.getUserID()); TbSysUserInfo newUserInfo = userMapper.selectUserInfoById(userInfo.getUserID()); @@ -718,7 +724,7 @@ public class SysUserServiceImpl implements SysUserService { resultDomain.fail("更新用户信息失败"); return resultDomain; } - + } catch (Exception e) { logger.error("更新用户信息异常:{}", userInfo.getUserID(), e); resultDomain.fail("更新用户信息失败:" + e.getMessage()); @@ -726,7 +732,6 @@ public class SysUserServiceImpl implements SysUserService { } } - @Override public ResultDomain getUserInfoTotal(String userId) { ResultDomain resultDomain = new ResultDomain<>(); diff --git a/schoolNewsServ/system/src/main/java/org/xyzh/system/utils/LoginUtil.java b/schoolNewsServ/system/src/main/java/org/xyzh/system/utils/LoginUtil.java index 3c153d3..4f9024c 100644 --- a/schoolNewsServ/system/src/main/java/org/xyzh/system/utils/LoginUtil.java +++ b/schoolNewsServ/system/src/main/java/org/xyzh/system/utils/LoginUtil.java @@ -1,5 +1,8 @@ package org.xyzh.system.utils; +import java.util.List; +import java.util.stream.Collectors; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.xyzh.common.core.domain.LoginDomain; @@ -9,6 +12,7 @@ import org.xyzh.common.dto.user.TbSysUserInfo; import org.xyzh.common.redis.service.RedisService; import org.xyzh.common.utils.NonUtils; import org.xyzh.common.utils.ServletUtil; +import org.xyzh.common.vo.UserDeptRoleVO; /** * @description LoginUtil.java文件描述 登录信息工具类 @@ -72,6 +76,26 @@ public class LoginUtil { } } + /** + * 获取当前用户的部门角色列表(扁平化) + * UserDeptRoleVO已包含deptPath字段,用于高效的权限继承判断 + * @return 部门角色列表 + */ + public static List getCurrentDeptRole() { + LoginDomain loginDomain = getCurrentLoginDomain(); + List roles = loginDomain.getRoles(); + + // UserDeptRoleVO应该在登录时就已经扁平化填充好了 + // 这里只需要确保userID被设置(如果还没设置的话) + String userId = loginDomain.getUser().getID(); + return roles.stream().map(item -> { + if (item.getUserID() == null) { + item.setUserID(userId); + } + return item; + }).collect(Collectors.toList()); + } + /** * @description 获取当前登录用户 * @return TbSysUser 当前登录用户,未登录返回null diff --git a/schoolNewsServ/system/src/main/resources/mapper/DepartmentMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/DepartmentMapper.xml index 385efbd..a1a1351 100644 --- a/schoolNewsServ/system/src/main/resources/mapper/DepartmentMapper.xml +++ b/schoolNewsServ/system/src/main/resources/mapper/DepartmentMapper.xml @@ -8,6 +8,7 @@ + @@ -19,7 +20,7 @@ - id, dept_id, name, parent_id, description, creator, updater, + id, dept_id, name, parent_id, dept_path, description, creator, updater, create_time, update_time, delete_time, deleted @@ -39,14 +40,54 @@ - + + + INNER JOIN tb_resource_permission rp ON d.dept_id = rp.resource_id + AND rp.resource_type = 4 + AND rp.deleted = 0 + AND rp.can_read = 1 + AND ( + -- 全局权限:所有用户可访问 + (rp.dept_id IS NULL AND rp.role_id IS NULL) + + OR EXISTS ( + SELECT 1 + FROM ( + + SELECT #{udr.deptID} AS dept_id, #{udr.deptPath} AS dept_path, #{udr.roleID} AS role_id + + ) user_roles + LEFT JOIN tb_sys_dept perm_dept ON perm_dept.dept_id = rp.dept_id AND perm_dept.deleted = 0 + WHERE + -- 部门级权限:当前部门或父部门(通过dept_path判断继承关系) + (rp.role_id IS NULL AND rp.dept_id IS NOT NULL + AND user_roles.dept_path LIKE CONCAT(perm_dept.dept_path, '%')) + -- 角色级权限:跨部门的角色权限 + OR (rp.dept_id IS NULL AND rp.role_id = user_roles.role_id) + -- 精确权限:特定部门的特定角色 + OR (rp.dept_id = user_roles.dept_id AND rp.role_id = user_roles.role_id) + ) + + ) + + + @@ -95,64 +136,63 @@ create_time ASC - - - UPDATE tb_sys_dept - SET deleted = 1, - delete_time = NOW(), - updater = #{updater} - WHERE deleted = 0 - AND dept_id IN - - #{deptId} - - - - - - INSERT INTO tb_sys_dept - - id, - dept_id, - parent_id, - name, - description, - creator, - create_time, + + + INSERT INTO tb_sys_dept ( + id, + dept_id, + name, + parent_id, + dept_path, + description, + creator, + create_time, deleted - - - #{id}, - #{deptID}, - #{parentID}, - #{name}, - #{description}, - #{creator}, - #{createTime}, - 0 - + ) VALUES ( + #{id}, + #{deptID}, + #{name}, + #{parentID}, + #{deptPath}, + #{description}, + #{creator}, + #{createTime}, + #{deleted} + ) - - + + UPDATE tb_sys_dept - dept_id = #{deptID}, - parent_id = #{parentID}, - name = #{name}, - description = #{description}, - updater = #{updater}, + + name = #{name}, + + + parent_id = #{parentID}, + + + dept_path = #{deptPath}, + + + description = #{description}, + + + updater = #{updater}, + update_time = NOW() WHERE dept_id = #{deptID} AND deleted = 0 - - - UPDATE tb_sys_dept + + + UPDATE tb_sys_dept SET deleted = 1, delete_time = NOW() + + , updater = #{updater} + WHERE dept_id = #{deptID} AND deleted = 0 - diff --git a/schoolNewsServ/system/src/main/resources/mapper/DeptRoleMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/DeptRoleMapper.xml index bc9f9c0..32db2c7 100644 --- a/schoolNewsServ/system/src/main/resources/mapper/DeptRoleMapper.xml +++ b/schoolNewsServ/system/src/main/resources/mapper/DeptRoleMapper.xml @@ -39,11 +39,29 @@ ORDER BY create_time DESC - SELECT - - FROM tb_sys_dept_role - ORDER BY dept_id, role_id, create_time DESC + dr.dept_id, + d.name AS dept_name, + d.description AS dept_description, + dr.role_id, + r.name AS role_name, + r.description AS role_description + FROM tb_sys_dept_role dr + LEFT JOIN tb_sys_dept d ON dr.dept_id = d.dept_id AND d.deleted = 0 + LEFT JOIN tb_sys_role r ON dr.role_id = r.role_id AND r.deleted = 0 + WHERE dr.deleted = 0 + ORDER BY dr.dept_id, dr.role_id, dr.create_time DESC @@ -65,4 +83,81 @@ (#{deptRole.deptID}, #{deptRole.roleID}) + + + + + + + + \ No newline at end of file diff --git a/schoolNewsServ/system/src/main/resources/mapper/ResourcePermissionMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/ResourcePermissionMapper.xml new file mode 100644 index 0000000..97b82b6 --- /dev/null +++ b/schoolNewsServ/system/src/main/resources/mapper/ResourcePermissionMapper.xml @@ -0,0 +1,80 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + id, resource_type, resource_id, dept_id, role_id, + can_read, can_write, can_execute, + creator, updater, create_time, update_time, delete_time, deleted + + + + + INSERT INTO tb_resource_permission ( + id, resource_type, resource_id, dept_id, role_id, + can_read, can_write, can_execute, + creator, create_time, deleted + ) VALUES ( + #{id}, #{resourceType}, #{resourceID}, #{deptID}, #{roleID}, + #{canRead}, #{canWrite}, #{canExecute}, + #{creator}, #{createTime}, #{deleted} + ) + + + + + INSERT INTO tb_resource_permission ( + id, resource_type, resource_id, dept_id, role_id, + can_read, can_write, can_execute, + creator, create_time, deleted + ) VALUES + + ( + #{item.id}, #{item.resourceType}, #{item.resourceID}, #{item.deptID}, #{item.roleID}, + #{item.canRead}, #{item.canWrite}, #{item.canExecute}, + #{item.creator}, #{item.createTime}, #{item.deleted} + ) + + + + diff --git a/schoolNewsServ/system/src/main/resources/mapper/RoleMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/RoleMapper.xml index 3219d7b..db00491 100644 --- a/schoolNewsServ/system/src/main/resources/mapper/RoleMapper.xml +++ b/schoolNewsServ/system/src/main/resources/mapper/RoleMapper.xml @@ -17,42 +17,22 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + id, role_id, name, description, creator, updater, create_time, update_time, delete_time, deleted - - dr.id, dr.dept_id, dr.role_id, - r.name as role_name, d.name as dept_name, - r.description as role_description, d.description as dept_description, - r.creator as role_creator, d.creator as dept_creator, - r.updater as role_updater, d.updater as dept_updater, - r.create_time as role_create_time, d.create_time as dept_create_time, - r.update_time as role_update_time, d.update_time as dept_update_time, - r.delete_time as role_delete_time, d.delete_time as dept_delete_time, - r.deleted as role_deleted, d.deleted as dept_deleted - @@ -110,10 +90,19 @@ + @@ -70,4 +103,20 @@ + + + + DELETE FROM tb_sys_user_dept_role + WHERE user_id = #{userID} + + + + + + DELETE FROM tb_sys_user_dept_role + WHERE user_id IN + + #{userId} + + \ No newline at end of file diff --git a/schoolNewsServ/system/src/main/resources/mapper/UserMapper.xml b/schoolNewsServ/system/src/main/resources/mapper/UserMapper.xml index a31ad96..7c2d6fc 100644 --- a/schoolNewsServ/system/src/main/resources/mapper/UserMapper.xml +++ b/schoolNewsServ/system/src/main/resources/mapper/UserMapper.xml @@ -242,12 +242,10 @@ - - UPDATE tb_sys_user - SET deleted = 1, - delete_time = NOW() - WHERE id = #{id} AND deleted = 0 - + + DELETE FROM tb_sys_user + WHERE id = #{userID} AND deleted = 0 + @@ -293,10 +291,7 @@ INNER JOIN tb_sys_user_dept_role tsudr ON tsui.user_id = tsudr.user_id INNER JOIN tb_sys_dept tsd ON tsudr.dept_id = tsd.dept_id WHERE tsui.user_id = #{userId} AND tsui.deleted = 0 - UNION ALL - - -- 递归查询:向上查找父部门 SELECT p.dept_id, p.name, @@ -304,12 +299,12 @@ CONCAT(p.name, '/', dh.dept_path) as dept_path, dh.level + 1 as level FROM tb_sys_dept p - INNER JOIN dept_hierarchy dh ON p.dept_id = dh.parent_id + INNER JOIN dept_hierarchy dh ON dh.parent_id = p.dept_id WHERE p.deleted = 0 ) SELECT dh.dept_path FROM dept_hierarchy dh - WHERE dh.parent_id IS NULL -- 只取最顶层的部门路径 + WHERE dh.parent_id IS NULL LIMIT 1 @@ -317,7 +312,6 @@ diff --git a/schoolNewsWeb/package-lock.json b/schoolNewsWeb/package-lock.json index 85c9fd7..d510f87 100644 --- a/schoolNewsWeb/package-lock.json +++ b/schoolNewsWeb/package-lock.json @@ -27,7 +27,7 @@ "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "sass": "^1.32.7", - "typescript": "~4.5.5", + "typescript": "^5.2.2", "vite": "^5.0.0", "vite-plugin-pwa": "^0.19.0" } @@ -7505,9 +7505,9 @@ } }, "node_modules/typescript": { - "version": "4.5.5", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-4.5.5.tgz", - "integrity": "sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==", + "version": "5.2.2", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.2.2.tgz", + "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", "devOptional": true, "license": "Apache-2.0", "bin": { @@ -7515,7 +7515,7 @@ "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/unbox-primitive": { diff --git a/schoolNewsWeb/package.json b/schoolNewsWeb/package.json index 995cbb6..cc5a600 100644 --- a/schoolNewsWeb/package.json +++ b/schoolNewsWeb/package.json @@ -30,7 +30,7 @@ "eslint": "^7.32.0", "eslint-plugin-vue": "^8.0.3", "sass": "^1.32.7", - "typescript": "~4.5.5", + "typescript": "^5.2.2", "vite": "^5.0.0", "vite-plugin-pwa": "^0.19.0" } diff --git a/schoolNewsWeb/public/img/admin/maintain.svg b/schoolNewsWeb/public/img/admin/maintain.svg new file mode 100644 index 0000000..9263dc6 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/maintain.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/schoolNewsWeb/public/img/admin/resource.svg b/schoolNewsWeb/public/img/admin/resource.svg new file mode 100644 index 0000000..2c81f96 --- /dev/null +++ b/schoolNewsWeb/public/img/admin/resource.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/schoolNewsWeb/src/apis/study/learning-task.ts b/schoolNewsWeb/src/apis/study/learning-task.ts index 1dc27a3..36def26 100644 --- a/schoolNewsWeb/src/apis/study/learning-task.ts +++ b/schoolNewsWeb/src/apis/study/learning-task.ts @@ -83,11 +83,11 @@ export const learningTaskApi = { }, /** - * 发布学习任务 + * 发布\下架学习任务 * @param taskID 任务ID * @returns Promise> */ - async publishTask(task: LearningTask): Promise> { + async changeTaskStatus(task: LearningTask): Promise> { const response = await api.put(`${this.learningTaskPrefix}/status`, task); return response.data; }, diff --git a/schoolNewsWeb/src/apis/system/dept.ts b/schoolNewsWeb/src/apis/system/dept.ts index 8e32dce..a107648 100644 --- a/schoolNewsWeb/src/apis/system/dept.ts +++ b/schoolNewsWeb/src/apis/system/dept.ts @@ -96,13 +96,13 @@ export const deptApi = { /** * @description 查询部门角色列表 - * @param dept 部门角色信息 + * @param deptRole 部门角色信息 * @returns Promise> 部门角色列表 * @author yslg * @ since 2025-10-06 */ - async getDeptRoleList(dept: SysDeptRole): Promise> { - const response = await api.post('/depts/role/list', dept); + async getDeptRoleList(deptRole: SysDeptRole): Promise> { + const response = await api.post('/depts/role/list', deptRole); return response.data; }, diff --git a/schoolNewsWeb/src/apis/system/user.ts b/schoolNewsWeb/src/apis/system/user.ts index 377455f..63802d8 100644 --- a/schoolNewsWeb/src/apis/system/user.ts +++ b/schoolNewsWeb/src/apis/system/user.ts @@ -97,8 +97,8 @@ export const userApi = { * @param user 用户信息 * @returns Promise> */ - async getUserDeptRole(user: SysUserDeptRole): Promise> { - const response = await api.post('/users/bind/deptrole/list', user); + async getUserDeptRole(user: UserDeptRoleVO): Promise> { + const response = await api.post('/users/bind/deptrole/list', user); return response.data; }, diff --git a/schoolNewsWeb/src/components/base/GenericSelector.vue b/schoolNewsWeb/src/components/base/GenericSelector.vue new file mode 100644 index 0000000..06be997 --- /dev/null +++ b/schoolNewsWeb/src/components/base/GenericSelector.vue @@ -0,0 +1,862 @@ + + + + + + diff --git a/schoolNewsWeb/src/components/base/TreeNode.vue b/schoolNewsWeb/src/components/base/TreeNode.vue new file mode 100644 index 0000000..861f4e5 --- /dev/null +++ b/schoolNewsWeb/src/components/base/TreeNode.vue @@ -0,0 +1,221 @@ + + + + + + diff --git a/schoolNewsWeb/src/components/base/index.ts b/schoolNewsWeb/src/components/base/index.ts index 81fbc4c..3e78959 100644 --- a/schoolNewsWeb/src/components/base/index.ts +++ b/schoolNewsWeb/src/components/base/index.ts @@ -7,4 +7,6 @@ export { default as MenuSidebar } from './MenuSidebar.vue'; export { default as TopNavigation } from './TopNavigation.vue'; export { default as UserDropdown } from './UserDropdown.vue'; export { default as Search } from './Search.vue'; -export { default as CenterHead } from './CenterHead.vue'; \ No newline at end of file +export { default as CenterHead } from './CenterHead.vue'; +export { default as GenericSelector } from './GenericSelector.vue'; +export { default as TreeNode } from './TreeNode.vue'; \ No newline at end of file diff --git a/schoolNewsWeb/src/components/base/selector-styles.scss b/schoolNewsWeb/src/components/base/selector-styles.scss new file mode 100644 index 0000000..6626ce4 --- /dev/null +++ b/schoolNewsWeb/src/components/base/selector-styles.scss @@ -0,0 +1,301 @@ +// 通用选择器样式 +.modal-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: rgba(0, 0, 0, 0.5); + display: flex; + align-items: center; + justify-content: center; + z-index: 3000; +} + +.modal-content { + background: white; + border-radius: 8px; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); + display: flex; + flex-direction: column; + max-height: 90vh; + + &.large { + width: 900px; + } +} + +.modal-header { + padding: 16px 24px; + border-bottom: 1px solid #e8e8e8; + display: flex; + justify-content: space-between; + align-items: center; + + .modal-title { + margin: 0; + font-size: 18px; + font-weight: 500; + color: #333; + } + + .modal-close { + background: none; + border: none; + font-size: 20px; + color: #999; + cursor: pointer; + padding: 0; + width: 24px; + height: 24px; + line-height: 24px; + + &:hover { + color: #666; + } + } +} + +.modal-body { + flex: 1; + overflow: hidden; + padding: 16px 24px; +} + +.modal-footer { + padding: 12px 24px; + border-top: 1px solid #e8e8e8; + display: flex; + justify-content: flex-end; + gap: 12px; +} + +.generic-selector { + display: flex; + gap: 16px; + height: 500px; +} + +.selector-panel { + flex: 1; + display: flex; + flex-direction: column; + border: 1px solid #e8e8e8; + border-radius: 4px; + background: #fafafa; + + .panel-header { + padding: 12px 16px; + background: #f0f0f0; + border-bottom: 1px solid #e8e8e8; + display: flex; + justify-content: space-between; + align-items: center; + + .panel-title { + margin: 0; + font-size: 14px; + font-weight: 500; + color: #333; + } + + .panel-count { + font-size: 12px; + color: #999; + } + } + + .panel-search { + padding: 12px 16px; + border-bottom: 1px solid #e8e8e8; + background: white; + + .search-input-small { + width: 100%; + padding: 6px 12px; + border: 1px solid #d9d9d9; + border-radius: 4px; + font-size: 14px; + outline: none; + + &:focus { + border-color: #409EFF; + } + } + } + + .panel-body { + flex: 1; + overflow-y: auto; + background: white; + + &::-webkit-scrollbar { + width: 8px; + } + + &::-webkit-scrollbar-track { + background: #f1f1f1; + } + + &::-webkit-scrollbar-thumb { + background: #c1c1c1; + border-radius: 4px; + + &:hover { + background: #a8a8a8; + } + } + } +} + +.item-list, +.tree-list { + padding: 8px; +} + +.tree-notice { + padding: 40px 20px; + text-align: center; + color: #909399; + font-size: 14px; +} + +.list-item { + display: flex; + align-items: center; + padding: 8px 12px; + cursor: pointer; + border-radius: 4px; + transition: background-color 0.2s; + + &:hover { + background-color: #f5f7fa; + } + + &.selected { + background-color: #e6f7ff; + } + + input[type="checkbox"] { + margin-right: 8px; + cursor: pointer; + } + + .item-label { + flex: 1; + font-size: 14px; + color: #333; + } + + .item-sublabel { + font-size: 12px; + color: #999; + margin-left: 8px; + } +} + +.selector-actions { + display: flex; + flex-direction: column; + justify-content: center; + gap: 12px; + + .action-btn { + padding: 8px 16px; + background: white; + border: 1px solid #d9d9d9; + border-radius: 4px; + cursor: pointer; + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + transition: all 0.2s; + min-width: 60px; + + &:hover:not(:disabled) { + border-color: #409EFF; + color: #409EFF; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.5; + } + + .arrow { + font-size: 16px; + font-weight: bold; + } + + .btn-text { + font-size: 12px; + } + } +} + +.loading-more, +.no-more { + text-align: center; + padding: 12px; + color: #999; + font-size: 12px; +} + +.loading-more { + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.loading-spinner-small { + width: 16px; + height: 16px; + border: 2px solid #f3f3f3; + border-top: 2px solid #409EFF; + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.btn-primary { + padding: 8px 20px; + background-color: #409EFF; + color: white; + border: none; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: background-color 0.2s; + + &:hover:not(:disabled) { + background-color: #66b1ff; + } + + &:disabled { + cursor: not-allowed; + opacity: 0.6; + } +} + +.btn-default { + padding: 8px 20px; + background-color: white; + color: #606266; + border: 1px solid #dcdfe6; + border-radius: 4px; + cursor: pointer; + font-size: 14px; + transition: all 0.2s; + + &:hover { + border-color: #409EFF; + color: #409EFF; + } +} + diff --git a/schoolNewsWeb/src/components/index.ts b/schoolNewsWeb/src/components/index.ts index eb9a65b..80ebbc8 100644 --- a/schoolNewsWeb/src/components/index.ts +++ b/schoolNewsWeb/src/components/index.ts @@ -8,5 +8,4 @@ export * from './text'; export * from './file'; // 导出 user 用户组件 -export * from './user'; diff --git a/schoolNewsWeb/src/components/user/README.md b/schoolNewsWeb/src/components/user/README.md deleted file mode 100644 index 7605098..0000000 --- a/schoolNewsWeb/src/components/user/README.md +++ /dev/null @@ -1,213 +0,0 @@ -# UserSelect 用户选择组件 - -## 功能特性 - -- ✅ 支持双栏选择器布局 -- ✅ 支持搜索功能(左右两侧独立搜索) -- ✅ 三种操作方式:双击、勾选+按钮、全部按钮 -- ✅ **支持滚动分页加载**(性能优化) -- ✅ 支持传入静态数据或API方法 -- ✅ 完全封装的样式和逻辑 - -## Props 配置 - -| 参数 | 类型 | 默认值 | 说明 | -|------|------|--------|------| -| visible | Boolean | false | 控制弹窗显示/隐藏 | -| mode | 'add' \| 'remove' | 'add' | 选择模式 | -| title | String | '人员选择' | 弹窗标题 | -| leftTitle | String | '可选人员' | 左侧面板标题 | -| rightTitle | String | '已选人员' | 右侧面板标题 | -| availableUsers | UserVO[] | [] | 左区域静态数据 | -| initialTargetUsers | UserVO[] | [] | 初始已选人员 | -| loading | Boolean | false | 确认按钮加载状态 | -| **usePagination** | Boolean | false | **是否启用分页加载** | -| **fetchApi** | Function | undefined | **分页加载API方法** | -| **filterParams** | Object | {} | **API过滤参数** | -| **pageSize** | Number | 20 | **每页数量** | - -## Events 事件 - -| 事件名 | 参数 | 说明 | -|--------|------|------| -| update:visible | (value: boolean) | 更新弹窗显示状态 | -| confirm | (users: UserVO[]) | 确认提交,返回选中的用户列表 | -| cancel | - | 取消操作 | - -## 使用方式 - -### 方式一:传入静态数据(适合数据量少的场景) - -```vue - - - -``` - -### 方式二:使用分页加载(推荐,适合数据量大的场景) - -```vue - - - -``` - -### 方式三:混合模式(添加模式用分页,删除模式用静态) - -```vue - - - -``` - -## API 方法要求 - -使用 `usePagination` 时,`fetchApi` 方法需要符合以下签名: - -```typescript -async function fetchApi( - pageParam: PageParam, - filter?: any -): Promise> -``` - -### PageParam 类型 - -```typescript -interface PageParam { - page: number; // 当前页码(从1开始) - size: number; // 每页数量 -} -``` - -### ResultDomain 类型 - -```typescript -interface ResultDomain { - success: boolean; - message?: string; - dataList?: T[]; - pageParam?: { - totalElements: number; // 总记录数 - // ... 其他分页信息 - }; -} -``` - -## 特性说明 - -### 滚动分页加载 - -- 当启用 `usePagination` 时,左侧面板支持滚动加载更多数据 -- 滚动到距离底部 50px 时自动加载下一页 -- 显示加载状态和"已加载全部数据"提示 -- 搜索时自动重置分页并重新加载 - -### 搜索功能 - -- **分页模式**:搜索关键词会传递给 API,在服务端进行过滤 -- **静态模式**:搜索在前端进行过滤 - -### 数据过滤 - -组件会自动过滤掉右侧已选择的用户,避免重复选择。 - -## 性能优化建议 - -1. **数据量 < 100**:使用静态数据模式 -2. **数据量 > 100**:使用分页加载模式 -3. **数据量 > 1000**:使用分页加载 + 服务端搜索 - -## 注意事项 - -1. 使用分页模式时,`availableUsers` 会被忽略 -2. 删除模式下通常使用静态数据(当前已分配的用户) -3. `filterParams` 支持传入额外的过滤条件,会合并到 API 请求中 - diff --git a/schoolNewsWeb/src/components/user/UserSelect.vue b/schoolNewsWeb/src/components/user/UserSelect.vue deleted file mode 100644 index 9d8dc1a..0000000 --- a/schoolNewsWeb/src/components/user/UserSelect.vue +++ /dev/null @@ -1,781 +0,0 @@ - - - - - - diff --git a/schoolNewsWeb/src/components/user/index.ts b/schoolNewsWeb/src/components/user/index.ts deleted file mode 100644 index 6d1e56e..0000000 --- a/schoolNewsWeb/src/components/user/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @description 用户相关组件 - * @author yslg - * @since 2025-10-22 - */ - -export { default as UserSelect } from './UserSelect.vue'; - diff --git a/schoolNewsWeb/src/config/index.ts b/schoolNewsWeb/src/config/index.ts index 9127809..1374531 100644 --- a/schoolNewsWeb/src/config/index.ts +++ b/schoolNewsWeb/src/config/index.ts @@ -51,6 +51,6 @@ export const APP_CONFIG = { refreshThreshold: 5 * 60 * 1000 // 提前5分钟刷新 } }; -export const PUBLIC_IMG_PATH = '/schoolNewsWeb/img'; +export const PUBLIC_IMG_PATH = 'http://localhost:8080/schoolNewsWeb/img'; export default APP_CONFIG; diff --git a/schoolNewsWeb/src/types/dept/index.ts b/schoolNewsWeb/src/types/dept/index.ts index c08a537..078ed55 100644 --- a/schoolNewsWeb/src/types/dept/index.ts +++ b/schoolNewsWeb/src/types/dept/index.ts @@ -6,6 +6,7 @@ import { BaseDTO } from '../base'; import { SysRole } from '../role'; +import { SysUserDeptRole } from '../user'; /** * 系统部门 @@ -15,6 +16,8 @@ export interface SysDept extends BaseDTO { deptID?: string; /** 父部门ID */ parentID?: string; + /** 部门路径,格式:/root_department/dept_001/ */ + deptPath?: string; /** 部门名称 */ name?: string; /** 部门描述 */ @@ -30,13 +33,35 @@ export interface SysDept extends BaseDTO { /** * 部门角色VO */ -export interface DeptRoleVO { - /** 部门信息 */ - dept?: SysDept; - /** 角色信息 */ - role?: SysRole; +export interface UserDeptRoleVO { + depts?: SysDept[]; roles?: SysRole[]; + userDeptRoles?: SysUserDeptRole[]; + + // 扁平化字段,用于权限查询优化 + /** 用户ID */ + userID?: string; + /** 部门ID */ + deptID?: string; + /** 部门名称 */ + deptName?: string; + /** 部门描述 */ + deptDescription?: string; + /** 父部门ID */ + parentID?: string; + /** 父部门名称 */ + parentName?: string; + /** 父部门描述 */ + parentDescription?: string; + /** 角色ID */ + roleID?: string; + /** 角色名称 */ + roleName?: string; + /** 角色描述 */ + roleDescription?: string; + /** 部门路径,用于快速权限继承判断 */ + deptPath?: string; } /** @@ -48,5 +73,19 @@ export interface SysDeptRole extends BaseDTO { deptID?: string; /** 角色ID */ roleID?: string; + + // 扁平化字段(用于前端展示,从关联查询中获取) + /** 部门名称 */ + deptName?: string; + /** 部门描述 */ + deptDescription?: string; + /** 父部门ID */ + parentID?: string; + /** 部门路径 */ + deptPath?: string; + /** 角色名称 */ + roleName?: string; + /** 角色描述 */ + roleDescription?: string; } diff --git a/schoolNewsWeb/src/types/user/index.ts b/schoolNewsWeb/src/types/user/index.ts index f8bdfe2..f406c0c 100644 --- a/schoolNewsWeb/src/types/user/index.ts +++ b/schoolNewsWeb/src/types/user/index.ts @@ -93,17 +93,4 @@ export interface SysUserDeptRole extends BaseDTO { deptID?: string; /** 角色ID */ roleID?: string; -} - -export interface UserDeptRoleVO extends BaseDTO { - /** 单个用户信息 */ - user?: SysUser; - /** 用户列表 */ - users?: SysUser[]; - /** 部门列表 */ - depts?: SysDept[]; - /** 角色列表 */ - roles?: SysRole[]; - /** 用户部门角色关联列表 */ - userDeptRoles?: SysUserDeptRole[]; } \ No newline at end of file diff --git a/schoolNewsWeb/src/views/admin/manage/study/StudyManagementView.vue b/schoolNewsWeb/src/views/admin/manage/study/StudyManagementView.vue index c296bd2..3b358b5 100644 --- a/schoolNewsWeb/src/views/admin/manage/study/StudyManagementView.vue +++ b/schoolNewsWeb/src/views/admin/manage/study/StudyManagementView.vue @@ -6,7 +6,6 @@ - @@ -17,7 +16,6 @@