From c215f6c20445b973ff730dcdf4206426992da81d Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 19 十月 2026 11:30:57 +0800
Subject: [PATCH] feat(mes): 完善质检流程功能
---
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 251 insertions(+), 22 deletions(-)
diff --git a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
index 492d1d0..2a3ba96 100644
--- a/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
+++ b/yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/seedquality/MesQcSeedQualityServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityPageReqVO;
@@ -43,6 +44,7 @@
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
+import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_QUALIFIED_WAREHOUSE_NOT_CONFIGURED;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_WAREHOUSE_NOT_EXISTS;
import static cn.iocoder.yudao.module.mes.enums.ErrorCodeConstants.WM_WAREHOUSE_TYPE_NOT_QUALIFIED;
@@ -50,9 +52,14 @@
@Service @Validated @Slf4j
public class MesQcSeedQualityServiceImpl implements MesQcSeedQualityService {
private static final Set<String> REQUIRED = Set.of("purity", "cleanliness", "germination_rate", "moisture");
+ private static final int WAIT_RETEST = 10, WAIT_PROCESS = 20, WAIT_COMPANY = 30,
+ RELEASED = 40, WAIT_PROCESS_FAIL = 50, WAIT_COMPANY_FAIL = 60, SCRAPPED = 70;
@Resource private MesQcSeedQualityMapper qualityMapper;
+ @Resource private MesQcSeedQualityActionMapper actionMapper;
@Resource private MesQcSeedQualityIndicatorMapper indicatorMapper;
@Resource private MesProBagCodeMapper bagCodeMapper;
+ @Resource private cn.iocoder.yudao.module.mes.dal.mysql.pro.pallet.MesProPalletMapper palletMapper;
+ @Resource private cn.iocoder.yudao.module.mes.dal.mysql.pro.batch.MesProBatchMapper batchMapper;
@Resource private MesProBatchService proBatchService;
@Resource private MesWmBatchMapper mesWmBatchMapper;
@Resource private MesWmMaterialStockMapper materialStockMapper;
@@ -65,22 +72,120 @@
@Override
public PageResult<MesQcSeedQualityDO> getQualityPage(MesQcSeedQualityPageReqVO reqVO) {
+ if (StrUtil.isNotBlank(reqVO.getCode())) {
+ MesProBagCodeDO bag = bagCodeMapper.selectByScanCode(reqVO.getCode());
+ if (bag != null) reqVO.setCode(bag.getCode());
+ }
return qualityMapper.selectPage(reqVO);
+ }
+
+ @Override
+ public Map<String, Object> resolveScan(String code) {
+ MesProBagCodeDO bag = bagCodeMapper.selectByScanCode(code);
+ if (bag == null) throw new IllegalArgumentException("鏈В鏋愬埌鏈夋晥琚嬬爜锛岃鎵弿琚嬬爜");
+ Map<String, Object> result = new HashMap<>();
+ result.put("code", bag.getCode());
+ result.put("businessCode", bag.getBusinessCode());
+ result.put("batchCode", bag.getBatchCode());
+ result.put("inbound", bag.getInboundEventId() != null);
+ result.put("canceled", Integer.valueOf(30).equals(bag.getStatus()));
+ MesQcSeedQualityDO quality = qualityMapper.selectByCode(bag.getCode());
+ result.put("qualityId", quality == null ? null : quality.getId());
+ result.put("result", quality == null ? null : quality.getResult());
+ return result;
+ }
+
+ private record Scope(String code, String businessCode, String batchCode, boolean canceled, List<MesProBagCodeDO> bags) { }
+
+ private Scope scope(String code, String type, boolean lock) {
+ if ("BAG".equals(type)) {
+ MesProBagCodeDO bag = bagCodeMapper.selectByScanCode(code);
+ if (bag == null) throw new IllegalArgumentException("鏈壘鍒拌鐮侊紝璇锋牳瀵规壂鐮佺被鍨�");
+ return new Scope(bag.getCode(), bag.getBusinessCode(), bag.getBatchCode(), Integer.valueOf(30).equals(bag.getStatus()), List.of(bag));
+ }
+ if ("PALLET".equals(type)) {
+ var pallet = lock ? palletMapper.selectByScanCodeForUpdate(code) : palletMapper.selectByScanCode(code);
+ if (pallet == null) throw new IllegalArgumentException("鏈壘鍒版墭鐩樼爜锛岃鏍稿鎵爜绫诲瀷");
+ if (pallet.getBatchId() == null) throw new IllegalArgumentException("鎵樼洏灏氭湭缁戝畾鐢熶骇鎵规锛屾棤娉曡川妫�");
+ return new Scope(StrUtil.blankToDefault(pallet.getCode(), pallet.getTempCode()), pallet.getBusinessCode(), pallet.getBatchCode(),
+ Integer.valueOf(20).equals(pallet.getStatus()), lock ? bagCodeMapper.selectListByPalletIdForUpdate(pallet.getId()) : bagCodeMapper.selectListByPalletId(pallet.getId()));
+ }
+ if ("BATCH".equals(type)) {
+ var batch = lock ? batchMapper.selectByScanCodeForUpdate(code) : batchMapper.selectByScanCode(code);
+ if (batch == null) throw new IllegalArgumentException("鏈壘鍒扮敓浜ф壒娆$爜锛岃鏍稿鎵爜绫诲瀷");
+ return new Scope(StrUtil.blankToDefault(batch.getCode(), batch.getTempCode()), batch.getBusinessCode(), batch.getCode(),
+ Integer.valueOf(40).equals(batch.getStatus()), lock ? bagCodeMapper.selectListByBatchIdForUpdate(batch.getId()) : bagCodeMapper.selectListByBatchId(batch.getId()));
+ }
+ throw new IllegalArgumentException("涓嶆敮鎸佺殑璐ㄦ鎵爜绫诲瀷");
+ }
+
+ @Override
+ public Map<String, Object> resolveScan(String code, String scanType) {
+ Scope scope = scope(code, scanType, false);
+ List<MesProBagCodeDO> effective = scope.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).toList();
+ Set<String> inspected = qualityMapper.selectListByCodes(effective.stream().map(MesProBagCodeDO::getCode).toList()).stream()
+ .map(MesQcSeedQualityDO::getCode).collect(java.util.stream.Collectors.toSet());
+ Map<String, Object> result = new HashMap<>();
+ result.put("scanType", scanType); result.put("code", scope.code()); result.put("businessCode", scope.businessCode());
+ result.put("batchCode", scope.batchCode()); result.put("canceled", scope.canceled());
+ result.put("totalCount", effective.size());
+ result.put("inbound", !effective.isEmpty() && effective.stream().allMatch(b -> b.getInboundEventId() != null));
+ result.put("pendingBagIds", effective.stream().filter(b -> b.getInboundEventId() != null && !inspected.contains(b.getCode())).map(MesProBagCodeDO::getId).sorted().toList());
+ result.put("completedBagIds", effective.stream().filter(b -> inspected.contains(b.getCode())).map(MesProBagCodeDO::getId).sorted().toList());
+ if ("BAG".equals(scanType)) {
+ MesQcSeedQualityDO quality = qualityMapper.selectByCode(scope.code());
+ result.put("qualityId", quality == null ? null : quality.getId()); result.put("result", quality == null ? null : quality.getResult());
+ }
+ return result;
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public Map<String, Object> submitGroup(cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityGroupSubmitReqVO req) {
+ Scope scope = scope(req.getCode(), req.getScanType(), true);
+ if (scope.canceled()) throw new IllegalArgumentException("璐ㄦ瀵硅薄宸蹭綔搴�");
+ List<MesProBagCodeDO> effective = scope.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).toList();
+ if (effective.isEmpty()) throw new IllegalArgumentException("娌℃湁鏈夋晥琚嬬爜");
+ List<MesProBagCodeDO> locked = bagCodeMapper.selectListByIdsForUpdate(effective.stream().map(MesProBagCodeDO::getId).toList());
+ if (locked.size() != effective.size() || locked.stream().anyMatch(b -> Integer.valueOf(30).equals(b.getStatus()) || b.getInboundEventId() == null))
+ throw new IllegalArgumentException("琚嬬爜宸插彉鍖栨垨灏氭湭鍏ㄩ儴鍏ュ簱锛岃閲嶆柊鎵弿鏍稿");
+ // 鍦ㄨ鐮侀攣涔嬪悗閲嶆柊鏍稿褰掑睘锛涘叾浠栫粦瀹氭搷浣滀笉寰楁敼鍙樺凡纭鐨勬楠岃寖鍥淬��
+ Scope current = scope(req.getCode(), req.getScanType(), false);
+ Set<Long> currentIds = current.bags().stream().filter(b -> !Integer.valueOf(30).equals(b.getStatus())).map(MesProBagCodeDO::getId).collect(java.util.stream.Collectors.toSet());
+ if (!currentIds.equals(new HashSet<>(locked.stream().map(MesProBagCodeDO::getId).toList())))
+ throw new IllegalArgumentException("琚嬬爜褰掑睘宸插彉鍖栵紝璇烽噸鏂版壂鎻忔牳瀵�");
+ Set<String> inspected = qualityMapper.selectListByCodesForUpdate(locked.stream().map(MesProBagCodeDO::getCode).toList()).stream()
+ .map(MesQcSeedQualityDO::getCode).collect(java.util.stream.Collectors.toSet());
+ List<MesProBagCodeDO> pending = locked.stream().filter(b -> !inspected.contains(b.getCode())).sorted(Comparator.comparing(MesProBagCodeDO::getId)).toList();
+ List<Long> confirmed = req.getBagIds().stream().sorted().toList();
+ if (pending.isEmpty() || !confirmed.equals(pending.stream().map(MesProBagCodeDO::getId).toList()))
+ throw new IllegalArgumentException("寰呰川妫�娓呭崟宸插彉鍖栨垨宸插叏閮ㄨ川妫�锛岃閲嶆柊鎵弿锛岀姝㈤噸澶嶅鐞�");
+ for (MesProBagCodeDO bag : pending) {
+ MesQcSeedQualitySubmitReqVO single = new MesQcSeedQualitySubmitReqVO();
+ single.setCode(bag.getCode());single.setIndicators(req.getIndicators());single.setReason(req.getReason());
+ single.setQualifiedWarehouseId(req.getQualifiedWarehouseId());
+ // 澶栧眰浜嬪姟瑕嗙洊鍏ㄩ儴琚嬬爜锛涗换浣曚竴琚嬪簱瀛樻祦杞け璐ラ兘浼氬洖婊氭暣娆℃搷浣溿��
+ submit(single);
+ }
+ return Map.of("processedCount", pending.size());
}
@Override @Transactional(rollbackFor = Exception.class)
public Long submit(MesQcSeedQualitySubmitReqVO reqVO) {
if (reqVO.getIndicators().size() != 4) throw new IllegalArgumentException("蹇呴』鎻愪氦鍥涢」鎸囨爣");
- if (qualityMapper.selectByCode(reqVO.getCode()) != null) throw new IllegalArgumentException("璇ヨ鐮佸凡璐ㄦ锛岀姝㈤噸澶嶆彁浜�");
// 绉嶅瓙璐ㄦ蹇呴』浠ヨ鐮侊紙鍗曡锛夋彁浜わ紝鐗╂枡淇℃伅鐢辫鐮佽嚜鍔ㄨВ鏋愶紝鍓嶇涓嶅啀鍗曠嫭閫夋嫨鐗╂枡
- MesProBagCodeDO bag = bagCodeMapper.selectByCode(reqVO.getCode());
+ MesProBagCodeDO bag = bagCodeMapper.selectByScanCodeForUpdate(reqVO.getCode());
if (bag == null) {
throw new IllegalArgumentException("鏈В鏋愬埌鏈夋晥琚嬬爜锛岀瀛愯川妫�蹇呴』浠ヨ鐮佹彁浜�");
}
- MesProBatchDO batch = resolveBatch(reqVO.getCode(), reqVO.getBatchCode());
+ if (Integer.valueOf(30).equals(bag.getStatus()) || bag.getInboundEventId() == null)
+ throw new IllegalArgumentException("琚嬬爜宸蹭綔搴熸垨灏氭湭鍏ュ簱锛屾棤娉曡川妫�");
+ // 鍙岀爜閿佸畾鍚屼竴琚嬶紝骞朵互杩芥函鐮佽褰曠粨鏋滐紝闃叉鎹㈢爜鎴栧苟鍙戦噸澶嶆墸鍑忓簱瀛樸��
+ if (qualityMapper.selectByCodeForUpdate(bag.getCode()) != null) throw new IllegalArgumentException("璇ヨ鐮佸凡璐ㄦ锛岀姝㈤噸澶嶆彁浜�");
+ MesProBatchDO batch = bag.getBatchId() == null ? null : proBatchService.getBatch(bag.getBatchId());
if (batch == null || batch.getItemId() == null) {
throw new IllegalArgumentException("鏈牴鎹鐮佽В鏋愬埌鐗╂枡淇℃伅锛岃纭鎵爜鍐呭鎴栨壒娆″彿姝g‘");
}
+ if (Integer.valueOf(40).equals(batch.getStatus())) throw new IllegalArgumentException("鐢熶骇鎵规宸蹭綔搴燂紝鏃犳硶璐ㄦ");
// 琚嬬爜瀵瑰簲鎵规鐨勫簱瀛樺繀椤诲浜庢殏瀛樺簱锛堝緟璐ㄦ锛変腑锛屾墠鑳借繘琛岀瀛愯川妫�
MesWmBatchDO wmBatch = mesWmBatchMapper.selectByCode(batch.getCode());
if (wmBatch == null || materialStockMapper.selectTempStockCountByBatchId(wmBatch.getId()) == 0) {
@@ -97,32 +202,52 @@
if (!MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(qualifiedWarehouse.getType())) {
throw exception(WM_WAREHOUSE_TYPE_NOT_QUALIFIED);
}
- // 瑙f瀽鍚堟牸搴撻粯璁ゅ簱鍖�/搴撲綅锛堝彇璇ヤ粨搴撲笅绗竴涓簱鍖恒�佸叾涓嬬涓�涓簱浣嶏紝鏈厤缃垯涓� null锛�
- Long qualifiedLocationId = null;
- Long qualifiedAreaId = null;
- List<MesWmWarehouseLocationDO> locations = warehouseLocationService.getWarehouseLocationList(qualifiedWarehouse.getId());
- if (CollUtil.isNotEmpty(locations)) {
- qualifiedLocationId = locations.get(0).getId();
- List<MesWmWarehouseAreaDO> areas = warehouseAreaService.getWarehouseAreaList(qualifiedLocationId);
- if (CollUtil.isNotEmpty(areas)) {
- qualifiedAreaId = areas.get(0).getId();
- }
- }
Set<String> codes = new HashSet<>();
boolean passed = true;
- MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(reqVO.getCode()).itemId(batch.getItemId())
- .batchCode(batch.getCode()).reason(reqVO.getReason()).result(0)
+ MesQcSeedQualityDO quality = MesQcSeedQualityDO.builder().code(bag.getCode()).itemId(batch.getItemId())
+ .batchCode(batch.getCode()).batchId(batch.getId()).reason(reqVO.getReason()).result(0)
+ .qualifiedWarehouseId(qualifiedWarehouse.getId()).inspectorId(getLoginUserId())
.inspectedAt(LocalDateTime.now()).build();
qualityMapper.insert(quality);
for (MesQcSeedQualitySubmitReqVO.Indicator item : reqVO.getIndicators()) {
if (!REQUIRED.contains(item.getCode()) || !codes.add(item.getCode())) throw new IllegalArgumentException("鎸囨爣蹇呴』涓虹函搴︺�佸噣搴︺�佸彂鑺界巼銆佹按鍒嗕笖涓嶅彲閲嶅");
- if (item.getMinValue().compareTo(item.getMaxValue()) > 0 || item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false;
+ if (item.getMinValue().compareTo(item.getMaxValue()) > 0) throw new IllegalArgumentException("鎸囨爣涓嬮檺涓嶈兘澶т簬涓婇檺");
+ if (item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false;
indicatorMapper.insert(MesQcSeedQualityIndicatorDO.builder().qualityId(quality.getId()).indicatorCode(item.getCode())
.value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build());
}
if (!codes.equals(REQUIRED)) throw new IllegalArgumentException("鍥涢」鎸囨爣涓嶅畬鏁�");
+ if (!passed && StrUtil.isBlank(reqVO.getReason())) throw new IllegalArgumentException("璐ㄦ涓嶅悎鏍煎繀椤诲~鍐欏師鍥�");
quality.setResult(passed ? 1 : 2);
+ quality.setWorkflowStatus(passed ? WAIT_PROCESS : WAIT_RETEST);
qualityMapper.updateById(quality);
+ recordAction(quality.getId(), "INITIAL", quality.getResult(), reqVO.getReason(), reqVO.getIndicators());
+ // 鍒濇浠呰褰曠粨璁猴紱琚嬩粛鐣欏湪鏆傚瓨搴擄紝寰呭妫�鍙婁袱绾у鎵瑰悗鍐嶆斁琛屾垨鎶ュ簾銆�
+ return quality.getId();
+ }
+
+ private void finalizeStock(MesQcSeedQualityDO quality, boolean passed) {
+ MesProBagCodeDO bag = bagCodeMapper.selectByScanCodeForUpdate(quality.getCode());
+ if (bag == null || bag.getInboundEventId() == null || !Objects.equals(bag.getQcStatus(), MesProBagCodeQcStatusEnum.UNCHECKED.getStatus()))
+ throw new IllegalArgumentException("琚嬬爜鐘舵�佸凡鍙樺寲锛岀姝㈤噸澶嶅簱瀛樻祦杞�");
+ MesProBatchDO batch = proBatchService.getBatch(bag.getBatchId());
+ if (batch == null) throw new IllegalArgumentException("琚嬬爜鎵�灞炵敓浜ф壒娆′笉瀛樺湪");
+ MesWmBatchDO wmBatch = mesWmBatchMapper.selectByCode(batch.getCode());
+ if (wmBatch == null) throw new IllegalArgumentException("鏆傚瓨搴撳瓨鎵�灞炰粨搴撴壒娆′笉瀛樺湪");
+ MesWmWarehouseDO qualifiedWarehouse = warehouseService.getWarehouse(quality.getQualifiedWarehouseId());
+ if (passed && (qualifiedWarehouse == null || Boolean.TRUE.equals(qualifiedWarehouse.getFrozen())
+ || !MesWmWarehouseTypeEnum.QUALIFIED.getType().equals(qualifiedWarehouse.getType())))
+ throw new IllegalArgumentException("鐩爣鍚堟牸搴撲笉瀛樺湪鎴栫被鍨嬪凡鍙樺寲");
+ Long qualifiedLocationId = null;
+ Long qualifiedAreaId = null;
+ if (passed) {
+ List<MesWmWarehouseLocationDO> locations = warehouseLocationService.getWarehouseLocationList(qualifiedWarehouse.getId());
+ if (CollUtil.isNotEmpty(locations)) {
+ qualifiedLocationId = locations.get(0).getId();
+ List<MesWmWarehouseAreaDO> areas = warehouseAreaService.getWarehouseAreaList(qualifiedLocationId);
+ if (CollUtil.isNotEmpty(areas)) qualifiedAreaId = areas.get(0).getId();
+ }
+ }
// 鎸夎鐮佺矑搴︽祦杞簱瀛橈細閿佸畾璇ヨ鍦ㄦ殏瀛樺簱涓殑娲诲姩鏄庣粏锛岀姝㈡寜鎵规浠绘剰鎵e噺
MesWmMaterialStockBagDO stockBag = materialStockBagMapper.selectActiveByBagCode(bag.getCode());
MesWmMaterialStockDO tempStock = resolveTempStock(bag, wmBatch, stockBag);
@@ -131,7 +256,7 @@
.setType(MesWmTransactionTypeEnum.OUT.getType())
.setBizType(MesBizTypeConstants.QC_SEED_QUALITY)
.setBizId(quality.getId())
- .setBizCode(reqVO.getCode())
+ .setBizCode(quality.getCode())
.setBizLineId(quality.getId())
.setItemId(batch.getItemId())
.setQuantity(BigDecimal.ONE.negate())
@@ -154,7 +279,7 @@
.setType(MesWmTransactionTypeEnum.IN.getType())
.setBizType(MesBizTypeConstants.QC_SEED_QUALITY)
.setBizId(quality.getId())
- .setBizCode(reqVO.getCode())
+ .setBizCode(quality.getCode())
.setBizLineId(quality.getId())
.setItemId(batch.getItemId())
.setQuantity(BigDecimal.ONE)
@@ -174,14 +299,113 @@
// B2. 鍚屾杩佺Щ琚嬬爜鏄庣粏鍒板悎鏍煎簱瀛樿惤鐐�
materialStockBagService.moveIn(qualInReq, qualifiedStock, qualInTxId);
bagCodeMapper.updateQcStatus(bag.getId(), MesProBagCodeQcStatusEnum.QUALIFIED.getStatus());
- log.info("[submit][璐ㄦ鍚堟牸锛岃鐮� {} 鐢辨殏瀛樺簱杞悎鏍煎簱 {}]", bag.getCode(), qualifiedWarehouse.getName());
+ log.info("[finalizeStock][瀹℃壒鏀捐锛岃鐮� {} 鐢辨殏瀛樺簱杞悎鏍煎簱 {}]", bag.getCode(), qualifiedWarehouse.getName());
} else {
// C. 璐ㄦ涓嶅悎鏍� 鈫� 璇ヨ鎶ュ簾锛涙殏瀛樺嚭搴撲簨鍔″凡鎵e噺鏆傚瓨搴撳瓨锛屾爣璁拌鐮佹姤搴�
materialStockBagService.scrap(tempOutReq, tempOutTxId);
bagCodeMapper.updateQcStatus(bag.getId(), MesProBagCodeQcStatusEnum.UNQUALIFIED.getStatus());
- log.info("[submit][璐ㄦ涓嶅悎鏍硷紝琚嬬爜 {} 鎶ュ簾]", bag.getCode());
+ log.info("[finalizeStock][瀹℃壒鎶ュ簾锛岃鐮� {}]", bag.getCode());
}
- return quality.getId();
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public void retest(Long id, cn.iocoder.yudao.module.mes.controller.admin.qc.seedquality.vo.MesQcSeedQualityRetestReqVO req) {
+ MesQcSeedQualityDO quality = requireStatus(id, WAIT_RETEST);
+ if (!Objects.equals(quality.getCode(), req.getCode())) throw new IllegalArgumentException("澶嶆琚嬬爜涓庡師璐ㄦ璁板綍涓嶄竴鑷�");
+ boolean passed = validateIndicators(req.getIndicators());
+ if (!passed && StrUtil.isBlank(req.getReason())) throw new IllegalArgumentException("澶嶆涓嶅悎鏍煎繀椤诲~鍐欏師鍥�");
+ recordAction(id, "RETEST", passed ? 1 : 2, req.getReason(), req.getIndicators());
+ qualityMapper.updateById(MesQcSeedQualityDO.builder().id(id).result(passed ? 1 : 2)
+ .reason(passed ? "" : req.getReason().trim()).retestResult(passed ? 1 : 2).retestedAt(LocalDateTime.now())
+ .inspectorId(getLoginUserId()).inspectedAt(LocalDateTime.now())
+ .workflowStatus(passed ? WAIT_PROCESS : WAIT_PROCESS_FAIL).build());
+ // 椤甸潰銆佸叕寮�杩芥函璇诲彇涓绘寚鏍囪〃锛涘悓姝ヤ负鏈�鏂颁竴娆℃祴鍊硷紝鍘嗘娴嬪�煎湪鎿嶄綔鐣欑棔琛ㄤ繚鐣欍��
+ Map<String, MesQcSeedQualityIndicatorDO> existing = new HashMap<>();
+ for (MesQcSeedQualityIndicatorDO indicator : indicatorMapper.selectListByQualityId(id))
+ existing.put(indicator.getIndicatorCode(), indicator);
+ for (MesQcSeedQualitySubmitReqVO.Indicator item : req.getIndicators()) {
+ MesQcSeedQualityIndicatorDO indicator = existing.get(item.getCode());
+ if (indicator == null) throw new IllegalArgumentException("璐ㄦ鎸囨爣璁板綍缂哄け");
+ indicatorMapper.updateById(MesQcSeedQualityIndicatorDO.builder().id(indicator.getId())
+ .value(item.getValue()).minValue(item.getMinValue()).maxValue(item.getMaxValue()).build());
+ }
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public void processApprove(Long id, boolean approved, String opinion) {
+ MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id);
+ if (quality == null || !Set.of(WAIT_PROCESS, WAIT_PROCESS_FAIL).contains(quality.getWorkflowStatus()))
+ throw new IllegalArgumentException("褰撳墠鐘舵�佷笉鑳借繘琛屽姞宸ラ儴瀹℃壒");
+ if (StrUtil.isBlank(opinion)) throw new IllegalArgumentException("璇峰~鍐欏鎵规剰瑙�");
+ int next = approved ? (quality.getWorkflowStatus() == WAIT_PROCESS ? WAIT_COMPANY : WAIT_COMPANY_FAIL) : WAIT_RETEST;
+ qualityMapper.updateById(MesQcSeedQualityDO.builder().id(id).workflowStatus(next)
+ .processApprovedAt(LocalDateTime.now()).build());
+ recordAction(id, approved ? "PROCESS_APPROVE" : "PROCESS_REJECT", quality.getResult(), opinion, null);
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public void companyApprove(Long id, boolean approved, String opinion) {
+ MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id);
+ if (quality == null || !Set.of(WAIT_COMPANY, WAIT_COMPANY_FAIL).contains(quality.getWorkflowStatus()))
+ throw new IllegalArgumentException("褰撳墠鐘舵�佷笉鑳借繘琛屽叕鍙歌川妫�瀹℃壒");
+ if (StrUtil.isBlank(opinion)) throw new IllegalArgumentException("璇峰~鍐欏鎵规剰瑙�");
+ if (approved) finalizeStock(quality, quality.getWorkflowStatus() == WAIT_COMPANY);
+ int next = approved ? (quality.getWorkflowStatus() == WAIT_COMPANY ? RELEASED : SCRAPPED) : WAIT_RETEST;
+ qualityMapper.updateById(MesQcSeedQualityDO.builder().id(id).workflowStatus(next)
+ .companyApprovedAt(LocalDateTime.now()).build());
+ recordAction(id, approved ? "COMPANY_APPROVE" : "COMPANY_REJECT", quality.getResult(), opinion, null);
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public void processApproveBatch(List<Long> ids, boolean approved, String opinion) {
+ for (Long id : approvalIds(ids)) processApprove(id, approved, opinion);
+ }
+
+ @Override @Transactional(rollbackFor = Exception.class)
+ public void companyApproveBatch(List<Long> ids, boolean approved, String opinion) {
+ for (Long id : approvalIds(ids)) companyApprove(id, approved, opinion);
+ }
+
+ private List<Long> approvalIds(List<Long> ids) {
+ if (ids == null || ids.isEmpty() || ids.size() > 500 || ids.stream().anyMatch(Objects::isNull))
+ throw new IllegalArgumentException("璇烽�夋嫨 1 鑷� 500 鏉¤川妫�璁板綍");
+ List<Long> sorted = ids.stream().distinct().sorted().toList();
+ if (sorted.size() != ids.size()) throw new IllegalArgumentException("璐ㄦ璁板綍涓嶈兘閲嶅閫夋嫨");
+ return sorted;
+ }
+
+ private MesQcSeedQualityDO requireStatus(Long id, int expected) {
+ MesQcSeedQualityDO quality = qualityMapper.selectByIdForUpdate(id);
+ if (quality == null || !Objects.equals(quality.getWorkflowStatus(), expected))
+ throw new IllegalArgumentException("璐ㄦ璁板綍鐘舵�佸凡鍙樺寲锛岃鍒锋柊鍚庨噸璇�");
+ return quality;
+ }
+
+ private boolean validateIndicators(List<MesQcSeedQualitySubmitReqVO.Indicator> indicators) {
+ if (indicators == null || indicators.size() != REQUIRED.size()) throw new IllegalArgumentException("蹇呴』鎻愪氦鍥涢」鎸囨爣");
+ Set<String> codes = new HashSet<>();
+ boolean passed = true;
+ for (MesQcSeedQualitySubmitReqVO.Indicator item : indicators) {
+ if (item == null || !REQUIRED.contains(item.getCode()) || !codes.add(item.getCode())
+ || item.getValue() == null || item.getMinValue() == null || item.getMaxValue() == null)
+ throw new IllegalArgumentException("鎸囨爣蹇呴』涓虹函搴︺�佸噣搴︺�佸彂鑺界巼銆佹按鍒嗕笖涓嶅彲閲嶅");
+ if (item.getMinValue().compareTo(item.getMaxValue()) > 0) throw new IllegalArgumentException("鎸囨爣涓嬮檺涓嶈兘澶т簬涓婇檺");
+ if (item.getValue().compareTo(item.getMinValue()) < 0 || item.getValue().compareTo(item.getMaxValue()) > 0) passed = false;
+ }
+ return passed;
+ }
+
+ private void recordAction(Long id, String action, Integer result, String opinion,
+ List<MesQcSeedQualitySubmitReqVO.Indicator> indicators) {
+ MesQcSeedQualityActionDO event = new MesQcSeedQualityActionDO();
+ event.setQualityId(id);
+ event.setAction(action);
+ event.setResult(result);
+ event.setOpinion(opinion);
+ event.setIndicatorsJson(indicators == null ? null : JSONUtil.toJsonStr(indicators));
+ event.setOperatorId(getLoginUserId());
+ event.setCreateTime(LocalDateTime.now());
+ actionMapper.insert(event);
}
/**
@@ -291,4 +515,9 @@
return List.of();
}
}
+
+ @Override
+ public List<MesQcSeedQualityActionDO> getActions(Long qualityId) {
+ return actionMapper.selectByQualityId(qualityId);
+ }
}
--
Gitblit v1.9.3