package cn.iocoder.yudao.module.system.service.approval; import cn.hutool.core.collection.CollUtil; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.module.system.api.user.AdminUserApi; import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO; import cn.iocoder.yudao.module.system.controller.admin.approval.vo.ApprovalConfigPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.approval.vo.ApprovalConfigRespVO; import cn.iocoder.yudao.module.system.controller.admin.approval.vo.ApprovalConfigSaveReqVO; import cn.iocoder.yudao.module.system.dal.dataobject.approval.ApprovalConfigDO; import cn.iocoder.yudao.module.system.dal.dataobject.approval.ApprovalConfigUserDO; import cn.iocoder.yudao.module.system.dal.mysql.approval.SystemApprovalConfigMapper; import cn.iocoder.yudao.module.system.dal.mysql.approval.SystemApprovalConfigUserMapper; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.util.*; import java.util.stream.Collectors; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList; import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*; /** * 通用审批配置 Service 实现类 * * 设计说明:本配置替代原有基于 BPM(Flowable)流程定义的审批方式,采用轻量级单级审批。 * 同一业务类型可配置多个审批人,多人之间为【或签】关系,即任意一人审核通过即推进业务状态。 * * @author 超级管理员 */ @Service @Validated @Slf4j public class ApprovalConfigServiceImpl implements ApprovalConfigService { @Resource private SystemApprovalConfigMapper systemApprovalConfigMapper; @Resource private SystemApprovalConfigUserMapper systemApprovalConfigUserMapper; @Resource private AdminUserApi adminUserApi; @Override @Transactional(rollbackFor = Exception.class) public Long saveApprovalConfig(ApprovalConfigSaveReqVO saveReqVO) { // 1. 校验业务类型编码唯一 validateBizTypeUnique(saveReqVO.getId(), saveReqVO.getBizType()); // 2. 校验审批人存在且未被禁用 List userIds = saveReqVO.getUserIds() == null ? Collections.emptyList() : saveReqVO.getUserIds().stream().filter(Objects::nonNull).distinct().collect(Collectors.toList()); validateApproverUsers(userIds); // 3. 保存主表 ApprovalConfigDO config = BeanUtils.toBean(saveReqVO, ApprovalConfigDO.class); if (config.getId() == null) { config.setId(null); systemApprovalConfigMapper.insert(config); } else { validateApprovalConfigExists(config.getId()); systemApprovalConfigMapper.updateById(config); } // 4. 保存审批人:整体覆盖(先物理删除旧的,再插入新的) systemApprovalConfigUserMapper.deleteByConfigId(config.getId()); if (CollUtil.isNotEmpty(userIds)) { List userDOList = new ArrayList<>(userIds.size()); for (int i = 0; i < userIds.size(); i++) { userDOList.add(ApprovalConfigUserDO.builder() .configId(config.getId()) .userId(userIds.get(i)) .sort(i) .build()); } systemApprovalConfigUserMapper.insertBatch(userDOList); } return config.getId(); } @Override @Transactional(rollbackFor = Exception.class) public void deleteApprovalConfig(Long id) { validateApprovalConfigExists(id); systemApprovalConfigMapper.deleteById(id); systemApprovalConfigUserMapper.deleteByConfigId(id); } @Override public ApprovalConfigDO getApprovalConfig(Long id) { return systemApprovalConfigMapper.selectById(id); } @Override public ApprovalConfigDO getApprovalConfigByBizType(String bizType) { if (bizType == null) { return null; } return systemApprovalConfigMapper.selectByBizType(bizType); } @Override public PageResult getApprovalConfigPage(ApprovalConfigPageReqVO pageReqVO) { PageResult pageResult = systemApprovalConfigMapper.selectPage(pageReqVO); if (CollUtil.isEmpty(pageResult.getList())) { return PageResult.empty(pageResult.getTotal()); } // 1. 批量查询审批人子表 List allConfigUsers = systemApprovalConfigUserMapper.selectListByConfigIds( convertSet(pageResult.getList(), ApprovalConfigDO::getId)); Map> configUserMap = allConfigUsers.stream() .collect(Collectors.groupingBy(ApprovalConfigUserDO::getConfigId)); // 2. 批量查询审批人昵称 Map nicknameMap = getUserNicknameMap( convertSet(allConfigUsers, ApprovalConfigUserDO::getUserId)); // 3. 组装返回 List list = convertList(pageResult.getList(), config -> { ApprovalConfigRespVO vo = BeanUtils.toBean(config, ApprovalConfigRespVO.class); List configUsers = configUserMap.getOrDefault(config.getId(), Collections.emptyList()); vo.setUserIds(convertList(configUsers, ApprovalConfigUserDO::getUserId)); vo.setUserNames(convertList(configUsers, user -> nicknameMap.get(user.getUserId()))); return vo; }); return new PageResult<>(list, pageResult.getTotal()); } @Override public Set getApproverUserIds(String bizType) { ApprovalConfigDO config = getApprovalConfigByBizType(bizType); if (config == null) { return Collections.emptySet(); } return getValidApproverUserIds(config.getId()); } @Override public Set validateApprovalEnabledAndGetApprovers(String bizType) { // 1. 校验业务类型已配置审批 ApprovalConfigDO config = getApprovalConfigByBizType(bizType); if (config == null) { throw exception(APPROVAL_CONFIG_NOT_EXISTS); } // 2. 校验已启用审批 if (!Boolean.TRUE.equals(config.getApprovalEnabled())) { throw exception(APPROVAL_CONFIG_NOT_ENABLED); } // 3. 校验存在有效审批人(过滤掉已删除 / 已禁用的用户) Set approverIds = getValidApproverUserIds(config.getId()); if (CollUtil.isEmpty(approverIds)) { throw exception(APPROVAL_CONFIG_APPROVER_EMPTY); } return approverIds; } @Override public void validateApprover(String bizType, Long userId) { Set approverIds = getApproverUserIds(bizType); if (userId == null || !approverIds.contains(userId)) { throw exception(APPROVAL_CONFIG_NOT_APPROVER); } } /** * 查询配置下的有效审批人编号(过滤已删除 / 已禁用的用户) * * 审批人可能在配置之后被禁用或删除,此处统一过滤, * 避免出现「配置里有人、但实际无人可审」导致单据卡在待审核状态。 * * @param configId 配置编号 * @return 有效审批人编号集合 */ private Set getValidApproverUserIds(Long configId) { List configUsers = systemApprovalConfigUserMapper.selectListByConfigId(configId); if (CollUtil.isEmpty(configUsers)) { return Collections.emptySet(); } Set userIds = convertSet(configUsers, ApprovalConfigUserDO::getUserId); return convertSet(adminUserApi.getUserList(userIds), AdminUserRespDTO::getId, user -> !CommonStatusEnum.isDisable(user.getStatus())); } private Map getUserNicknameMap(Collection userIds) { if (CollUtil.isEmpty(userIds)) { return Collections.emptyMap(); } return adminUserApi.getUserList(userIds).stream() .collect(Collectors.toMap(AdminUserRespDTO::getId, AdminUserRespDTO::getNickname, (a, b) -> a)); } private void validateApprovalConfigExists(Long id) { if (systemApprovalConfigMapper.selectById(id) == null) { throw exception(APPROVAL_CONFIG_NOT_EXISTS); } } private void validateBizTypeUnique(Long id, String bizType) { ApprovalConfigDO config = systemApprovalConfigMapper.selectByBizType(bizType); if (config != null && !config.getId().equals(id)) { throw exception(APPROVAL_CONFIG_BIZ_TYPE_DUPLICATE, bizType); } } private void validateApproverUsers(Collection userIds) { if (CollUtil.isEmpty(userIds)) { return; } List users = adminUserApi.getUserList(userIds); Map userMap = users.stream() .collect(Collectors.toMap(AdminUserRespDTO::getId, user -> user, (a, b) -> a)); for (Long userId : userIds) { AdminUserRespDTO user = userMap.get(userId); if (user == null || CommonStatusEnum.isDisable(user.getStatus())) { throw exception(APPROVAL_CONFIG_APPROVER_NOT_EXISTS); } } } }