liu
昨天 a08832337f8de6a53edebbab1fb0b5a3bd1785aa
yudao-module-srm/src/main/java/cn/iocoder/yudao/module/srm/service/apply/SrmSupplierApplyServiceImpl.java
@@ -1,9 +1,17 @@
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;
@@ -13,24 +21,47 @@
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) {
@@ -54,7 +85,9 @@
    @Override
    public void updateApply(SrmSupplierApplySaveReqVO updateReqVO) {
        SrmSupplierApplyDO apply = validateApplyExists(updateReqVO.getId());
        if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus())) {
        // 仅待提交/已驳回可编辑
        if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus())
                && !SupplierApplyStatusEnum.REJECTED.getCode().equals(apply.getApplyStatus())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_DRAFT);
        }
        // 校验:必须选择 SRM 供应商或手动填写供应商名称
@@ -72,7 +105,12 @@
    @Override
    public void deleteApply(Long id) {
        validateApplyExists(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);
    }
@@ -88,59 +126,83 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitApply(Long id) {
    public void submitApply(Long id, String processDefinitionKey, Long userId) {
        // 1. 校验存在 + 待提交/已驳回状态
        SrmSupplierApplyDO apply = validateApplyExists(id);
        if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus())) {
        if (!SupplierApplyStatusEnum.DRAFT.getCode().equals(apply.getApplyStatus())
                && !SupplierApplyStatusEnum.REJECTED.getCode().equals(apply.getApplyStatus())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_DRAFT);
        }
        apply.setApplyStatus(SupplierApplyStatusEnum.PENDING_PURCHASE.getCode());
        apply.setApplyTime(LocalDateTime.now());
        supplierApplyMapper.updateById(apply);
        // 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<Map<String, Object>> getSupplierApplyApproveProcessDefinitionList() {
        // 1. 校验分类和流程定义是否存在
        validateSupplierApplyApproveCategoryAndProcessDefinition();
        // 2. 获取分类下的流程定义信息
        List<BpmProcessDefinitionInfoDO> definitionInfoList = bpmProcessDefinitionService
                .getProcessDefinitionInfoListByCategory(SUPPLIER_APPLY_APPROVE_CATEGORY_CODE);
        if (CollUtil.isEmpty(definitionInfoList)) {
            return Collections.emptyList();
        }
        // 3. 遍历获取流程定义详情,过滤激活状态,保留最新版本
        Map<String, ProcessDefinition> 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<Map<String, Object>> result = new ArrayList<>();
        for (ProcessDefinition pd : latestVersionMap.values()) {
            Map<String, Object> 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 reviewPurchase(Long id, String opinion, Boolean passed) {
    public void updateSupplierApplyAuditStatus(Long id, Integer bpmResult) {
        // 1. 校验存在
        SrmSupplierApplyDO apply = validateApplyExists(id);
        if (!SupplierApplyStatusEnum.PENDING_PURCHASE.getCode().equals(apply.getApplyStatus())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_PENDING);
        }
        apply.setPurchaseReviewOpinion(opinion);
        apply.setPurchaseReviewTime(LocalDateTime.now());
        apply.setApplyStatus(Boolean.TRUE.equals(passed)
                ? SupplierApplyStatusEnum.PENDING_QUALITY.getCode()
                : SupplierApplyStatusEnum.REJECTED.getCode());
        supplierApplyMapper.updateById(apply);
        if (!SupplierApplyStatusEnum.PROCESS.getCode().equals(apply.getApplyStatus())) {
            log.warn("[updateSupplierApplyAuditStatus] 准入申请({}) 不处于审批中状态", id);
            return;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reviewQuality(Long id, String opinion, Boolean passed) {
        SrmSupplierApplyDO apply = validateApplyExists(id);
        if (!SupplierApplyStatusEnum.PENDING_QUALITY.getCode().equals(apply.getApplyStatus())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_PENDING);
        }
        apply.setQualityReviewOpinion(opinion);
        apply.setQualityReviewTime(LocalDateTime.now());
        apply.setApplyStatus(Boolean.TRUE.equals(passed)
                ? SupplierApplyStatusEnum.PENDING_FINANCE.getCode()
                : SupplierApplyStatusEnum.REJECTED.getCode());
        supplierApplyMapper.updateById(apply);
        // 2. 根据审批结果更新状态
        Integer newStatus = convertBpmResultToStatus(bpmResult);
        if (newStatus != null) {
            supplierApplyMapper.updateById(new SrmSupplierApplyDO()
                    .setId(id).setApplyStatus(newStatus));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void reviewFinance(Long id, String opinion, Boolean passed) {
        SrmSupplierApplyDO apply = validateApplyExists(id);
        if (!SupplierApplyStatusEnum.PENDING_FINANCE.getCode().equals(apply.getApplyStatus())) {
            throw ServiceExceptionUtil.exception(ErrorCodeConstants.APPLY_STATUS_NOT_PENDING);
        // 3. 审批通过:自动创建/关联供应商档案
        if (SupplierApplyStatusEnum.APPROVED.getCode().equals(newStatus)) {
            approve(id);
        }
        apply.setFinanceReviewOpinion(opinion);
        apply.setFinanceReviewTime(LocalDateTime.now());
        apply.setApplyStatus(Boolean.TRUE.equals(passed)
                ? SupplierApplyStatusEnum.APPROVED.getCode()
                : SupplierApplyStatusEnum.REJECTED.getCode());
        supplierApplyMapper.updateById(apply);
    }
    @Override
@@ -158,7 +220,7 @@
        if (supplierApplyMapper.existsApprovedBySupplierId(supplierId)) {
            return;
        }
        // 自动创建准入申请并直接置为已通过
        // 自动创建准入申请并直接置为已通过(定标自动准入,不走协同办公)
        SrmSupplierApplyDO apply = new SrmSupplierApplyDO();
        apply.setApplyNo(generateApplyNo());
        apply.setSupplierId(supplierId);
@@ -221,6 +283,31 @@
    }
    /**
     * 校验供应商准入审批分类存在
     */
    private void validateSupplierApplyApproveCategoryAndProcessDefinition() {
        List<BpmCategoryDO> 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() {