接口修正、成就修正、学习记录修正

This commit is contained in:
2025-11-03 17:12:40 +08:00
parent 35aee59178
commit b95fff224b
28 changed files with 730 additions and 302 deletions

View File

@@ -74,8 +74,7 @@ CREATE TABLE `tb_resource_recommend` (
`deleted` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否删除',
PRIMARY KEY (`id`),
KEY `idx_resource_id` (`resource_id`),
KEY `idx_recommend_type` (`recommend_type`),
CONSTRAINT `fk_resource_recommend_resource` FOREIGN KEY (`resource_id`) REFERENCES `tb_resource` (`resource_id`) ON DELETE CASCADE
KEY `idx_recommend_type` (`recommend_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='资源推荐表';
-- 标签表

View File

@@ -3,8 +3,8 @@ use school_news;
DROP TABLE IF EXISTS `tb_sys_operation_log`;
CREATE TABLE `tb_sys_operation_log` (
`id` VARCHAR(50) NOT NULL COMMENT '日志ID',
`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`user_id` VARCHAR(50) NULL COMMENT '用户ID',
`username` VARCHAR(50) NULL COMMENT '用户名',
`module` VARCHAR(100) DEFAULT NULL COMMENT '操作模块',
`operation` VARCHAR(100) DEFAULT NULL COMMENT '操作类型',
`method` VARCHAR(200) DEFAULT NULL COMMENT '请求方法',

View File

@@ -46,7 +46,7 @@ CREATE TABLE `tb_sys_user_info` (
DROP TABLE IF EXISTS `tb_sys_login_log`;
CREATE TABLE `tb_sys_login_log` (
`id` VARCHAR(50) NOT NULL COMMENT '登录日志ID',
`user_id` VARCHAR(50) NOT NULL COMMENT '用户ID',
`user_id` VARCHAR(50) NULL COMMENT '用户ID',
`username` VARCHAR(50) NOT NULL COMMENT '用户名',
`ip_address` VARCHAR(45) DEFAULT NULL COMMENT 'IP地址',
`ip_source` VARCHAR(100) DEFAULT NULL COMMENT 'IP来源',

View File

@@ -64,26 +64,41 @@ INSERT INTO `tb_sys_permission` (id,permission_id, name, code, description, modu
-- 插入角色-权限关联数据
INSERT INTO `tb_sys_role_permission` (id, role_id, permission_id, creator, create_time) VALUES
('0', 'superadmin', 'perm_default', '1', now()),
('1', 'superadmin', 'perm_system_manage', '1', now()),
('2', 'superadmin', 'perm_system_dept_manage', '1', now()),
('3', 'superadmin', 'perm_system_menu_manage', '1', now()),
('4', 'superadmin', 'perm_system_permission_manage', '1', now()),
('5', 'superadmin', 'perm_system_role_manage', '1', now()),
('6', 'superadmin', 'perm_system_user_manage', '1', now()),
('7', 'superadmin', 'perm_system_module_manage', '1', now()),
('8', 'superadmin', 'perm_news_manage', '1', now()),
('9', 'superadmin', 'perm_news_article_add', '1', now()),
('10', 'superadmin', 'perm_study_manage', '1', now()),
('10.1', 'superadmin', 'perm_achievement_manage', '1', now()),
('11', 'superadmin', 'perm_ai_manage', '1', now()),
('12', 'superadmin', 'perm_usercenter_manage', '1', now()),
('13', 'superadmin', 'perm_file_manage', '1', now()),
('14', 'freedom', 'perm_default', '1', now()),
('15', 'superadmin', 'perm_crontab_manage', '1', now()),
('16', 'superadmin', 'perm_crontab_execute', '1', now()),
('17', 'admin', 'perm_crontab_manage', '1', now()),
('18', 'admin', 'perm_crontab_execute', '1', now());
-- 超级管理员:拥有所有权限
('1', 'superadmin', 'perm_default', '1', now()),
('2', 'superadmin', 'perm_system_manage', '1', now()),
('3', 'superadmin', 'perm_system_dept_manage', '1', now()),
('4', 'superadmin', 'perm_system_menu_manage', '1', now()),
('5', 'superadmin', 'perm_system_permission_manage', '1', now()),
('6', 'superadmin', 'perm_system_role_manage', '1', now()),
('7', 'superadmin', 'perm_system_user_manage', '1', now()),
('8', 'superadmin', 'perm_system_module_manage', '1', now()),
('9', 'superadmin', 'perm_news_manage', '1', now()),
('10', 'superadmin', 'perm_news_article_add', '1', now()),
('11', 'superadmin', 'perm_study_manage', '1', now()),
('12', 'superadmin', 'perm_achievement_manage', '1', now()),
('13', 'superadmin', 'perm_ai_manage', '1', now()),
('14', 'superadmin', 'perm_usercenter_manage', '1', now()),
('15', 'superadmin', 'perm_file_manage', '1', now()),
('16', 'superadmin', 'perm_crontab_manage', '1', now()),
('17', 'superadmin', 'perm_crontab_execute', '1', now()),
-- 管理员:拥有业务管理权限,但没有系统日志等系统管理权限
('20', 'admin', 'perm_default', '1', now()),
('21', 'admin', 'perm_news_manage', '1', now()),
('22', 'admin', 'perm_news_article_add', '1', now()),
('23', 'admin', 'perm_study_manage', '1', now()),
('24', 'admin', 'perm_achievement_manage', '1', now()),
('25', 'admin', 'perm_ai_manage', '1', now()),
('26', 'admin', 'perm_usercenter_manage', '1', now()),
('27', 'admin', 'perm_file_manage', '1', now()),
-- 自由角色:拥有用户视图相关的所有权限(前台用户权限)
('30', 'freedom', 'perm_default', '1', now()),
('31', 'freedom', 'perm_news_article_add', '1', now()),
('32', 'freedom', 'perm_ai_manage', '1', now()),
('33', 'freedom', 'perm_usercenter_manage', '1', now()),
('34', 'freedom', 'perm_file_manage', '1', now());
-- 插入前端菜单数据
INSERT INTO `tb_sys_menu` VALUES
@@ -135,7 +150,7 @@ INSERT INTO `tb_sys_menu` VALUES
('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),
('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),

View File

@@ -11,6 +11,7 @@ import org.xyzh.achievement.mapper.AchievementMapper;
import org.xyzh.achievement.mapper.UserAchievementMapper;
import org.xyzh.achievement.mapper.UserAchievementProgressMapper;
import org.xyzh.api.achievement.AchievementService;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.core.enums.AchievementEventType;
import org.xyzh.common.core.event.AchievementEvent;
@@ -20,6 +21,7 @@ import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.dto.usercenter.TbAchievement;
import org.xyzh.common.dto.usercenter.TbUserAchievement;
import org.xyzh.common.dto.usercenter.TbUserAchievementProgress;
import org.xyzh.common.redis.service.RedisService;
import org.xyzh.common.utils.IDUtils;
import org.xyzh.common.vo.AchievementVO;
import org.xyzh.system.utils.LoginUtil;
@@ -41,6 +43,7 @@ import java.util.stream.Collectors;
public class ACHAchievementServiceImpl implements AchievementService {
private static final Logger logger = LoggerFactory.getLogger(ACHAchievementServiceImpl.class);
private static final String REDIS_LOGIN_PREFIX = "login:token:";
@Autowired
private AchievementMapper achievementMapper;
@@ -56,6 +59,9 @@ public class ACHAchievementServiceImpl implements AchievementService {
@Autowired
private ResourcePermissionService resourcePermissionService;
@Autowired
private RedisService redisService;
// ==================== 成就定义管理 ====================
@@ -510,7 +516,8 @@ public class ACHAchievementServiceImpl implements AchievementService {
logger.debug("处理成就事件: {}", event);
// 获取该事件类型相关的所有成就
List<TbAchievement> achievements = getAchievementsByEventType(event.getEventType());
// 传入 userID在异步线程中根据 userID 查询用户部门角色
List<TbAchievement> achievements = getAchievementsByEventType(event.getEventType(), event.getUserID());
if (achievements.isEmpty()) {
resultDomain.success("无相关成就", newAchievements);
return resultDomain;
@@ -763,12 +770,38 @@ public class ACHAchievementServiceImpl implements AchievementService {
/**
* 根据事件类型获取相关成就
* @param eventType 事件类型
* @param userID 用户ID用于在异步线程中从Redis获取用户登录信息
*/
private List<TbAchievement> getAchievementsByEventType(AchievementEventType eventType) {
private List<TbAchievement> getAchievementsByEventType(AchievementEventType eventType, String userID) {
// 获取所有成就
TbAchievement filter = new TbAchievement();
filter.setDeleted(false);
List<UserDeptRoleVO> userDeptRoles = LoginUtil.getCurrentDeptRole();
// 【关键优化】在异步线程中从Redis缓存获取用户的LoginDomain
// 避免数据库查询,性能更好
List<UserDeptRoleVO> userDeptRoles = new ArrayList<>();
if (StringUtils.hasText(userID)) {
try {
// 从Redis获取用户登录信息
String redisKey = REDIS_LOGIN_PREFIX + userID;
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
if (loginDomain != null && loginDomain.getRoles() != null) {
userDeptRoles = loginDomain.getRoles();
logger.debug("从Redis缓存获取用户部门角色成功userID: {}, 角色数: {}",
userID, userDeptRoles.size());
} else {
logger.warn("Redis缓存中未找到用户登录信息或角色为空userID: {}", userID);
}
} catch (Exception e) {
logger.error("从Redis获取用户部门角色异常userID: {}", userID, e);
}
} else {
logger.warn("userID为空无法获取用户部门角色");
}
List<TbAchievement> allAchievements = achievementMapper.selectAchievements(filter, userDeptRoles);
// 筛选支持该事件类型的成就

View File

@@ -435,14 +435,13 @@ public class AuthController {
return result;
}
// 3. 密码加密
String encryptedPassword = passwordEncoder.encode(password);
user.setPassword(encryptedPassword);
// 3. 设置密码明文Service层会加密
user.setPassword(password);
// 4. 设置用户状态为正常
user.setStatus(0);
// 5. 调用UserService注册用户
// 5. 调用UserService注册用户Service层会加密密码
ResultDomain<TbSysUser> registerResult = userService.registerUser(user);
if (!registerResult.isSuccess()) {

View File

@@ -7,9 +7,11 @@ import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.dto.dept.TbSysDeptRole;
import org.xyzh.common.dto.permission.TbSysPermission;
import org.xyzh.common.core.enums.UserStatus;
import org.xyzh.common.vo.UserDeptRoleVO;
import java.util.Collection;
import java.util.List;
import java.util.ArrayList;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@@ -36,6 +38,29 @@ public class UserPrincipal implements UserDetails {
return new UserPrincipal(user, roles, permissions);
}
/**
* @description 从LoginDomain创建UserPrincipal用于从Redis缓存恢复认证信息
* @param user 用户信息
* @param userDeptRoles 用户部门角色列表来自LoginDomain
* @param permissions 权限列表
* @return UserPrincipal 用户主体
* @author yslg
* @since 2025-11-03
*/
public static UserPrincipal createFromLoginDomain(TbSysUser user, List<UserDeptRoleVO> userDeptRoles, List<TbSysPermission> permissions) {
// 将UserDeptRoleVO转换为TbSysDeptRole简化处理实际使用时UserDeptRoleVO已包含所需信息
List<TbSysDeptRole> roles = new ArrayList<>();
if (userDeptRoles != null) {
for (UserDeptRoleVO vo : userDeptRoles) {
TbSysDeptRole role = new TbSysDeptRole();
role.setRoleID(vo.getRoleID());
role.setDeptID(vo.getDeptID());
roles.add(role);
}
}
return new UserPrincipal(user, roles, permissions != null ? permissions : new ArrayList<>());
}
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 角色权限

View File

@@ -12,12 +12,16 @@ import org.springframework.security.web.authentication.WebAuthenticationDetailsS
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import org.xyzh.auth.service.UserDetailsServiceImpl;
import org.xyzh.auth.util.JwtTokenUtil;
import org.xyzh.auth.config.AuthProperties;
import org.xyzh.common.core.domain.LoginDomain;
import org.xyzh.common.redis.service.RedisService;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @description JwtAuthenticationFilter.java文件描述 JWT认证过滤器
* @filename JwtAuthenticationFilter.java
@@ -28,14 +32,18 @@ import java.io.IOException;
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
private static final String REDIS_LOGIN_PREFIX = "login:token:";
private static final Logger logger = LoggerFactory.getLogger(JwtAuthenticationFilter.class);
@Autowired
private JwtTokenUtil jwtTokenUtil;
@Autowired
private UserDetailsServiceImpl userDetailsService;
private AuthProperties authProperties;
@Autowired
private AuthProperties authProperties;
private RedisService redisService;
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
@@ -56,17 +64,38 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
String userId = jwtTokenUtil.getUserIdFromToken(token);
if (userId != null && SecurityContextHolder.getContext().getAuthentication() == null) {
UserDetails userDetails = userDetailsService.loadUserByUserId(userId);
// 验证token有效性
if (!jwtTokenUtil.validateToken(token, userId)) {
logger.warn("Token验证失败userId: {}", userId);
filterChain.doFilter(request, response);
return;
}
if (jwtTokenUtil.validateToken(token, userId)) {
// 【优化】从Redis缓存中获取LoginDomain避免每次都查数据库
String redisKey = REDIS_LOGIN_PREFIX + userId;
LoginDomain loginDomain = (LoginDomain) redisService.get(redisKey);
if (loginDomain != null && loginDomain.getUser() != null) {
// 【优化】直接使用缓存中的用户信息构建UserDetails不查数据库
// 使用UserPrincipal从LoginDomain创建UserDetails
UserDetails userDetails = org.xyzh.auth.domain.UserPrincipal.createFromLoginDomain(
loginDomain.getUser(),
loginDomain.getRoles(),
loginDomain.getPermissions()
);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
logger.debug("用户认证成功从缓存userId: {}", userId);
} else {
logger.warn("Redis缓存中未找到用户登录信息userId: {}, 可能已过期或未登录", userId);
}
}
} catch (Exception e) {
logger.error("JWT token validation failed: " + e.getMessage());
logger.error("JWT token validation failed: " + e.getMessage(), e);
}
}

View File

@@ -1,103 +0,0 @@
package org.xyzh.auth.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.xyzh.auth.domain.UserPrincipal;
import org.xyzh.common.dto.user.TbSysUser;
import org.xyzh.common.dto.dept.TbSysDeptRole;
import org.xyzh.common.dto.permission.TbSysPermission;
import org.xyzh.api.system.user.UserService;
import org.xyzh.api.system.role.RoleService;
import org.xyzh.api.system.permission.PermissionService;
import java.util.List;
import java.util.ArrayList;
/**
* @description UserDetailsServiceImpl.java文件描述 用户详情服务实现
* @filename UserDetailsServiceImpl.java
* @author yslg
* @copyright xyzh
* @since 2025-09-28
*/
@Service
public class UserDetailsServiceImpl implements UserDetailsService {
private static final Logger logger = LoggerFactory.getLogger(UserDetailsServiceImpl.class);
@Autowired
private UserService userService;
@Autowired(required = false)
private RoleService roleService;
@Autowired(required = false)
private PermissionService permissionService;
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
TbSysUser filter = new TbSysUser();
filter.setUsername(username);
List<TbSysUser> users = userService.getUserByFilter(filter).getDataList();
if(users.isEmpty()) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
TbSysUser user = users.get(0);
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + username);
}
return loadUserByUserId(user.getID());
}
/**
* @description 根据用户ID加载用户详情
* @param userId 用户ID
* @return UserDetails 用户详情
* @author yslg
* @since 2025-09-28
*/
public UserDetails loadUserByUserId(String userId) {
TbSysUser filter = new TbSysUser();
filter.setID(userId);
List<TbSysUser> users = userService.getUserByFilter(filter).getDataList();
if(users.isEmpty()) {
throw new UsernameNotFoundException("用户不存在: " + userId);
}
TbSysUser user = users.get(0);
if (user == null) {
throw new UsernameNotFoundException("用户不存在: " + userId);
}
// 获取用户角色(如果角色服务可用)
List<TbSysDeptRole> roles = new ArrayList<>();
if (roleService != null) {
try {
// TODO: 需要在RoleService中实现findRolesByUserId方法
// roles = roleService.findRolesByUserId(userId);
} catch (Exception e) {
logger.warn("无法获取用户角色: " + e.getMessage());
}
}
// 获取用户权限(如果权限服务可用)
List<TbSysPermission> permissions = new ArrayList<>();
if (permissionService != null) {
try {
// TODO: 需要在PermissionService中实现findPermissionsByUserId方法
// permissions = permissionService.findPermissionsByUserId(userId);
} catch (Exception e) {
logger.warn("无法获取用户权限: " + e.getMessage());
}
}
return UserPrincipal.create(user, roles, permissions);
}
}

View File

@@ -65,7 +65,8 @@ public class PasswordLoginStrategy implements LoginStrategy {
}else{
filter.setUsername(loginParam.getUsername());
}
filter.setPassword(passwordEncoder.encode(loginParam.getPassword()));
// 【优化】删除无用的密码编码SQL查询不使用password字段
// 密码验证在 verifyCredential() 方法中进行
TbSysUser user = userService.getLoginUser(filter).getData();
if(user == null) {
return null;
@@ -75,7 +76,7 @@ public class PasswordLoginStrategy implements LoginStrategy {
@Override
public boolean verifyCredential(String inputCredential, String storedCredential) {
logger.info(passwordEncoder.encode(inputCredential));
// 使用BCrypt的matches方法验证密码内部会自动处理salt
return passwordEncoder.matches(inputCredential, storedCredential);
}
}

View File

@@ -3,7 +3,7 @@ package org.xyzh.crontab.scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Component;
@@ -29,9 +29,7 @@ public class SchedulerManager {
private TaskScheduler taskScheduler;
@Autowired
private ApplicationContext applicationContext;
@Autowired
@Qualifier("crontabTaskExecutor")
private TaskExecutor taskExecutor;
/**

View File

@@ -20,7 +20,7 @@ import java.util.Date;
* @copyright xyzh
* @since 2025-10-25
*/
@Component
@Component("crontabTaskExecutor")
public class TaskExecutor {
private static final Logger logger = LoggerFactory.getLogger(TaskExecutor.class);

View File

@@ -0,0 +1,63 @@
package org.xyzh.system.config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
/**
* @description 异步任务配置
* @filename AsyncConfig.java
* @author AI
* @copyright xyzh
* @since 2025-11-03
*/
@Configuration
@EnableAsync
public class AsyncConfig {
private static final Logger logger = LoggerFactory.getLogger(AsyncConfig.class);
/**
* @description 配置异步任务执行器(用于@Async和事件发布
* @return Executor
* @author AI
* @since 2025-11-03
*/
@Bean(name = "taskExecutor")
public Executor taskExecutor() {
logger.info("初始化异步任务执行器 taskExecutor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程数
executor.setCorePoolSize(5);
// 最大线程数
executor.setMaxPoolSize(10);
// 队列容量
executor.setQueueCapacity(100);
// 线程名称前缀
executor.setThreadNamePrefix("async-event-");
// 拒绝策略:由调用线程处理
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务完成后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
// 等待时间(秒)
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}

View File

@@ -195,11 +195,17 @@ public class SysDepartmentServiceImpl implements SysDepartmentService {
// 设置基础信息
department.setID(IDUtils.generateID());
department.setDeptID(IDUtils.generateID());
String deptId = IDUtils.generateID();
department.setDeptID(deptId);
department.setCreator(currentUser.getID());
department.setCreateTime(new Date());
department.setDeleted(false);
// 【修复】设置dept_path
String deptPath = buildDeptPath(department.getParentID(), deptId);
department.setDeptPath(deptPath);
logger.info("创建部门设置dept_path: {}", deptPath);
// 插入数据库
int result = departmentMapper.insertDept(department);
@@ -456,4 +462,37 @@ public class SysDepartmentServiceImpl implements SysDepartmentService {
return resultDomain;
}
}
/**
* @description 构建部门路径(优化版:只查询一次父部门路径)
* @param parentId 父部门ID
* @param deptId 当前部门ID
* @return String 部门路径
* @author yslg
* @since 2025-11-03
*/
private String buildDeptPath(String parentId, String deptId) {
// 如果没有父部门,说明是根部门
if (parentId == null || parentId.trim().isEmpty()) {
return "/" + deptId + "/";
}
try {
// 【优化】直接通过一条简单SQL查询父部门的dept_path
String parentPath = departmentMapper.getDeptPathByDeptId(parentId);
if (parentPath != null && !parentPath.trim().isEmpty()) {
// 父部门路径 + 当前部门ID + /
return parentPath + deptId + "/";
} else {
// 父部门没有dept_path使用父部门ID构建
logger.warn("父部门dept_path为空parentId: {}将使用父部门ID构建路径", parentId);
return "/" + parentId + "/" + deptId + "/";
}
} catch (Exception e) {
logger.error("构建dept_path失败parentId: {}, deptId: {}", parentId, deptId, e);
// 发生异常时,返回简单路径
return "/" + deptId + "/";
}
}
}

View File

@@ -65,6 +65,9 @@ public class DatabaseAppender extends AbstractAppender {
java.lang.reflect.Method getUsernameMethod = loginUtil.getClass().getMethod("getCurrentUsername");
userId = (String) getUserIdMethod.invoke(null);
username = (String) getUsernameMethod.invoke(null);
if(username == null || username.trim().isEmpty()) {
username = "system";
}
}
}
} catch (Exception e) {

View File

@@ -90,4 +90,13 @@ public interface DepartmentMapper extends BaseMapper<TbSysDept> {
* @since 2025-09-28
*/
int deleteDept(TbSysDept department);
/**
* @description 根据部门ID获取部门路径不带权限过滤仅用于构建子部门路径
* @param deptId 部门ID
* @return String 部门路径
* @author yslg
* @since 2025-11-03
*/
String getDeptPathByDeptId(@Param("deptId") String deptId);
}

View File

@@ -74,6 +74,7 @@ public class SysUserServiceImpl implements SysUserService {
userInfo.setAvatar("default");
TbSysUserDeptRole userDeptRole = new TbSysUserDeptRole();
userDeptRole.setID(IDUtils.generateID()); // 设置ID
userDeptRole.setUserID(user.getID());
userDeptRole.setDeptID("default_department");
userDeptRole.setRoleID("freedom");
@@ -482,51 +483,55 @@ public class SysUserServiceImpl implements SysUserService {
ResultDomain<UserDeptRoleVO> resultDomain = new ResultDomain<>();
TbSysUser currentUser = LoginUtil.getCurrentUser();
// 收集所有用户ID
List<String> 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<TbSysUserDeptRole> userDeptRoles = new ArrayList<>();
Date now = new Date();
for (TbSysUser user : userDeptRoleVO.getUsers()) {
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);
}
}
logger.info("准备插入 {} 条新绑定记录", userDeptRoles.size());
// 插入新的绑定关系
int result = userDeptRoleMapper.bindUser(userDeptRoles);
logger.info("成功插入 {} 条绑定记录", result);
if (result > 0) {
resultDomain.success("绑定用户部门角色成功", userDeptRoleVO);
} else {
resultDomain.fail("绑定用户部门角色失败:没有记录被插入");
}
// 【限制】检查是否只有一个部门-角色配置(一个用户只能有一个部门-角色)
if (userDeptRoleVO.getUserDeptRoles() == null || userDeptRoleVO.getUserDeptRoles().size() != 1) {
resultDomain.fail("每个用户只能绑定一个部门-角色,请提供且仅提供一个部门-角色配置");
logger.warn("绑定失败:提供了 {} 个部门-角色配置但系统限制为1个",
userDeptRoleVO.getUserDeptRoles() != null ? userDeptRoleVO.getUserDeptRoles().size() : 0);
return resultDomain;
}
// 收集所有用户ID
List<String> userIds = new ArrayList<>();
for (TbSysUser user : userDeptRoleVO.getUsers()) {
userIds.add(user.getID());
}
logger.info("准备为 {} 个用户绑定部门角色(每个用户一个部门-角色)", userIds.size());
// 批量删除所有涉及用户的旧绑定关系(物理删除,包括软删除的记录)
int deleteCount = userDeptRoleMapper.deleteUserDeptRoleByUserIds(userIds);
logger.info("删除了 {} 条旧绑定记录", deleteCount);
// 准备新的绑定数据(每个用户只绑定一个部门-角色)
List<TbSysUserDeptRole> userDeptRoles = new ArrayList<>();
Date now = new Date();
TbSysUserDeptRole templateDeptRole = userDeptRoleVO.getUserDeptRoles().get(0);
for (TbSysUser user : userDeptRoleVO.getUsers()) {
TbSysUserDeptRole newUserDeptRole = new TbSysUserDeptRole();
newUserDeptRole.setID(IDUtils.generateID());
newUserDeptRole.setUserID(user.getID());
newUserDeptRole.setDeptID(templateDeptRole.getDeptID());
newUserDeptRole.setRoleID(templateDeptRole.getRoleID());
newUserDeptRole.setCreateTime(now);
newUserDeptRole.setCreator(currentUser.getID());
userDeptRoles.add(newUserDeptRole);
}
logger.info("准备插入 {} 条新绑定记录每个用户1条", userDeptRoles.size());
// 插入新的绑定关系
int result = userDeptRoleMapper.bindUser(userDeptRoles);
logger.info("成功插入 {} 条绑定记录", result);
if (result > 0) {
resultDomain.success("绑定用户部门角色成功", userDeptRoleVO);
} else {
resultDomain.fail("绑定用户部门角色失败:没有记录被插入");
}
return resultDomain;
}

View File

@@ -111,6 +111,15 @@
</if>
</select>
<!-- 根据部门ID获取部门路径不带权限过滤仅用于构建子部门路径 -->
<select id="getDeptPathByDeptId" resultType="java.lang.String">
SELECT dept_path
FROM tb_sys_dept
WHERE dept_id = #{deptId}
AND deleted = 0
LIMIT 1
</select>
<!-- 根据部门ID查询部门信息包含父部门信息 -->
<select id="selectDeptWithParent" resultMap="BaseResultMap">
SELECT