1314 lines
54 KiB
Java
1314 lines
54 KiB
Java
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<User> getUserList(int pageNum, int pageSize, String keyword, Long deptId, Integer status) {
|
||
Page<User> page = new Page<>(pageNum, pageSize);
|
||
|
||
LambdaQueryWrapper<User> 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<User>()
|
||
.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<User>()
|
||
.eq(User::getPhone, user.getPhone())
|
||
);
|
||
if (count > 0) {
|
||
throw new BusinessException("手机号已存在");
|
||
}
|
||
}
|
||
|
||
// 验证邮箱唯一性
|
||
if (StringUtils.hasText(user.getEmail())) {
|
||
long count = userMapper.selectCount(
|
||
new LambdaQueryWrapper<User>()
|
||
.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<Long> 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<Long> 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<User> getAllUsers() {
|
||
return userMapper.selectList(
|
||
new LambdaQueryWrapper<User>()
|
||
.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<User> 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<Map<String, Object>> dingDepartments) {
|
||
if (dingDepartments == null || dingDepartments.isEmpty()) {
|
||
return null;
|
||
}
|
||
|
||
// 获取所有系统部门
|
||
List<Department> systemDepts = departmentMapper.selectList(
|
||
new LambdaQueryWrapper<Department>()
|
||
.eq(Department::getStatus, 1)
|
||
.eq(Department::getDeleted, 0)
|
||
);
|
||
|
||
for (Map<String, Object> 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<String, Object> detailedInfo,
|
||
String dingtalkUserId, List<Map<String, Object>> 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<Long> 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<Department> availableDepts = departmentMapper.selectList(
|
||
new LambdaQueryWrapper<Department>()
|
||
.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<String, Object> tokenData = dingTalkService.getUserAccessToken(code);
|
||
String accessToken = (String) tokenData.get("accessToken");
|
||
if (accessToken == null) {
|
||
throw new BusinessException(ResultCode.DINGTALK_AUTH_ERROR, "获取钉钉访问令牌失败");
|
||
}
|
||
|
||
// 2. 获取用户信息(包含unionId)
|
||
Map<String, Object> 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<String, Object> detailedInfo = null;
|
||
List<Map<String, Object>> dingDepartments = new ArrayList<>();
|
||
|
||
if (dingtalkUserId != null) {
|
||
detailedInfo = dingTalkService.getDetailedUserInfo(dingtalkUserId);
|
||
if (detailedInfo != null) {
|
||
log.info("获取到详细用户信息: userId={}", dingtalkUserId);
|
||
// 获取钉钉部门信息
|
||
dingDepartments = (List<Map<String, Object>>) 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<User>()
|
||
.eq(User::getEmail, email)
|
||
);
|
||
|
||
if (user == null) {
|
||
// 首次登录:创建用户但不分配部门
|
||
// 需要用户手动选择部门类型和具体部门
|
||
log.info("钉钉用户首次登录,创建临时账号: email={}", email);
|
||
|
||
// 尝试匹配系统部门(支持多部门)
|
||
List<Long> 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<Department> availableDepts = departmentMapper.selectList(
|
||
new LambdaQueryWrapper<Department>()
|
||
.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<String> 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<UserDepartmentVO> departments = List.of();
|
||
try {
|
||
List<UserDepartment> userDepts = userDepartmentMapper.selectList(
|
||
new LambdaQueryWrapper<UserDepartment>()
|
||
.eq(UserDepartment::getUserId, user.getId())
|
||
);
|
||
|
||
if (!userDepts.isEmpty()) {
|
||
List<Long> deptIds = userDepts.stream()
|
||
.map(UserDepartment::getDeptId)
|
||
.collect(Collectors.toList());
|
||
List<Department> depts = departmentMapper.selectBatchIds(deptIds);
|
||
|
||
// 构建部门VO列表,保持isPrimary顺序
|
||
Map<Long, Department> 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<Long> getUserDepartmentIds(Long userId) {
|
||
List<UserDepartment> userDepts = userDepartmentMapper.selectList(
|
||
new LambdaQueryWrapper<UserDepartment>()
|
||
.eq(UserDepartment::getUserId, userId)
|
||
);
|
||
return userDepts.stream()
|
||
.map(UserDepartment::getDeptId)
|
||
.collect(Collectors.toList());
|
||
}
|
||
|
||
/**
|
||
* 获取用户的所有部门信息
|
||
*/
|
||
public List<Department> getUserDepartments(Long userId) {
|
||
List<Long> 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<UserDepartment>()
|
||
.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<UserDepartment>()
|
||
.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<UserDepartment>()
|
||
.eq(UserDepartment::getUserId, userId)
|
||
.eq(UserDepartment::getDeptId, deptId)
|
||
);
|
||
|
||
if (userDept != null && userDept.getIsPrimary() == 1) {
|
||
// 如果是主部门,需要设置另一个部门为主部门,或清空主部门
|
||
List<UserDepartment> otherDepts = userDepartmentMapper.selectList(
|
||
new LambdaQueryWrapper<UserDepartment>()
|
||
.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<UserDepartment>()
|
||
.eq(UserDepartment::getUserId, userId)
|
||
.eq(UserDepartment::getDeptId, deptId)
|
||
);
|
||
log.info("从部门移除用户: userId={}, deptId={}", userId, deptId);
|
||
}
|
||
|
||
/**
|
||
* 设置用户的部门列表(替换现有部门)
|
||
*/
|
||
@Transactional(rollbackFor = Exception.class)
|
||
public void setUserDepartments(Long userId, List<Long> deptIds, Long primaryDeptId) {
|
||
// 删除所有现有关联
|
||
userDepartmentMapper.delete(
|
||
new LambdaQueryWrapper<UserDepartment>()
|
||
.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<Long> matchSystemDepartments(List<Map<String, Object>> dingDepartments) {
|
||
if (dingDepartments == null || dingDepartments.isEmpty()) {
|
||
return List.of();
|
||
}
|
||
|
||
// 获取所有系统部门
|
||
List<Department> systemDepts = departmentMapper.selectList(
|
||
new LambdaQueryWrapper<Department>()
|
||
.eq(Department::getStatus, 1)
|
||
.eq(Department::getDeleted, 0)
|
||
);
|
||
|
||
List<Long> matchedDeptIds = new ArrayList<>();
|
||
|
||
for (Map<String, Object> 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<Map<String, Object>> getDesignersWithWorkload(Long deptId) {
|
||
log.info("获取设计师列表及工作量: deptId={}", deptId);
|
||
|
||
// 查询用户列表
|
||
LambdaQueryWrapper<User> 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<User> users = userMapper.selectList(queryWrapper);
|
||
|
||
// 构建返回结果
|
||
List<Map<String, Object>> result = new ArrayList<>();
|
||
for (User user : users) {
|
||
Map<String, Object> 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<com.designhub.entity.Task>()
|
||
.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<com.designhub.entity.Task> 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<User> 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;
|
||
}
|
||
}
|