package com.designhub.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.designhub.dto.LoginDTO; import com.designhub.dto.RegisterDTO; import com.designhub.entity.Department; import com.designhub.entity.User; import com.designhub.entity.UserDepartment; import com.designhub.exception.BusinessException; import com.designhub.mapper.UserMapper; import com.designhub.mapper.DepartmentMapper; import com.designhub.mapper.UserDepartmentMapper; import com.designhub.common.ResultCode; import com.designhub.util.JwtUtil; import com.designhub.vo.LoginVO; import com.designhub.vo.UserInfoVO; import com.designhub.vo.UserDepartmentVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import java.util.HashMap; import java.util.stream.Collectors; /** * 用户管理Service */ @Service @RequiredArgsConstructor @Slf4j public class UserService { private final UserMapper userMapper; private final DepartmentMapper departmentMapper; private final UserDepartmentMapper userDepartmentMapper; private final TeamScorePoolService teamScorePoolService; private final RoleService roleService; private final DingTalkService dingTalkService; private final DepartmentService departmentService; private final JwtUtil jwtUtil; private final BCryptPasswordEncoder passwordEncoder = new BCryptPasswordEncoder(); @Autowired(required = false) private com.designhub.mapper.TaskMapper taskMapper; private Department getDepartmentOrThrow(Long deptId) { if (deptId == null) { return null; } Department department = departmentMapper.selectById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } if (department.getStatus() != null && department.getStatus() == 0) { throw new BusinessException("该部门已被禁用"); } return department; } /** * 分页查询用户列表 */ public Page getUserList(int pageNum, int pageSize, String keyword, Long deptId, Integer status) { Page page = new Page<>(pageNum, pageSize); LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); if (StringUtils.hasText(keyword)) { queryWrapper.and(wrapper -> wrapper .like(User::getUsername, keyword) .or() .like(User::getRealName, keyword) .or() .like(User::getPhone, keyword) .or() .like(User::getEmail, keyword) ); } if (deptId != null) { queryWrapper.eq(User::getDeptId, deptId); } if (status != null) { queryWrapper.eq(User::getStatus, status); } queryWrapper.orderByDesc(User::getCreateTime); return userMapper.selectPage(page, queryWrapper); } /** * 根据ID获取用户 */ public User getUserById(Long id) { User user = userMapper.selectById(id); if (user != null) { // 不返回密码 user.setPassword(null); } return user; } /** * 根据用户名获取用户 */ public User getUserByUsername(String username) { return userMapper.selectOne( new LambdaQueryWrapper() .eq(User::getUsername, username) ); } /** * 用户注册 */ @Transactional(rollbackFor = Exception.class) public User register(RegisterDTO registerDTO) { log.info("用户注册: username={}, roleId={}, deptId={}", registerDTO.getUsername(), registerDTO.getRoleId(), registerDTO.getDeptId()); // 验证用户名唯一性 User existUser = getUserByUsername(registerDTO.getUsername()); if (existUser != null) { throw new BusinessException("用户名已存在"); } // 验证角色ID(只允许注册为设计师或需求方) if (registerDTO.getRoleId() != 5L && registerDTO.getRoleId() != 6L) { throw new BusinessException("无效的角色选择"); } // 验证部门存在 Department department = departmentMapper.selectById(registerDTO.getDeptId()); if (department == null) { throw new BusinessException("部门不存在"); } // 验证角色和部门类型匹配 String roleCode = registerDTO.getRoleId() == 5L ? "DESIGNER" : "REQUESTER"; String expectedDeptType = roleCode.equals("DESIGNER") ? "receiving" : "demand"; if (!expectedDeptType.equals(department.getDeptType())) { throw new BusinessException("角色与部门类型不匹配"); } // 创建用户 User user = new User(); user.setUsername(registerDTO.getUsername()); user.setPassword(passwordEncoder.encode(registerDTO.getPassword())); user.setRealName(registerDTO.getRealName()); user.setEmail(registerDTO.getEmail()); user.setPhone(registerDTO.getPhone()); user.setDeptId(registerDTO.getDeptId()); user.setDeptName(department.getDeptName()); user.setRoleIds(String.valueOf(registerDTO.getRoleId())); user.setStatus(1); user.setDeleted(0); userMapper.insert(user); log.info("用户注册成功: userId={}, username={}", user.getId(), user.getUsername()); // 不返回密码 user.setPassword(null); return user; } /** * 创建用户 */ @Transactional(rollbackFor = Exception.class) public User createUser(User user) { // 验证用户名唯一性 User existUser = getUserByUsername(user.getUsername()); if (existUser != null) { throw new BusinessException("用户名已存在"); } // 验证手机号唯一性 if (StringUtils.hasText(user.getPhone())) { long count = userMapper.selectCount( new LambdaQueryWrapper() .eq(User::getPhone, user.getPhone()) ); if (count > 0) { throw new BusinessException("手机号已存在"); } } // 验证邮箱唯一性 if (StringUtils.hasText(user.getEmail())) { long count = userMapper.selectCount( new LambdaQueryWrapper() .eq(User::getEmail, user.getEmail()) ); if (count > 0) { throw new BusinessException("邮箱已存在"); } } // 加密密码 if (StringUtils.hasText(user.getPassword())) { user.setPassword(passwordEncoder.encode(user.getPassword())); } else { // 默认密码 user.setPassword(passwordEncoder.encode("123456")); } // 设置部门名称 if (user.getDeptId() != null) { Department department = getDepartmentOrThrow(user.getDeptId()); user.setDeptName(department.getDeptName()); } // 设置默认状态 if (user.getStatus() == null) { user.setStatus(1); } userMapper.insert(user); return user; } /** * 更新用户 */ @Transactional(rollbackFor = Exception.class) public boolean updateUser(User user) { User existUser = getUserById(user.getId()); if (existUser == null) { throw new BusinessException("用户不存在"); } // 如果修改了用户名,验证唯一性 if (StringUtils.hasText(user.getUsername()) && !user.getUsername().equals(existUser.getUsername())) { User sameUsernameUser = getUserByUsername(user.getUsername()); if (sameUsernameUser != null) { throw new BusinessException("用户名已存在"); } } // 如果更新了部门 if (user.getDeptId() != null) { Department department = getDepartmentOrThrow(user.getDeptId()); user.setDeptName(department != null ? department.getDeptName() : null); } // 不允许通过此接口修改密码 user.setPassword(null); return userMapper.updateById(user) > 0; } /** * 删除用户(逻辑删除) */ @Transactional(rollbackFor = Exception.class) public boolean deleteUser(Long id) { User user = getUserById(id); if (user == null) { throw new BusinessException("用户不存在"); } // 不允许删除自己 // TODO: 获取当前登录用户ID // if (id.equals(currentUserId)) { // throw new BusinessException("不能删除自己"); // } // 注意:已移除删除用户角色关联的代码,因为sys_user_role表不存在 // 系统使用sys_user表中的role_id字段管理角色,不使用关联表 return userMapper.deleteById(id) > 0; } /** * 重置密码 */ @Transactional(rollbackFor = Exception.class) public boolean resetPassword(Long id, String newPassword) { User user = getUserById(id); if (user == null) { throw new BusinessException("用户不存在"); } user.setPassword(passwordEncoder.encode(newPassword)); return userMapper.updateById(user) > 0; } /** * 修改密码 */ @Transactional(rollbackFor = Exception.class) public boolean changePassword(Long id, String oldPassword, String newPassword) { User user = userMapper.selectById(id); if (user == null) { throw new BusinessException("用户不存在"); } // 验证旧密码 if (!passwordEncoder.matches(oldPassword, user.getPassword())) { throw new BusinessException("原密码不正确"); } user.setPassword(passwordEncoder.encode(newPassword)); return userMapper.updateById(user) > 0; } /** * 为用户分配角色 */ @Transactional(rollbackFor = Exception.class) public boolean assignRoles(Long userId, List roleIds) { User user = getUserById(userId); if (user == null) { throw new BusinessException("用户不存在"); } // 将角色ID列表转换为逗号分隔的字符串 if (roleIds != null && !roleIds.isEmpty()) { String roleIdsStr = roleIds.stream() .map(String::valueOf) .reduce((a, b) -> a + "," + b) .orElse(""); user.setRoleIds(roleIdsStr); userMapper.updateById(user); } else { user.setRoleIds(null); userMapper.updateById(user); } return true; } /** * 获取用户的角色ID列表 */ public List getUserRoleIds(Long userId) { User user = getUserById(userId); if (user == null || user.getRoleIds() == null || user.getRoleIds().isEmpty()) { return List.of(); } // 将逗号分隔的字符串转换为Long列表 return java.util.Arrays.stream(user.getRoleIds().split(",")) .map(String::trim) .filter(s -> !s.isEmpty()) .map(Long::parseLong) .toList(); } /** * 启用/禁用用户 */ @Transactional(rollbackFor = Exception.class) public boolean toggleUserStatus(Long id, Integer status) { User user = new User(); user.setId(id); user.setStatus(status); return userMapper.updateById(user) > 0; } /** * 获取全部用户列表(不分页) */ public List getAllUsers() { return userMapper.selectList( new LambdaQueryWrapper() .orderByAsc(User::getDeptId) .orderByDesc(User::getCreateTime) ); } /** * 调整用户所属部门 */ @Transactional(rollbackFor = Exception.class) public boolean updateUserDepartment(Long userId, Long deptId) { log.info("开始调整用户部门: userId={}, 新deptId={}", userId, deptId); User user = getUserById(userId); if (user == null) { log.error("用户不存在: userId={}", userId); throw new BusinessException("用户不存在"); } log.info("当前用户信息: username={}, 原deptId={}, 原deptName={}", user.getUsername(), user.getDeptId(), user.getDeptName()); // 使用 LambdaUpdateWrapper 强制更新,包括 null 值 LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(User::getId, userId); if (deptId == null || deptId == 0) { // 清除部门 log.info("清除用户部门: userId={}", userId); updateWrapper.set(User::getDeptId, null); updateWrapper.set(User::getDeptName, null); } else { Department department = getDepartmentOrThrow(deptId); log.info("设置新部门: deptId={}, deptName={}", department.getId(), department.getDeptName()); updateWrapper.set(User::getDeptId, department.getId()); updateWrapper.set(User::getDeptName, department.getDeptName()); // 确保新部门有积分池(如果不存在则自动创建) teamScorePoolService.upsertPool(department.getId(), department.getDeptName()); log.info("已确保部门积分池存在: deptId={}, deptName={}", department.getId(), department.getDeptName()); } int rows = userMapper.update(null, updateWrapper); log.info("更新结果: 影响行数={}", rows); return rows > 0; } /** * 尝试匹配系统部门(根据钉钉部门名称) */ private Long matchSystemDepartment(List> dingDepartments) { if (dingDepartments == null || dingDepartments.isEmpty()) { return null; } // 获取所有系统部门 List systemDepts = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getStatus, 1) .eq(Department::getDeleted, 0) ); for (Map dingDept : dingDepartments) { String dingDeptName = (String) dingDept.get("name"); Object dingDeptIdObj = dingDept.get("dept_id"); String dingDeptId = dingDeptIdObj != null ? dingDeptIdObj.toString() : null; // 1. 优先通过钉钉部门ID精确匹配 if (dingDeptId != null) { Department matched = systemDepts.stream() .filter(d -> dingDeptId.equals(d.getDingDeptId())) .findFirst() .orElse(null); if (matched != null) { log.info("通过钉钉部门ID精确匹配: {} -> {}", dingDeptId, matched.getDeptName()); return matched.getId(); } } // 2. 通过部门名称模糊匹配 if (dingDeptName != null) { Department matched = systemDepts.stream() .filter(d -> d.getDeptName().contains(dingDeptName) || dingDeptName.contains(d.getDeptName())) .findFirst() .orElse(null); if (matched != null) { log.info("通过部门名称模糊匹配: {} -> {}", dingDeptName, matched.getDeptName()); return matched.getId(); } } } return null; } /** * 同步钉钉用户信息到系统 */ private void syncUserFromDingTalk(User user, Map detailedInfo, String dingtalkUserId, List> dingDepartments) { boolean needUpdate = false; // 1. 同步基本信息 if (detailedInfo != null) { if (!StringUtils.hasText(user.getRealName()) && detailedInfo.get("name") != null) { user.setRealName((String) detailedInfo.get("name")); needUpdate = true; } if (!StringUtils.hasText(user.getAvatar()) && detailedInfo.get("avatar") != null) { user.setAvatar((String) detailedInfo.get("avatar")); needUpdate = true; } if (!StringUtils.hasText(user.getPhone()) && detailedInfo.get("mobile") != null) { user.setPhone((String) detailedInfo.get("mobile")); needUpdate = true; } } // 2. 同步钉钉userId if (!StringUtils.hasText(user.getDingUserId()) && dingtalkUserId != null) { user.setDingUserId(dingtalkUserId); needUpdate = true; } // 3. 同步部门(支持多部门) if (dingDepartments != null && !dingDepartments.isEmpty()) { // 匹配多个部门 List matchedDeptIds = matchSystemDepartments(dingDepartments); if (!matchedDeptIds.isEmpty()) { // 同步多个部门到sys_user_department表 // 第一个匹配的部门作为主部门 Long primaryDeptId = matchedDeptIds.get(0); setUserDepartments(user.getId(), matchedDeptIds, primaryDeptId); // 更新sys_user表的主部门(向后兼容) Department primaryDept = departmentMapper.selectById(primaryDeptId); if (primaryDept != null) { user.setDeptId(primaryDeptId); user.setDeptName(primaryDept.getDeptName()); // 根据主部门类型设置角色(但如果用户已经是管理员,保留管理员角色) String currentRoleIds = user.getRoleIds(); log.info("syncUserFromDingTalk - 检查管理员角色: userId={}, currentRoleIds={}", user.getId(), currentRoleIds); boolean isAdmin = currentRoleIds != null && (currentRoleIds.contains("1") || currentRoleIds.contains("2")); log.info("syncUserFromDingTalk - isAdmin={}, currentRoleIds.contains('1')={}, currentRoleIds.contains('2')={}", isAdmin, currentRoleIds != null && currentRoleIds.contains("1"), currentRoleIds != null && currentRoleIds.contains("2")); if (!isAdmin) { // 只有非管理员用户才根据部门类型设置角色 String newRoleIds = "receiving".equals(primaryDept.getDeptType()) ? "5" : "6"; log.info("syncUserFromDingTalk - 非管理员用户,设置角色: userId={}, newRoleIds={}", user.getId(), newRoleIds); user.setRoleIds(newRoleIds); } else { log.info("用户已是管理员,保留管理员角色: userId={}, roleIds={}", user.getId(), currentRoleIds); } user.setStatus(1); // 启用用户 needUpdate = true; log.info("同步用户多个部门: userId={}, 部门数量={}, 主部门={}", user.getId(), matchedDeptIds.size(), primaryDept.getDeptName()); } } else { // 无法匹配,使用第一个可用部门作为默认值 List availableDepts = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getStatus, 1) .eq(Department::getDeleted, 0) .orderByAsc(Department::getSortOrder) .orderByAsc(Department::getId) ); if (!availableDepts.isEmpty()) { Department defaultDept = availableDepts.get(0); setUserDepartments(user.getId(), List.of(defaultDept.getId()), defaultDept.getId()); user.setDeptId(defaultDept.getId()); user.setDeptName(defaultDept.getDeptName()); // 根据部门类型设置角色(但如果用户已经是管理员,保留管理员角色) String currentRoleIds = user.getRoleIds(); log.info("syncUserFromDingTalk - 检查管理员角色(默认部门): userId={}, currentRoleIds={}", user.getId(), currentRoleIds); boolean isAdmin = currentRoleIds != null && (currentRoleIds.contains("1") || currentRoleIds.contains("2")); log.info("syncUserFromDingTalk - isAdmin={}", isAdmin); if (!isAdmin) { // 只有非管理员用户才根据部门类型设置角色 String newRoleIds = "receiving".equals(defaultDept.getDeptType()) ? "5" : "6"; log.info("syncUserFromDingTalk - 非管理员用户,设置角色: userId={}, newRoleIds={}", user.getId(), newRoleIds); user.setRoleIds(newRoleIds); } else { log.info("用户已是管理员,保留管理员角色: userId={}, roleIds={}", user.getId(), currentRoleIds); } user.setStatus(1); // 启用用户 needUpdate = true; log.warn("已有用户无法匹配,使用默认部门: userId={}, deptId={}, deptName={}, 钉钉部门: {}", user.getId(), defaultDept.getId(), defaultDept.getDeptName(), dingDepartments.stream() .map(d -> String.valueOf(d.get("name"))) .collect(Collectors.joining(", "))); } } } if (needUpdate) { userMapper.updateById(user); log.info("钉钉用户信息已同步: userId={}", user.getId()); } } /** * 处理钉钉OAuth登录 */ @Transactional(rollbackFor = Exception.class) private LoginVO handleDingTalkLogin(String code) { log.info("开始钉钉OAuth登录流程, code: {}...", code.substring(0, Math.min(10, code.length()))); try { // 1. 获取用户访问令牌 Map tokenData = dingTalkService.getUserAccessToken(code); String accessToken = (String) tokenData.get("accessToken"); if (accessToken == null) { throw new BusinessException(ResultCode.DINGTALK_AUTH_ERROR, "获取钉钉访问令牌失败"); } // 2. 获取用户信息(包含unionId) Map userMeData = dingTalkService.getUserInfo(accessToken); String unionId = (String) userMeData.get("unionId"); String nick = (String) userMeData.get("nick"); String avatarUrl = (String) userMeData.get("avatarUrl"); if (unionId == null) { throw new BusinessException(ResultCode.DINGTALK_AUTH_ERROR, "获取钉钉用户unionId失败"); } log.info("钉钉用户信息: unionId={}, nick={}", unionId, nick); // 3. 根据unionId获取userId(用于获取更详细信息) String dingtalkUserId = dingTalkService.getUserIdByUnionId(unionId); Map detailedInfo = null; List> dingDepartments = new ArrayList<>(); if (dingtalkUserId != null) { detailedInfo = dingTalkService.getDetailedUserInfo(dingtalkUserId); if (detailedInfo != null) { log.info("获取到详细用户信息: userId={}", dingtalkUserId); // 获取钉钉部门信息 dingDepartments = (List>) detailedInfo.get("departments"); if (dingDepartments != null && !dingDepartments.isEmpty()) { String deptNames = dingDepartments.stream() .map(d -> String.valueOf(d.get("name"))) .collect(Collectors.joining(", ")); log.info("钉钉用户所属部门: {}", deptNames); } } } // 4. 查找或创建用户 // 使用unionId作为唯一标识 String email = "dingtalk_" + unionId + "@dingtalk.local"; User user = userMapper.selectOne( new LambdaQueryWrapper() .eq(User::getEmail, email) ); if (user == null) { // 首次登录:创建用户但不分配部门 // 需要用户手动选择部门类型和具体部门 log.info("钉钉用户首次登录,创建临时账号: email={}", email); // 尝试匹配系统部门(支持多部门) List matchedDeptIds = matchSystemDepartments(dingDepartments); String username = detailedInfo != null && detailedInfo.get("job_number") != null ? (String) detailedInfo.get("job_number") : "DT_" + unionId.substring(0, 8); user = new User(); user.setUsername(username); user.setEmail(email); user.setPassword(passwordEncoder.encode(UUID.randomUUID().toString())); user.setRealName(detailedInfo != null && detailedInfo.get("name") != null ? (String) detailedInfo.get("name") : nick); user.setAvatar(detailedInfo != null && detailedInfo.get("avatar") != null ? (String) detailedInfo.get("avatar") : avatarUrl); user.setPhone(detailedInfo != null ? (String) detailedInfo.get("mobile") : null); user.setDingUserId(dingtalkUserId); user.setStatus(1); user.setDeleted(0); if (!matchedDeptIds.isEmpty()) { // 自动匹配到多个部门,第一个作为主部门 Long primaryDeptId = matchedDeptIds.get(0); Department primaryDept = departmentMapper.selectById(primaryDeptId); user.setDeptId(primaryDeptId); user.setDeptName(primaryDept.getDeptName()); // 根据主部门类型设置默认角色 user.setRoleIds("receiving".equals(primaryDept.getDeptType()) ? "5" : "6"); log.info("自动匹配到系统部门(多部门): userId={}, 部门数量={}, 主部门={}", username, matchedDeptIds.size(), primaryDept.getDeptName()); } else { // 无法自动匹配,使用第一个可用部门作为默认值 List availableDepts = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getStatus, 1) .eq(Department::getDeleted, 0) .orderByAsc(Department::getSortOrder) .orderByAsc(Department::getId) ); if (!availableDepts.isEmpty()) { // 使用第一个可用部门作为默认值 Department defaultDept = availableDepts.get(0); user.setDeptId(defaultDept.getId()); user.setDeptName(defaultDept.getDeptName()); user.setRoleIds("receiving".equals(defaultDept.getDeptType()) ? "5" : "6"); matchedDeptIds = List.of(defaultDept.getId()); log.warn("无法自动匹配系统部门,使用默认部门: deptId={}, deptName={}, 钉钉部门: {}", defaultDept.getId(), defaultDept.getDeptName(), dingDepartments.stream() .map(d -> String.valueOf(d.get("name"))) .collect(Collectors.joining(", "))); } else { // 如果系统没有任何部门,才需要用户手动选择 String deptNamesStr = dingDepartments.stream() .map(d -> String.valueOf(d.get("name"))) .collect(Collectors.joining(", ")); log.warn("无法自动匹配系统部门,且系统无可用部门,钉钉部门: {}", deptNamesStr); user.setDeptId(null); user.setDeptName(null); user.setRoleIds(null); user.setStatus(0); // 暂时禁用,待选择部门后启用 } } userMapper.insert(user); // 同步多个部门到sys_user_department表 if (!matchedDeptIds.isEmpty()) { Long primaryDeptId = matchedDeptIds.get(0); setUserDepartments(user.getId(), matchedDeptIds, primaryDeptId); } log.info("钉钉用户创建成功: userId={}, username={}, needSelectDept={}", user.getId(), user.getUsername(), user.getDeptId() == null); // 如果需要选择部门(系统无任何可用部门的情况),抛出特殊异常 if (user.getDeptId() == null) { String deptInfo = dingDepartments.isEmpty() ? "无钉钉部门信息" : dingDepartments.stream() .map(d -> String.valueOf(d.get("name"))) .collect(Collectors.joining(",")); throw new BusinessException(ResultCode.PARAM_ERROR, "NEED_SELECT_DEPT:" + user.getId() + ":" + deptInfo); } } else { // 已有用户:同步更新信息 log.info("钉钉用户已存在,同步更新: userId={}, 当前roleIds={}", user.getId(), user.getRoleIds()); syncUserFromDingTalk(user, detailedInfo, dingtalkUserId, dingDepartments); log.info("钉钉用户同步完成: userId={}, 最终roleIds={}", user.getId(), user.getRoleIds()); } // 5. 生成Token String token = jwtUtil.generateToken(user.getId(), user.getUsername()); String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername()); return LoginVO.builder() .token(token) .refreshToken(refreshToken) .userId(user.getId()) .username(user.getUsername()) .realName(user.getRealName()) .avatar(user.getAvatar()) .roleIds(user.getRoleIds()) .build(); } catch (BusinessException e) { throw e; } catch (Exception e) { log.error("钉钉登录失败", e); throw new BusinessException(ResultCode.DINGTALK_AUTH_ERROR, "钉钉登录失败: " + e.getMessage()); } } /** * 完成钉钉用户设置(选择部门) */ @Transactional(rollbackFor = Exception.class) public LoginVO completeDingTalkUserSetup(Long userId, Long deptId) { log.info("完成钉钉用户设置: userId={}, deptId={}", userId, deptId); // 1. 获取用户 User user = userMapper.selectById(userId); if (user == null) { throw new BusinessException(ResultCode.USER_NOT_EXIST); } // 2. 获取部门 Department dept = departmentMapper.selectById(deptId); if (dept == null) { throw new BusinessException(ResultCode.DEPT_NOT_EXIST); } // 3. 更新用户部门和角色 user.setDeptId(deptId); user.setDeptName(dept.getDeptName()); // 根据部门类型设置角色 user.setRoleIds("receiving".equals(dept.getDeptType()) ? "5" : "6"); user.setStatus(1); // 启用用户 userMapper.updateById(user); log.info("钉钉用户部门设置完成: userId={}, deptName={}, roleIds={}", userId, dept.getDeptName(), user.getRoleIds()); // 4. 生成Token并返回 String token = jwtUtil.generateToken(user.getId(), user.getUsername()); String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername()); return LoginVO.builder() .token(token) .refreshToken(refreshToken) .userId(user.getId()) .username(user.getUsername()) .realName(user.getRealName()) .avatar(user.getAvatar()) .roleIds(user.getRoleIds()) .build(); } /** * 用户登录 */ public LoginVO login(LoginDTO loginDTO) { String loginType = loginDTO.getLoginType(); if ("password".equals(loginType)) { // 用户名密码登录 User user = getUserByUsername(loginDTO.getUsername()); if (user == null) { throw new BusinessException(ResultCode.USER_NOT_EXIST); } if (!passwordEncoder.matches(loginDTO.getPassword(), user.getPassword())) { throw new BusinessException(ResultCode.PASSWORD_ERROR); } if (user.getStatus() == 0) { throw new BusinessException(ResultCode.USER_DISABLED); } // 生成Token String token = jwtUtil.generateToken(user.getId(), user.getUsername()); String refreshToken = jwtUtil.generateRefreshToken(user.getId(), user.getUsername()); return LoginVO.builder() .token(token) .refreshToken(refreshToken) .userId(user.getId()) .username(user.getUsername()) .realName(user.getRealName()) .avatar(user.getAvatar()) .roleIds(user.getRoleIds()) .build(); } else if ("dingtalk".equals(loginType)) { // 钉钉OAuth 2.0登录 return handleDingTalkLogin(loginDTO.getAuthCode()); } else if ("casdoor".equals(loginType)) { // Casdoor登录 - 暂时返回错误,待后续实现 throw new BusinessException(ResultCode.PARAM_ERROR, "Casdoor登录功能尚未开放"); } else { throw new BusinessException(ResultCode.PARAM_ERROR, "不支持的登录类型"); } } /** * 根据Token获取用户信息 */ public UserInfoVO getUserInfoByToken(String token) { if (!StringUtils.hasText(token)) { throw new BusinessException(ResultCode.UNAUTHORIZED); } if (!jwtUtil.validateToken(token)) { throw new BusinessException(ResultCode.UNAUTHORIZED, "Token无效或已过期"); } String username = jwtUtil.getUsernameFromToken(token); log.info("getUserInfoByToken - username: {}", username); User user = getUserByUsername(username); if (user == null) { throw new BusinessException(ResultCode.USER_NOT_EXIST); } log.info("getUserInfoByToken - 用户: id={}, username={}, deptId={}, deptName={}", user.getId(), user.getUsername(), user.getDeptId(), user.getDeptName()); // 获取用户角色代码列表 List roleCodes = List.of(); try { var roles = roleService.getRolesByUserId(user.getId()); roleCodes = roles.stream() .map(role -> role.getRoleCode()) .toList(); log.info("getUserInfoByToken - 用户角色: {}", roleCodes); } catch (Exception e) { log.warn("获取用户角色失败: userId={}, error={}", user.getId(), e.getMessage()); } // 检查是否为部门负责人 Integer isDepartmentLeader = 0; try { boolean isLeader = departmentService.isUserDepartmentLeader(user.getId()); isDepartmentLeader = isLeader ? 1 : 0; log.info("getUserInfoByToken - 是否为部门负责人: userId={}, isLeader={}", user.getId(), isLeader); } catch (Exception e) { log.warn("检查部门负责人状态失败: userId={}, error={}", user.getId(), e.getMessage()); } // 获取用户的所有部门列表 List departments = List.of(); try { List userDepts = userDepartmentMapper.selectList( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, user.getId()) ); if (!userDepts.isEmpty()) { List deptIds = userDepts.stream() .map(UserDepartment::getDeptId) .collect(Collectors.toList()); List depts = departmentMapper.selectBatchIds(deptIds); // 构建部门VO列表,保持isPrimary顺序 Map deptMap = depts.stream() .collect(Collectors.toMap(Department::getId, d -> d)); departments = userDepts.stream() .map(ud -> { Department dept = deptMap.get(ud.getDeptId()); if (dept != null) { return UserDepartmentVO.builder() .deptId(dept.getId()) .deptName(dept.getDeptName()) .isPrimary(ud.getIsPrimary()) .build(); } return null; }) .filter(d -> d != null) .collect(Collectors.toList()); } } catch (Exception e) { log.warn("获取用户部门列表失败: userId={}, error={}", user.getId(), e.getMessage()); } UserInfoVO userInfoVO = UserInfoVO.builder() .id(user.getId()) .username(user.getUsername()) .realName(user.getRealName()) .email(user.getEmail()) .phone(user.getPhone()) .avatar(user.getAvatar()) .deptId(user.getDeptId()) .deptName(user.getDeptName()) .departments(departments) .roleIds(user.getRoleIds()) .roles(roleCodes) .status(user.getStatus()) .isDepartmentLeader(isDepartmentLeader) .build(); log.info("getUserInfoByToken - 返回VO: id={}, deptId={}, deptName={}, roleIds={}, roles={}, isDepartmentLeader={}", userInfoVO.getId(), userInfoVO.getDeptId(), userInfoVO.getDeptName(), userInfoVO.getRoleIds(), roleCodes, isDepartmentLeader); return userInfoVO; } /** * 刷新Token */ public LoginVO refreshToken(String authHeader) { String token = jwtUtil.extractTokenFromHeader(authHeader); if (!StringUtils.hasText(token)) { throw new BusinessException(ResultCode.UNAUTHORIZED); } if (!jwtUtil.validateToken(token)) { throw new BusinessException(ResultCode.UNAUTHORIZED, "Token无效或已过期"); } String username = jwtUtil.getUsernameFromToken(token); Long userId = jwtUtil.getUserIdFromToken(token); User user = getUserById(userId); if (user == null) { throw new BusinessException(ResultCode.USER_NOT_EXIST); } // 生成新的Token String newToken = jwtUtil.generateToken(userId, username); String newRefreshToken = jwtUtil.generateRefreshToken(userId, username); return LoginVO.builder() .token(newToken) .refreshToken(newRefreshToken) .userId(user.getId()) .username(user.getUsername()) .realName(user.getRealName()) .avatar(user.getAvatar()) .roleIds(user.getRoleIds()) .build(); } // ==================== 多部门支持方法 ==================== /** * 获取用户的所有部门ID列表 */ public List getUserDepartmentIds(Long userId) { List userDepts = userDepartmentMapper.selectList( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) ); return userDepts.stream() .map(UserDepartment::getDeptId) .collect(Collectors.toList()); } /** * 获取用户的所有部门信息 */ public List getUserDepartments(Long userId) { List deptIds = getUserDepartmentIds(userId); if (deptIds.isEmpty()) { return List.of(); } return departmentMapper.selectBatchIds(deptIds); } /** * 添加用户到部门(如果不存在则添加) */ @Transactional(rollbackFor = Exception.class) public void addUserToDepartment(Long userId, Long deptId, boolean isPrimary) { // 检查是否已存在 UserDepartment exist = userDepartmentMapper.selectOne( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) .eq(UserDepartment::getDeptId, deptId) ); if (exist == null) { UserDepartment userDept = new UserDepartment(); userDept.setUserId(userId); userDept.setDeptId(deptId); userDept.setIsPrimary(isPrimary ? 1 : 0); userDepartmentMapper.insert(userDept); log.info("添加用户到部门: userId={}, deptId={}, isPrimary={}", userId, deptId, isPrimary); } else if (isPrimary && exist.getIsPrimary() == 0) { // 更新为主部门 exist.setIsPrimary(1); userDepartmentMapper.updateById(exist); log.info("设置为主部门: userId={}, deptId={}", userId, deptId); } // 如果设置为主部门,需要更新sys_user表的dept_id if (isPrimary) { Department dept = departmentMapper.selectById(deptId); if (dept != null) { User user = userMapper.selectById(userId); if (user != null) { user.setDeptId(deptId); user.setDeptName(dept.getDeptName()); userMapper.updateById(user); } } // 将其他部门设置为非主部门 userDepartmentMapper.update(null, new LambdaUpdateWrapper() .eq(UserDepartment::getUserId, userId) .ne(UserDepartment::getDeptId, deptId) .set(UserDepartment::getIsPrimary, 0) ); } } /** * 从部门中移除用户 */ @Transactional(rollbackFor = Exception.class) public void removeUserFromDepartment(Long userId, Long deptId) { // 检查是否为主部门 UserDepartment userDept = userDepartmentMapper.selectOne( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) .eq(UserDepartment::getDeptId, deptId) ); if (userDept != null && userDept.getIsPrimary() == 1) { // 如果是主部门,需要设置另一个部门为主部门,或清空主部门 List otherDepts = userDepartmentMapper.selectList( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) .ne(UserDepartment::getDeptId, deptId) .last("LIMIT 1") ); if (!otherDepts.isEmpty()) { // 设置第一个其他部门为主部门 UserDepartment newPrimary = otherDepts.get(0); newPrimary.setIsPrimary(1); userDepartmentMapper.updateById(newPrimary); Department newDept = departmentMapper.selectById(newPrimary.getDeptId()); if (newDept != null) { User user = userMapper.selectById(userId); if (user != null) { user.setDeptId(newDept.getId()); user.setDeptName(newDept.getDeptName()); userMapper.updateById(user); } } } else { // 没有其他部门,清空主部门 User user = userMapper.selectById(userId); if (user != null) { user.setDeptId(null); user.setDeptName(null); userMapper.updateById(user); } } } // 删除关联 userDepartmentMapper.delete( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) .eq(UserDepartment::getDeptId, deptId) ); log.info("从部门移除用户: userId={}, deptId={}", userId, deptId); } /** * 设置用户的部门列表(替换现有部门) */ @Transactional(rollbackFor = Exception.class) public void setUserDepartments(Long userId, List deptIds, Long primaryDeptId) { // 删除所有现有关联 userDepartmentMapper.delete( new LambdaQueryWrapper() .eq(UserDepartment::getUserId, userId) ); // 添加新关联 if (deptIds != null && !deptIds.isEmpty()) { for (Long deptId : deptIds) { boolean isPrimary = deptId.equals(primaryDeptId); addUserToDepartment(userId, deptId, isPrimary); } } else { // 清空部门 User user = userMapper.selectById(userId); if (user != null) { user.setDeptId(null); user.setDeptName(null); userMapper.updateById(user); } } } /** * 匹配多个系统部门(根据钉钉部门列表) */ private List matchSystemDepartments(List> dingDepartments) { if (dingDepartments == null || dingDepartments.isEmpty()) { return List.of(); } // 获取所有系统部门 List systemDepts = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getStatus, 1) .eq(Department::getDeleted, 0) ); List matchedDeptIds = new ArrayList<>(); for (Map dingDept : dingDepartments) { String dingDeptName = (String) dingDept.get("name"); Object dingDeptIdObj = dingDept.get("dept_id"); String dingDeptId = dingDeptIdObj != null ? dingDeptIdObj.toString() : null; Department matched = null; // 1. 优先通过钉钉部门ID精确匹配 if (dingDeptId != null) { matched = systemDepts.stream() .filter(d -> dingDeptId.equals(d.getDingDeptId())) .findFirst() .orElse(null); if (matched != null) { log.info("通过钉钉部门ID精确匹配: {} -> {}", dingDeptId, matched.getDeptName()); } } // 2. 通过部门名称模糊匹配 if (matched == null && dingDeptName != null) { matched = systemDepts.stream() .filter(d -> d.getDeptName().contains(dingDeptName) || dingDeptName.contains(d.getDeptName())) .findFirst() .orElse(null); if (matched != null) { log.info("通过部门名称模糊匹配: {} -> {}", dingDeptName, matched.getDeptName()); } } if (matched != null && !matchedDeptIds.contains(matched.getId())) { matchedDeptIds.add(matched.getId()); } } return matchedDeptIds; } /** * 获取设计师列表及工作量 * @param deptId 部门ID(可选,如果不传则返回所有设计师) * @return 设计师列表,包含每个设计师的进行中任务数量 */ public List> getDesignersWithWorkload(Long deptId) { log.info("获取设计师列表及工作量: deptId={}", deptId); // 查询用户列表 LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(User::getStatus, 1); // 只查询启用的用户 queryWrapper.eq(User::getDeleted, 0); // 未删除 if (deptId != null) { queryWrapper.eq(User::getDeptId, deptId); } queryWrapper.orderByAsc(User::getRealName); List users = userMapper.selectList(queryWrapper); // 构建返回结果 List> result = new ArrayList<>(); for (User user : users) { Map designerInfo = new HashMap<>(); designerInfo.put("id", user.getId()); designerInfo.put("username", user.getUsername()); designerInfo.put("realName", user.getRealName()); designerInfo.put("deptId", user.getDeptId()); // 查询进行中的任务数量(包括普通任务和子任务) int inProgressCount = 0; if (taskMapper != null) { // 查询普通任务 Long normalTaskCount = taskMapper.selectCount( new LambdaQueryWrapper() .eq(com.designhub.entity.Task::getReceiverId, user.getId()) .eq(com.designhub.entity.Task::getStatus, "in_progress") .eq(com.designhub.entity.Task::getDeleted, 0) ); // 查询子任务(TaskItem) Long subTaskCount = taskMapper.countSubTasksByReceiverId(user.getId()); inProgressCount = (normalTaskCount != null ? normalTaskCount.intValue() : 0) + (subTaskCount != null ? subTaskCount.intValue() : 0); } designerInfo.put("inProgressCount", inProgressCount); // 获取部门名称 if (user.getDeptId() != null) { Department dept = departmentMapper.selectById(user.getDeptId()); if (dept != null) { designerInfo.put("deptName", dept.getDeptName()); } } // 获取最早预计完成时间(查询该设计师进行中任务的最早deadline) if (taskMapper != null && inProgressCount > 0) { // 查询普通任务的最早deadline LambdaQueryWrapper taskQuery = new LambdaQueryWrapper<>(); taskQuery.eq(com.designhub.entity.Task::getReceiverId, user.getId()) .eq(com.designhub.entity.Task::getStatus, "in_progress") .eq(com.designhub.entity.Task::getDeleted, 0) .isNotNull(com.designhub.entity.Task::getDeadline) .orderByAsc(com.designhub.entity.Task::getDeadline) .last("LIMIT 1"); com.designhub.entity.Task earliestTask = taskMapper.selectOne(taskQuery); if (earliestTask != null && earliestTask.getDeadline() != null) { designerInfo.put("earliestDeadline", earliestTask.getDeadline()); } } result.add(designerInfo); } log.info("查询到{}个设计师", result.size()); return result; } /** * 设置/取消管理员 */ @Transactional(rollbackFor = Exception.class) public boolean setAdmin(Long userId, Integer isAdmin) { log.info("设置用户管理员身份: userId={}, isAdmin={}", userId, isAdmin); User user = getUserById(userId); if (user == null) { throw new BusinessException(ResultCode.NOT_FOUND, "用户不存在"); } // 更新管理员标识 LambdaUpdateWrapper updateWrapper = new LambdaUpdateWrapper<>(); updateWrapper.eq(User::getId, userId); updateWrapper.set(User::getIsAdmin, isAdmin != null ? isAdmin : 0); boolean result = userMapper.update(null, updateWrapper) > 0; log.info("设置管理员身份结果: userId={}, isAdmin={}, result={}", userId, isAdmin, result); return result; } /** * 检查用户是否是管理员 */ public boolean isAdminUser(Long userId) { User user = userMapper.selectById(userId); return user != null && user.getIsAdmin() != null && user.getIsAdmin() == 1; } }