package cn.iocoder.yudao.module.srm.service.apply; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.StrUtil; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil; import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi; import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO; import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO; import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum; import cn.iocoder.yudao.module.bpm.service.definition.BpmCategoryService; import cn.iocoder.yudao.module.bpm.service.definition.BpmProcessDefinitionService; import cn.iocoder.yudao.module.srm.controller.admin.apply.vo.SrmSupplierApplyPageReqVO; import cn.iocoder.yudao.module.srm.controller.admin.apply.vo.SrmSupplierApplySaveReqVO; import cn.iocoder.yudao.module.srm.dal.dataobject.SrmSupplierApplyDO; import cn.iocoder.yudao.module.srm.dal.dataobject.SrmSupplierDO; import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierApplyMapper; import cn.iocoder.yudao.module.srm.dal.mysql.SrmSupplierMapper; import cn.iocoder.yudao.module.srm.enums.ErrorCodeConstants; import cn.iocoder.yudao.module.srm.enums.SupplierApplyStatusEnum; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.flowable.engine.repository.ProcessDefinition; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.validation.annotation.Validated; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; /** * SRM 供应商准入申请 Service 实现类 * * 接入协同办公(BPM)后,采购/质量/财务审核在协同办公流程中完成, * SRM 侧只负责发起流程并接收流程结束事件回写状态。 */ @Service @Validated @Slf4j public class SrmSupplierApplyServiceImpl implements SrmSupplierApplyService { /** * BPM 供应商准入审批分类编码 */ private static final String SUPPLIER_APPLY_APPROVE_CATEGORY_CODE = "supplier_apply_approve"; @Resource private SrmSupplierApplyMapper supplierApplyMapper; @Resource private SrmSupplierMapper supplierMapper; @Resource private BpmProcessInstanceApi processInstanceApi; @Resource private BpmCategoryService bpmCategoryService; @Resource private BpmProcessDefinitionService bpmProcessDefinitionService; @Override public Long createApply(SrmSupplierApplySaveReqVO createReqVO) { // 校验:必须选择 SRM 供应商或手动填写供应商名称 if (createReqVO.getSupplierId() != null) { SrmSupplierDO supplier = supplierMapper.selectById(createReqVO.getSupplierId()); if (supplier == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_NOT_EXISTS); } } else if (StrUtil.isBlank(createReqVO.getSupplierName())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_SUPPLIER_NAME_REQUIRED); } SrmSupplierApplyDO apply = BeanUtils.toBean(createReqVO, SrmSupplierApplyDO.class); apply.setApplyNo(generateApplyNo()); apply.setApplyStatus(SupplierApplyStatusEnum.DRAFT.getCode()); apply.setApplyTime(LocalDateTime.now()); supplierApplyMapper.insert(apply); return apply.getId(); } @Override public void updateApply(SrmSupplierApplySaveReqVO updateReqVO) { SrmSupplierApplyDO apply = validateApplyExists(updateReqVO.getId()); // 仅待提交/已驳回可编辑 if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus()) && !SupplierApplyStatusEnum.REJECTED.getCode().equals(apply.getApplyStatus())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_DRAFT); } // 校验:必须选择 SRM 供应商或手动填写供应商名称 if (updateReqVO.getSupplierId() != null) { SrmSupplierDO supplier = supplierMapper.selectById(updateReqVO.getSupplierId()); if (supplier == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_NOT_EXISTS); } } else if (StrUtil.isBlank(updateReqVO.getSupplierName())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_SUPPLIER_NAME_REQUIRED); } SrmSupplierApplyDO updateObj = BeanUtils.toBean(updateReqVO, SrmSupplierApplyDO.class); supplierApplyMapper.updateById(updateObj); } @Override public void deleteApply(Long id) { SrmSupplierApplyDO apply = validateApplyExists(id); // 仅待提交/已驳回可删除 if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus()) && !SupplierApplyStatusEnum.REJECTED.getCode().equals(apply.getApplyStatus())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_DRAFT); } supplierApplyMapper.deleteById(id); } @Override public SrmSupplierApplyDO getApply(Long id) { return supplierApplyMapper.selectById(id); } @Override public PageResult getApplyPage(SrmSupplierApplyPageReqVO pageReqVO) { return supplierApplyMapper.selectPage(pageReqVO); } @Override @Transactional(rollbackFor = Exception.class) public void submitApply(Long id, String processDefinitionKey, Long userId) { // 1. 校验存在 + 待提交/已驳回状态 SrmSupplierApplyDO apply = validateApplyExists(id); if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus()) && !SupplierApplyStatusEnum.REJECTED.getCode().equals(apply.getApplyStatus())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_DRAFT); } // 2. 创建 BPM 流程实例(采购审核→质量审核→财务审核,流程与审批人在协同办公配置) String processInstanceId = processInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO() .setProcessDefinitionKey(processDefinitionKey) .setBusinessKey(String.valueOf(id))); // 3. 更新申请状态为审批中,回存流程实例编号 supplierApplyMapper.updateById(new SrmSupplierApplyDO() .setId(id) .setApplyStatus(SupplierApplyStatusEnum.PROCESS.getCode()) .setProcessInstanceId(processInstanceId)); } @Override public List> getSupplierApplyApproveProcessDefinitionList() { // 1. 校验分类和流程定义是否存在 validateSupplierApplyApproveCategoryAndProcessDefinition(); // 2. 获取分类下的流程定义信息 List definitionInfoList = bpmProcessDefinitionService .getProcessDefinitionInfoListByCategory(SUPPLIER_APPLY_APPROVE_CATEGORY_CODE); if (CollUtil.isEmpty(definitionInfoList)) { return Collections.emptyList(); } // 3. 遍历获取流程定义详情,过滤激活状态,保留最新版本 Map latestVersionMap = new HashMap<>(); for (BpmProcessDefinitionInfoDO info : definitionInfoList) { ProcessDefinition pd = bpmProcessDefinitionService.getProcessDefinition(info.getProcessDefinitionId()); if (pd == null || pd.isSuspended()) { continue; } ProcessDefinition existing = latestVersionMap.get(pd.getKey()); if (existing == null || pd.getVersion() > existing.getVersion()) { latestVersionMap.put(pd.getKey(), pd); } } // 4. 返回流程定义列表 List> result = new ArrayList<>(); for (ProcessDefinition pd : latestVersionMap.values()) { Map item = new HashMap<>(); item.put("id", pd.getId()); item.put("key", pd.getKey()); item.put("name", pd.getName()); result.add(item); } return result; } @Override @Transactional(rollbackFor = Exception.class) public void updateSupplierApplyAuditStatus(Long id, Integer bpmResult) { // 1. 校验存在 SrmSupplierApplyDO apply = validateApplyExists(id); if (!SupplierApplyStatusEnum.PROCESS.getCode().equals(apply.getApplyStatus())) { log.warn("[updateSupplierApplyAuditStatus] 准入申请({}) 不处于审批中状态", id); return; } // 2. 根据审批结果更新状态 Integer newStatus = convertBpmResultToStatus(bpmResult); if (newStatus != null) { supplierApplyMapper.updateById(new SrmSupplierApplyDO() .setId(id).setApplyStatus(newStatus)); } // 3. 审批通过:自动创建/关联供应商档案 if (SupplierApplyStatusEnum.APPROVED.getCode().equals(newStatus)) { approve(id); } } @Override @Transactional(rollbackFor = Exception.class) public void ensureSupplierApproved(Long supplierId) { if (supplierId == null) { return; } // 校验供应商存在 SrmSupplierDO supplier = supplierMapper.selectById(supplierId); if (supplier == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.SUPPLIER_NOT_EXISTS); } // 已准入则无需处理 if (supplierApplyMapper.existsApprovedBySupplierId(supplierId)) { return; } // 自动创建准入申请并直接置为已通过(定标自动准入,不走协同办公) SrmSupplierApplyDO apply = new SrmSupplierApplyDO(); apply.setApplyNo(generateApplyNo()); apply.setSupplierId(supplierId); apply.setSupplierName(supplier.getName()); apply.setSupplierType(supplier.getType()); apply.setCompanyNature(supplier.getCompanyNature()); apply.setRegisteredCapital(supplier.getRegisteredCapital()); apply.setBusinessLicenseNo(supplier.getBusinessLicenseNo()); apply.setTaxNo(supplier.getTaxNo()); apply.setAddress(supplier.getAddress()); apply.setBankName(supplier.getBankName()); apply.setBankAccount(supplier.getBankAccount()); apply.setContactName(supplier.getContactName()); apply.setContactMobile(supplier.getContactMobile()); apply.setContactEmail(supplier.getContactEmail()); apply.setRemark("中标生成采购订单时自动准入并通过"); apply.setApplyStatus(SupplierApplyStatusEnum.APPROVED.getCode()); apply.setApplyTime(LocalDateTime.now()); supplierApplyMapper.insert(apply); } @Override @Transactional(rollbackFor = Exception.class) public void approve(Long id) { SrmSupplierApplyDO apply = validateApplyExists(id); if (!SupplierApplyStatusEnum.APPROVED.getCode().equals(apply.getApplyStatus())) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_PENDING); } // 如果已关联 SRM 供应商,不需要重复创建 if (apply.getSupplierId() == null) { SrmSupplierDO supplier = new SrmSupplierDO(); supplier.setName(apply.getSupplierName()); supplier.setCompanyNature(apply.getCompanyNature()); supplier.setRegisteredCapital(apply.getRegisteredCapital()); supplier.setBusinessLicenseNo(apply.getBusinessLicenseNo()); supplier.setTaxNo(apply.getTaxNo()); supplier.setAddress(apply.getAddress()); supplier.setBankName(apply.getBankName()); supplier.setBankAccount(apply.getBankAccount()); supplier.setContactName(apply.getContactName()); supplier.setContactMobile(apply.getContactMobile()); supplier.setContactEmail(apply.getContactEmail()); supplier.setRemark(apply.getRemark()); supplier.setStatus(0); supplierMapper.insert(supplier); apply.setSupplierId(supplier.getId()); } supplierApplyMapper.updateById(apply); } // ========== 校验方法 ========== private SrmSupplierApplyDO validateApplyExists(Long id) { SrmSupplierApplyDO apply = supplierApplyMapper.selectById(id); if (apply == null) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_NOT_EXISTS); } return apply; } /** * 校验供应商准入审批分类存在 */ private void validateSupplierApplyApproveCategoryAndProcessDefinition() { List categories = bpmCategoryService.getCategoryListByCode( Collections.singletonList(SUPPLIER_APPLY_APPROVE_CATEGORY_CODE)); if (CollUtil.isEmpty(categories)) { throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_BPM_CATEGORY_NOT_EXISTS); } } /** * 转换 BPM 审批结果为业务状态 */ private Integer convertBpmResultToStatus(Integer bpmResult) { if (BpmTaskStatusEnum.APPROVE.getStatus().equals(bpmResult)) { return SupplierApplyStatusEnum.APPROVED.getCode(); } else if (BpmTaskStatusEnum.REJECT.getStatus().equals(bpmResult)) { return SupplierApplyStatusEnum.REJECTED.getCode(); } else if (BpmTaskStatusEnum.CANCEL.getStatus().equals(bpmResult)) { return SupplierApplyStatusEnum.CANCEL.getCode(); } return null; } /** * 生成申请编号 */ private String generateApplyNo() { String prefix = "AP"; String date = java.time.LocalDate.now().format(java.time.format.DateTimeFormatter.ofPattern("yyyyMM")); int seq = (int) (Math.random() * 9000) + 1000; return prefix + date + String.format("%04d", seq); } }