package com.designhub.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.designhub.dto.DepartmentCreateRequest; import com.designhub.dto.DepartmentLimitSettingsRequest; import com.designhub.dto.DepartmentScoreAdjustRequest; import com.designhub.dto.DepartmentUpdateRequest; import com.designhub.entity.Brand; import com.designhub.entity.Department; import com.designhub.entity.DeptBrandRelation; import com.designhub.entity.User; import com.designhub.exception.BusinessException; import com.designhub.mapper.BrandMapper; import com.designhub.mapper.DepartmentMapper; import com.designhub.mapper.DeptBrandRelationMapper; import com.designhub.mapper.UserMapper; import com.designhub.vo.DepartmentOverviewVO; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; /** * 部门管理Service */ @Slf4j @Service @RequiredArgsConstructor public class DepartmentService { private final DepartmentMapper departmentMapper; private final TeamScorePoolService teamScorePoolService; private final UserMapper userMapper; private final DeptBrandRelationMapper deptBrandRelationMapper; private final BrandMapper brandMapper; /** * 获取所有部门列表 */ public List getAllDepartments() { return departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getStatus, 1) .orderByAsc(Department::getSortOrder) ); } /** * 获取部门树形结构 */ public List getDepartmentTree() { List allDepts = departmentMapper.selectDeptTree(); return buildTree(allDepts, 0L); } /** * 根据类型获取部门列表 */ public List getDepartmentsByType(String deptType) { return departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getDeptType, deptType) .eq(Department::getStatus, 1) .orderByAsc(Department::getSortOrder) ); } /** * 根据ID获取部门 */ public Department getDepartmentById(Long id) { return departmentMapper.selectById(id); } /** * 部门概览列表 */ public List getDepartmentOverviewList() { List departments = getAllDepartments(); Map memberCountMap = loadMemberCountMap(); Map leaderNameMap = loadLeaderNames(departments); return departments.stream() .map(dept -> buildOverviewVO(dept, memberCountMap, leaderNameMap)) .collect(Collectors.toList()); } /** * 获取单个部门概览 */ public DepartmentOverviewVO getDepartmentOverview(Long deptId) { Department department = getDepartmentById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } Map memberCountMap = loadMemberCountMap(); Map leaderNameMap = loadLeaderNames(Collections.singletonList(department)); return buildOverviewVO(department, memberCountMap, leaderNameMap); } /** * 创建部门 */ @Transactional(rollbackFor = Exception.class) public DepartmentOverviewVO createDepartment(DepartmentCreateRequest request) { validateDeptType(request.getDeptType()); // 验证负责人ID是否有效 if (request.getLeaderId() != null && request.getLeaderId() > 0) { User leader = userMapper.selectById(request.getLeaderId()); if (leader == null) { throw new BusinessException("指定的负责人不存在"); } } Department department = new Department(); department.setDeptName(request.getDeptName()); department.setDeptCode(request.getDeptCode()); department.setDeptType(request.getDeptType()); department.setParentId(request.getParentId() != null ? request.getParentId() : 0L); department.setLeaderId(request.getLeaderId()); department.setSortOrder(request.getSortOrder()); department.setStatus(request.getStatus()); department.setAllowedDemandTypes(joinAllowedTypes(request.getAllowedDemandTypes())); departmentMapper.insert(department); // 初始化积分池(需求部门和一级需求部门都需要积分池) teamScorePoolService.upsertPool(department.getId(), department.getDeptName()); if ("demand".equals(request.getDeptType()) || "primary_demand".equals(request.getDeptType())) { if (request.getInitialScoreBalance() != null) { teamScorePoolService.setBalance(department.getId(), request.getInitialScoreBalance()); department.setScoreBalance(request.getInitialScoreBalance()); } teamScorePoolService.updateLimitConfig( department.getId(), defaultLimitEnabled(request.getLimitEnabled()), defaultLimitPeriod(request.getLimitPeriod()), request.getPeriodLimit(), request.getAlertThreshold() ); department.setLimitEnabled(defaultLimitEnabled(request.getLimitEnabled())); department.setLimitPeriod(defaultLimitPeriod(request.getLimitPeriod())); department.setPeriodLimit(request.getPeriodLimit()); department.setAlertThreshold(request.getAlertThreshold()); department.setOverLimitAction( StringUtils.hasText(request.getOverLimitAction()) ? request.getOverLimitAction() : "alert" ); } return getDepartmentOverview(department.getId()); } /** * 更新部门 */ @Transactional(rollbackFor = Exception.class) public boolean updateDepartment(Long id, DepartmentUpdateRequest request) { Department existDept = getDepartmentById(id); if (existDept == null) { throw new BusinessException("部门不存在"); } if (StringUtils.hasText(request.getDeptName())) { existDept.setDeptName(request.getDeptName()); } if (StringUtils.hasText(request.getDeptCode())) { existDept.setDeptCode(request.getDeptCode()); } if (StringUtils.hasText(request.getDeptType())) { validateDeptType(request.getDeptType()); existDept.setDeptType(request.getDeptType()); } // 处理parentId:允许设置为0来清除(设置为null) if (request.getParentId() != null) { Long parentId = request.getParentId(); existDept.setParentId(parentId == 0 ? null : parentId); } // 处理leaderId:前端总是发送leaderId字段 // null表示清除负责人,非null数字表示设置负责人 // 由于无法区分字段不存在和字段为null,前端必须总是发送此字段 // 如果前端不发送字段,这里getLeaderId()也会返回null,但不会更新(保持原值) // 为了支持清除,我们检查:如果leaderId字段在请求中存在(通过检查其他必填字段),则处理 // 简化方案:总是检查leaderId,如果为null且当前有值,则清除;如果为数字,则设置 // 但由于无法判断字段是否存在,我们采用:如果leaderId不为null,则更新 if (request.getLeaderId() != null) { Long leaderId = request.getLeaderId(); // 验证负责人ID是否有效(0表示清除,大于0需要验证) if (leaderId > 0) { User leader = userMapper.selectById(leaderId); if (leader == null) { throw new BusinessException("指定的负责人不存在"); } } existDept.setLeaderId(leaderId == 0 ? null : leaderId); } else if (request.getDeptName() != null) { // 如果其他字段被更新(说明这是完整的更新请求),且leaderId为null,则清除负责人 // 这是一个启发式判断:如果请求包含deptName(通常是必填字段),则认为leaderId=null是明确的清除操作 existDept.setLeaderId(null); } // 否则(leaderId为null且没有其他字段更新),不处理leaderId,保持原值 if (request.getSortOrder() != null) { existDept.setSortOrder(request.getSortOrder()); } if (request.getStatus() != null) { existDept.setStatus(request.getStatus()); } if (request.getAllowedDemandTypes() != null) { existDept.setAllowedDemandTypes(joinAllowedTypes(request.getAllowedDemandTypes())); } if (request.getLimitEnabled() != null) { existDept.setLimitEnabled(request.getLimitEnabled()); } if (request.getLimitPeriod() != null) { existDept.setLimitPeriod(request.getLimitPeriod()); } if (request.getPeriodLimit() != null) { existDept.setPeriodLimit(request.getPeriodLimit()); } if (request.getAlertThreshold() != null) { existDept.setAlertThreshold(request.getAlertThreshold()); } if (request.getOverLimitAction() != null) { existDept.setOverLimitAction(request.getOverLimitAction()); } departmentMapper.updateById(existDept); // 同步积分池(需求部门和一级需求部门都需要积分池) teamScorePoolService.upsertPool(existDept.getId(), existDept.getDeptName()); if (("demand".equals(existDept.getDeptType()) || "primary_demand".equals(existDept.getDeptType())) && (request.getLimitEnabled() != null || request.getLimitPeriod() != null || request.getPeriodLimit() != null || request.getAlertThreshold() != null)) { teamScorePoolService.updateLimitConfig( existDept.getId(), request.getLimitEnabled(), request.getLimitPeriod(), request.getPeriodLimit(), request.getAlertThreshold() ); } return true; } /** * 更新部门限制配置 */ @Transactional(rollbackFor = Exception.class) public boolean updateLimitSettings(Long deptId, DepartmentLimitSettingsRequest request) { Department department = getDepartmentById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } if (!"demand".equals(department.getDeptType()) && !"primary_demand".equals(department.getDeptType())) { throw new BusinessException("只有需求部门或一级需求部门需要配置积分限制"); } department.setLimitEnabled(request.getLimitEnabled()); department.setLimitPeriod(request.getLimitPeriod()); department.setPeriodLimit(request.getPeriodLimit()); department.setAlertThreshold(request.getAlertThreshold()); department.setOverLimitAction(request.getOverLimitAction()); departmentMapper.updateById(department); teamScorePoolService.updateLimitConfig( deptId, request.getLimitEnabled(), request.getLimitPeriod(), request.getPeriodLimit(), request.getAlertThreshold() ); return true; } /** * 删除部门(逻辑删除) */ @Transactional(rollbackFor = Exception.class) public boolean deleteDepartment(Long id) { long childCount = departmentMapper.selectCount( new LambdaQueryWrapper() .eq(Department::getParentId, id) ); if (childCount > 0) { throw new BusinessException("该部门下还有子部门,无法删除"); } return departmentMapper.deleteById(id) > 0; } /** * 设置部门负责人/主管 */ @Transactional(rollbackFor = Exception.class) public boolean setDepartmentHead(Long deptId, Long userId, String type) { Department department = getDepartmentById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } if ("head".equals(type)) { if (!"demand".equals(department.getDeptType()) && !"primary_demand".equals(department.getDeptType())) { throw new BusinessException("只有需求部门或一级需求部门可以设置负责人"); } department.setLeaderId(userId); } else if ("supervisor".equals(type)) { if (!"receiving".equals(department.getDeptType())) { throw new BusinessException("只有接单部门可以设置主管"); } department.setSupervisorId(userId); } else { throw new BusinessException("类型参数错误"); } return departmentMapper.updateById(department) > 0; } /** * 调整部门积分 */ @Transactional(rollbackFor = Exception.class) public void adjustScore(Long deptId, DepartmentScoreAdjustRequest request) { Department department = getDepartmentById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } if (!"demand".equals(department.getDeptType()) && !"primary_demand".equals(department.getDeptType())) { throw new BusinessException("只有需求部门或一级需求部门可以调整积分"); } if (request.getAmount() == null || request.getAmount() <= 0) { throw new BusinessException("调整积分必须大于0"); } String action = request.getAction().toLowerCase(); switch (action) { case "recharge" -> teamScorePoolService.rechargeScore(deptId, request.getAmount()); case "deduct" -> teamScorePoolService.deductScore(deptId, request.getAmount()); default -> throw new BusinessException("不支持的积分操作类型"); } } private void validateDeptType(String deptType) { if (!"demand".equals(deptType) && !"receiving".equals(deptType) && !"primary_demand".equals(deptType)) { throw new BusinessException("部门类型必须是 demand、receiving 或 primary_demand"); } } // ==================== 品牌关联管理 ==================== /** * 根据品牌ID获取负责的需求部门 */ public Department getDemandDeptByBrandId(Long brandId) { if (brandId == null) { return null; } Long deptId = deptBrandRelationMapper.getDeptIdByBrandId(brandId); if (deptId == null) { return null; } return departmentMapper.selectById(deptId); } /** * 设置部门负责的品牌 */ @Transactional(rollbackFor = Exception.class) public void setDeptResponsibleBrands(Long deptId, List brandIds) { Department department = getDepartmentById(deptId); if (department == null) { throw new BusinessException("部门不存在"); } if (!"demand".equals(department.getDeptType())) { throw new BusinessException("只有需求部门可以设置负责品牌"); } // 检查品牌是否已被其他部门负责 if (!CollectionUtils.isEmpty(brandIds)) { for (Long brandId : brandIds) { Long existingDeptId = deptBrandRelationMapper.getDeptIdByBrandId(brandId); if (existingDeptId != null && !existingDeptId.equals(deptId)) { Brand brand = brandMapper.selectById(brandId); Department existingDept = departmentMapper.selectById(existingDeptId); throw new BusinessException("品牌 [" + (brand != null ? brand.getName() : brandId) + "] 已被部门 [" + (existingDept != null ? existingDept.getDeptName() : existingDeptId) + "] 负责"); } } } // 删除原有关联 deptBrandRelationMapper.deleteByDeptId(deptId); // 批量插入新关联 if (!CollectionUtils.isEmpty(brandIds)) { deptBrandRelationMapper.batchInsert(deptId, brandIds); } log.info("设置部门 {} 负责品牌: {}", deptId, brandIds); } /** * 获取部门负责的品牌列表 */ public List getDeptResponsibleBrands(Long deptId) { List brandIds = deptBrandRelationMapper.getBrandIdsByDeptId(deptId); if (CollectionUtils.isEmpty(brandIds)) { return Collections.emptyList(); } return brandMapper.selectBatchIds(brandIds); } /** * 获取部门负责的品牌ID列表 */ public List getDeptResponsibleBrandIds(Long deptId) { return deptBrandRelationMapper.getBrandIdsByDeptId(deptId); } /** * 获取所有品牌-部门关联关系 */ public List getAllBrandDeptRelations() { return deptBrandRelationMapper.getAllRelationsWithNames(); } /** * 检查部门是否为一级需求部门 */ public boolean isPrimaryDemandDept(Long deptId) { if (deptId == null) { return false; } Department dept = getDepartmentById(deptId); return dept != null && "primary_demand".equals(dept.getDeptType()); } /** * 检查部门是否可以发起任务(需求部门或一级需求部门) */ public boolean canPublishTask(Long deptId) { if (deptId == null) { return false; } Department dept = getDepartmentById(deptId); if (dept == null) { return false; } String deptType = dept.getDeptType(); return "demand".equals(deptType) || "primary_demand".equals(deptType); } /** * 获取所有一级需求部门 */ public List getPrimaryDemandDepartments() { return departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getDeptType, "primary_demand") .eq(Department::getStatus, 1) .orderByAsc(Department::getSortOrder) ); } /** * 获取所有需求部门的ID列表(只包括 demand 类型,不包括 primary_demand) * 用于跨部门成员分配功能 * 注意:一级需求部门(primary_demand)的成员不应该出现在下拉列表中 */ public List getAllDemandDeptIds() { List demandDepts = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getDeptType, "demand") // 只查询 demand 类型,不包括 primary_demand .eq(Department::getStatus, 1) .eq(Department::getDeleted, 0) ); List allDeptIds = new java.util.ArrayList<>(); for (Department dept : demandDepts) { allDeptIds.add(dept.getId()); // 递归获取所有子部门ID allDeptIds.addAll(getAllChildDeptIds(dept.getId())); } return allDeptIds; } private Integer defaultLimitEnabled(Integer limitEnabled) { return limitEnabled != null ? limitEnabled : 0; } private String defaultLimitPeriod(String limitPeriod) { return StringUtils.hasText(limitPeriod) ? limitPeriod : "MONTHLY"; } private String joinAllowedTypes(List allowedTypes) { if (CollectionUtils.isEmpty(allowedTypes)) { return null; } return allowedTypes.stream() .filter(StringUtils::hasText) .collect(Collectors.joining(",")); } private List splitAllowedTypes(String allowedTypes) { if (allowedTypes == null || allowedTypes.trim().isEmpty()) { return Collections.emptyList(); } return Arrays.stream(allowedTypes.split(",")) .map(String::trim) .filter(StringUtils::hasText) .collect(Collectors.toList()); } private Map loadMemberCountMap() { // 只统计启用且未删除的用户 return userMapper.selectList( new LambdaQueryWrapper() .eq(User::getStatus, 1) .eq(User::getDeleted, 0) ).stream() .filter(user -> user.getDeptId() != null) .collect(Collectors.groupingBy( User::getDeptId, Collectors.counting() )); } private Map loadLeaderNames(List departments) { List leaderIds = departments.stream() .map(Department::getLeaderId) .filter(Objects::nonNull) .distinct() .collect(Collectors.toList()); if (leaderIds.isEmpty()) { return Collections.emptyMap(); } return userMapper.selectBatchIds(leaderIds).stream() .collect(Collectors.toMap( User::getId, user -> StringUtils.hasText(user.getRealName()) ? user.getRealName() : user.getUsername() )); } private DepartmentOverviewVO buildOverviewVO(Department department, Map memberCountMap, Map leaderNameMap) { DepartmentOverviewVO vo = new DepartmentOverviewVO(); vo.setId(department.getId()); vo.setDeptName(department.getDeptName()); vo.setDeptCode(department.getDeptCode()); vo.setDeptType(department.getDeptType()); vo.setParentId(department.getParentId()); vo.setLeaderId(department.getLeaderId()); if (department.getLeaderId() != null && leaderNameMap != null) { vo.setLeaderName(leaderNameMap.get(department.getLeaderId())); } vo.setDescription(department.getDescription()); vo.setSortOrder(department.getSortOrder()); vo.setStatus(department.getStatus()); vo.setAllowedDemandTypes(splitAllowedTypes(department.getAllowedDemandTypes())); var pool = teamScorePoolService.getByDeptId(department.getId()); if (pool != null) { vo.setScoreBalance(pool.getBalance()); vo.setTotalRecharge(pool.getTotalRecharge()); vo.setTotalConsume(pool.getTotalConsume()); vo.setTotalEarn(pool.getTotalEarn()); vo.setLimitEnabled(pool.getLimitEnabled()); vo.setLimitPeriod(pool.getLimitPeriod()); vo.setPeriodLimit(pool.getPeriodLimit()); vo.setPeriodUsed(pool.getPeriodUsed()); vo.setAlertThreshold(pool.getAlertThreshold()); vo.setLimitAlert(teamScorePoolService.isAlert(department.getId())); } else { vo.setScoreBalance(department.getScoreBalance()); vo.setLimitEnabled(department.getLimitEnabled()); vo.setLimitPeriod(department.getLimitPeriod()); vo.setPeriodLimit(department.getPeriodLimit()); vo.setAlertThreshold(department.getAlertThreshold()); vo.setLimitAlert(false); } vo.setOverLimitAction(department.getOverLimitAction()); if (memberCountMap != null && memberCountMap.containsKey(department.getId())) { vo.setMemberCount(memberCountMap.get(department.getId()).intValue()); } else { vo.setMemberCount(0); } return vo; } /** * 构建树形结构 */ private List buildTree(List allDepts, Long parentId) { List children = allDepts.stream() .filter(dept -> { Long deptParentId = dept.getParentId(); if (deptParentId == null) { return parentId == null || parentId == 0L; } return deptParentId.equals(parentId); }) .collect(Collectors.toList()); for (Department child : children) { buildTree(allDepts, child.getId()); } return children; } /** * 获取部门的所有子部门ID(递归) */ public List getAllChildDeptIds(Long deptId) { List result = new ArrayList<>(); if (deptId == null) { return result; } // 查找直接子部门(只查询未删除且启用的部门) List children = departmentMapper.selectList( new LambdaQueryWrapper() .eq(Department::getParentId, deptId) .eq(Department::getDeleted, 0) .eq(Department::getStatus, 1) ); // 递归获取所有子部门 for (Department child : children) { result.add(child.getId()); result.addAll(getAllChildDeptIds(child.getId())); } log.info("获取部门{}的所有子部门ID: {}", deptId, result); return result; } /** * 检查用户是否为任何部门的负责人 */ public boolean isUserDepartmentLeader(Long userId) { if (userId == null) { return false; } // 查询是否有部门的 leader_id 等于该用户ID Long count = departmentMapper.selectCount( new LambdaQueryWrapper() .eq(Department::getLeaderId, userId) .eq(Department::getStatus, 1) ); return count != null && count > 0; } }