Commit cf703186db47a88b5c9f48a9135264760fd641be

Authored by 房远帅
1 parent b4c60e3b

采购计划:中止

... ... @@ -159,4 +159,18 @@ public class ProcurementPlanController extends DefaultBaseController {
159 159
160 160 return InvokeResultBuilder.success();
161 161 }
  162 +
  163 + /**
  164 + * 中止
  165 + */
  166 + @ApiOperation("中止")
  167 + @ApiImplicitParam(value = "ID", name = "id", paramType = "query", required = true)
  168 + @HasPermission({"plan-manage:domestic-plan:stop"})
  169 + @PostMapping("/stop")
  170 + public InvokeResult<Void> stop(@NotBlank(message = "ID不能为空!") String id) {
  171 +
  172 + procurementPlanService.abort(id);
  173 +
  174 + return InvokeResultBuilder.success();
  175 + }
162 176 }
... ...
... ... @@ -5,7 +5,9 @@ import com.baomidou.mybatisplus.core.conditions.Wrapper;
5 5 import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
6 6 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
7 7 import com.github.pagehelper.PageInfo;
  8 +import com.lframework.starter.bpm.entity.FlowInstanceWrapper;
8 9 import com.lframework.starter.bpm.service.FlowInstanceWrapperService;
  10 +import com.lframework.starter.bpm.service.FlowTaskWrapperService;
9 11 import com.lframework.starter.common.exceptions.impl.DefaultClientException;
10 12 import com.lframework.starter.common.exceptions.impl.InputErrorException;
11 13 import com.lframework.starter.common.utils.Assert;
... ... @@ -63,6 +65,8 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
63 65 private WorkshopService workshopService;
64 66 @Resource
65 67 private FlowInstanceWrapperService flowInstanceWrapperService;
  68 + @Resource
  69 + private FlowTaskWrapperService flowTaskWrapperService;
66 70
67 71 @Override
68 72 public PageResult<ProcurementPlan> query(Integer pageIndex, Integer pageSize,
... ... @@ -425,6 +429,107 @@ public class ProcurementPlanServiceImpl extends BaseMpServiceImpl<ProcurementPla
425 429 }
426 430
427 431 /**
  432 + * 中止采购计划
  433 + * 规则:
  434 + * 1. 选择分厂后的任意环节可发起中止
  435 + * 2. 子计划发起中止 → 对应主计划下所有子计划全部中止
  436 + * 3. 主计划下只要有1个已完成(COMPLETED)子计划,不允许中止
  437 + * 4. 审核中(AUDIT)的计划需要终止工作流
  438 + *
  439 + * @param id 采购计划ID(可以是主计划ID或任意子计划ID)
  440 + */
  441 + @OpLog(type = OtherOpLogType.class, name = "中止采购计划,ID:{}", params = {"#id"})
  442 + @Transactional(rollbackFor = Exception.class)
  443 + @Override
  444 + public void abort(String id) {
  445 + if (StringUtil.isBlank(id)) {
  446 + throw new InputErrorException("ID不能为空!");
  447 + }
  448 +
  449 + //查询当前计划
  450 + ProcurementPlan currentPlan = getBaseMapper().findById(id);
  451 + if (currentPlan == null) {
  452 + throw new DefaultClientException("采购计划不存在!");
  453 + }
  454 +
  455 + //确定主计划ID,并收集所有需要中止的子计划列表
  456 + String mainPlanId;
  457 + List<ProcurementPlan> allSubPlans;
  458 + if (StringUtil.isBlank(currentPlan.getParentId())) {
  459 + //当前是主计划
  460 + mainPlanId = currentPlan.getId();
  461 + } else {
  462 + //当前是子计划,向上追溯到主计划
  463 + mainPlanId = currentPlan.getParentId();
  464 + }
  465 + //查询主计划下的所有子计划(非冻结的)
  466 + allSubPlans = getBaseMapper().selectList(
  467 + Wrappers.lambdaQuery(ProcurementPlan.class)
  468 + .eq(ProcurementPlan::getFrozen, false)
  469 + .eq(ProcurementPlan::getParentId, mainPlanId)
  470 + );
  471 + //如果没有子计划(单分厂,没有拆分过,当前就是一个计划),把当前计划加入列表
  472 + if (CollectionUtil.isEmpty(allSubPlans)) {
  473 + allSubPlans = new ArrayList<>();
  474 + allSubPlans.add(currentPlan);
  475 + }
  476 +
  477 +
  478 + //前置校验:存在任意"已完成"子计划则不允许中止
  479 + long completedCount = allSubPlans.stream()
  480 + .filter(p -> "COMPLETED".equals(p.getProcessStatus()))
  481 + .count();
  482 + if (completedCount > 0) {
  483 + throw new DefaultClientException("存在已完成的计划(子计划),不允许发起中止!");
  484 + }
  485 +
  486 + //收集所有需要中止的计划ID(用于批量查流程实例)
  487 + List<String> planIdsToAbort = allSubPlans.stream()
  488 + .map(ProcurementPlan::getId)
  489 + .collect(Collectors.toList());
  490 +
  491 + //批量查询这些计划关联的流程实例(用于审核中的计划终止工作流)
  492 + List<FlowInstanceWrapper> flowList = flowInstanceWrapperService.getFlowByBusinessIdList(planIdsToAbort);
  493 + // businessId -> FlowInstanceWrapper
  494 + Map<String, FlowInstanceWrapper> flowMap = CollectionUtil.isEmpty(flowList)
  495 + ? new HashMap<>()
  496 + : flowList.stream()
  497 + .filter(f -> StringUtil.isNotBlank(f.getBusinessId()))
  498 + .collect(Collectors.toMap(FlowInstanceWrapper::getBusinessId, f -> f, (v1, v2) -> v1));
  499 +
  500 + //逐个处理子计划:过滤掉已中止的,其余更新状态为ABORTED,审核中的同时终止工作流
  501 + for (ProcurementPlan subPlan : allSubPlans) {
  502 + String status = subPlan.getProcessStatus();
  503 + // 已中止:幂等跳过
  504 + if ("ABORTED".equals(status)) {
  505 + continue;
  506 + }
  507 + // 已完成:理论上前面已经校验,这里再兜底
  508 + if ("COMPLETED".equals(status)) {
  509 + throw new DefaultClientException(String.format("计划[%s]已完成,不允许中止!", subPlan.getCode()));
  510 + }
  511 +
  512 + // 审核中(AUDIT)需要先终止工作流
  513 + if ("AUDIT".equals(status)) {
  514 + FlowInstanceWrapper flow = flowMap.get(subPlan.getId());
  515 + if (flow != null && flow.getId() != null) {
  516 + flowTaskWrapperService.termination(flow.getId());
  517 + }
  518 + }
  519 +
  520 + // 更新计划状态为"已中止"
  521 + LambdaUpdateWrapper<ProcurementPlan> updateWrapper = Wrappers.lambdaUpdate(ProcurementPlan.class)
  522 + .set(ProcurementPlan::getProcessStatus, "ABORTED")
  523 + .set(ProcurementPlan::getProcessNode, null)
  524 + .eq(ProcurementPlan::getId, subPlan.getId());
  525 + getBaseMapper().update(updateWrapper);
  526 + }
  527 +
  528 + OpLogUtil.setVariable("id", id);
  529 + OpLogUtil.setExtra(currentPlan);
  530 + }
  531 +
  532 + /**
428 533 * 子计划创建:与 create() 逻辑一致,但不重新 buildPlanCode(因为子计划编号已按父code+后缀生成)
429 534 */
430 535 @Transactional(rollbackFor = Exception.class)
... ...
... ... @@ -89,4 +89,11 @@ public interface ProcurementPlanService extends BaseMpService<ProcurementPlan> {
89 89 void updateApprovalStatus(String id, String approvalStatus);
90 90
91 91 void updateExamine(String id);
  92 +
  93 + /**
  94 + * 中止采购计划
  95 + *
  96 + * @param id 采购计划ID(可以是主计划或子计划ID)
  97 + */
  98 + void abort(String id);
92 99 }
... ...