From 34981c30a78e8bbd7791131059a9210f9928b62c Mon Sep 17 00:00:00 2001
From: 刘光辉 <347230014@qq.com>
Date: 星期四, 17 九月 2026 09:24:09 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master' into master
---
jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsJianceTaskController.java | 454 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 454 insertions(+), 0 deletions(-)
diff --git a/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsJianceTaskController.java b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsJianceTaskController.java
new file mode 100644
index 0000000..b10f76a
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsJianceTaskController.java
@@ -0,0 +1,454 @@
+package jnpf.limsController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jnpf.exception.DataException;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import jnpf.limsEntity.JianyanJielun;
+import jnpf.limsEntity.LimsJianceTaskAcceptParam;
+import jnpf.limsEntity.LimsJianyanAssignItem;
+import jnpf.limsEntity.LimsJianyanAssignParam;
+import jnpf.limsEntity.LimsJianyanAuditFailParam;
+import jnpf.limsEntity.LimsJianyanAuditItem;
+import jnpf.limsEntity.LimsJianyanAuditOutcome;
+import jnpf.limsEntity.LimsJianyanAuditParam;
+import jnpf.limsEntity.LimsJianyanResultItem;
+import jnpf.limsEntity.LimsJianyanResultParam;
+import jnpf.limsEntity.LimsJianyanXiangEntity;
+import jnpf.limsEntity.LimsSignEntity;
+import jnpf.limsEntity.LimsSignUsageTarget;
+import jnpf.limsEntity.LimsSubmitResultOutcome;
+import jnpf.limsEntity.SignOpType;
+import jnpf.limsService.LimsJianceTaskRemindService;
+import jnpf.limsService.LimsJianceTaskService;
+import jnpf.limsService.LimsJianyanXiangService;
+import jnpf.limsService.LimsSignService;
+import jnpf.util.R;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+@Slf4j
+@Tag(name = "lims妫�娴嬩换鍔℃帴鍙�", description = "鎺ユ牱鍚庣殑鐝粍浠诲姟澶勭悊锛氭帴鏀� / 鍒嗘牱 / 鎸囨淳妫�楠屽憳 绛�")
+@RestController
+@RequestMapping("/lims/biz/jiance-task")
+public class LimsJianceTaskController {
+
+ @Autowired
+ private LimsJianceTaskService limsJianceTaskService;
+
+ @Autowired
+ private LimsSignService limsSignService;
+
+ @Autowired
+ private LimsJianyanXiangService limsJianyanXiangService;
+
+ @Autowired
+ private LimsJianceTaskRemindService limsJianceTaskRemindService;
+
+ /**
+ * 鏍峰搧鍒嗘锛氭妸璇烽獙鍗� submit 鏃跺垱寤虹殑妫�楠岄」锛坈reated + wei_fenjian锛夋帹杩涘埌 pending_assign锛�
+ * 鏍囪 fenjian_zhuangtai=fenjian锛屽苟鍥炲啓鎿嶄綔鍛樺綍鍏ョ殑 4 涓垎妫�瀛楁銆�
+ * 璇锋眰浣擄細{
+ * "biz_sign": "<lims_sign 涓婚敭>",
+ * "zuhe_fenjian_liang": "...", // 缁勫悎鍒嗘閲忥紝璇锋眰绾у叡浜紙閫夊~锛屼紶浜嗘墠鍐欙級
+ * "zuhe_fenjian_danwei": "...", // 缁勫悎鍒嗘鍗曚綅锛岃姹傜骇鍏变韩锛堥�夊~锛屼紶浜嗘墠鍐欙級
+ * "data": [{ "id": "<妫�楠岄」 f_id>", "fenjian_liang": "...", "fenjian_danwei": "...",
+ * "fenjian_fenzhuang_rongqi": "...", "fenjian_baocun_tiaojian": "..." }, ...]
+ * }
+ * biz_sign锛歭ims_sign 涓婚敭锛宱p_type=jiance_task_accept锛涚鍚嶄粎浣滃璁¤褰曞湪 lims_sign锛屼笟鍔¤〃涓嶈惤 sign 鍒椼��
+ * 缁勫悎涓夊垪锛坺uhe_id + zuhe_fenjian_liang + zuhe_fenjian_danwei锛変粎鍐欏叆 data 涓� shifou_zuhe_fenjian=yes 鐨勮锛�
+ * zuhe_id 鐢卞悗绔敓鎴愩�佹暣娆¤姹傚叡浜竴涓紝缁勫悎閲� / 鍗曚綅鍙栦笂杩拌姹傜骇鍏变韩鍊笺��
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "鏍峰搧鍒嗘")
+ @PostMapping("/accept")
+ public R<String> fenjian(@RequestBody LimsJianceTaskAcceptParam param) {
+ if (param == null || param.getData() == null || param.getData().isEmpty()) {
+ return R.error("璇烽�夋嫨鑷冲皯涓�鏉℃楠岄」");
+ }
+ if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ param.getBizSign(), SignOpType.JIANCE_TASK_ACCEPT, null);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ List<String> fenjianIds = param.getData().stream().map(d -> d.getId()).collect(Collectors.toList());
+
+ List<LimsSignUsageTarget> affectedTargets;
+ try {
+ affectedTargets = limsJianceTaskService.fenjianSamples(param);
+ } catch (DataException e) {
+ // 鍒嗘瀛楁锛堝垎妫�閲� / 鍗曚綅 / 瀹瑰櫒 / 淇濆瓨鏉′欢锛夎绾у繀濉牎楠屽け璐ヨ蛋鍒拌繖閲�
+ return R.error(e.getMessage());
+ }
+ int affected = affectedTargets.size();
+
+ if (affected <= 0) {
+ return R.error("鎵�閫夋楠岄」涓嶅湪寰呭垎妫�鐘舵��");
+ }
+ limsSignService.recordSignUsage(
+ sign, SignOpType.JIANCE_TASK_ACCEPT, fenjianIds, affectedTargets);
+ return R.success("宸插垎妫� " + affected + " 鏉�");
+ }
+
+ /**
+ * 妫�楠屼换鍔�"閫愯"鍒嗛厤锛堟敮鎸佹瘡琛屼笉鍚岀殑妫�楠屽憳 / 瑕佹眰瀹屾垚鏃ユ湡锛屾敮鎸佹敼娲撅級銆�
+ * 璇锋眰浣擄細{
+ * "biz_sign": "绛惧悕鏁版嵁",
+ * "data": [
+ * // 鏅�氭楠岄」锛氬垎閰嶇粰妫�楠屽憳
+ * {"id": "id1", "jiancha_ren": "user1", "wancheng_riqi": "2026-04-30"},
+ * // 澶栨椤癸紙shifou_waijian=yes锛夛細鍒嗛厤缁欏妫�鍗曚綅
+ * {"id": "id2", "waijian_danwei": "XX妫�娴嬫墍", "waijian_lururen": "寮犱笁"}
+ * ]
+ * }
+ * biz_sign 涓烘湰娆″垎閰嶇殑鍏变韩绛惧悕锛屼粎瀹¤璁板綍鍦� lims_sign锛堜笟鍔¤〃涓嶈惤绛惧悕鍒楋級銆�
+ * 鍏佽 pending_assign 鎴� pending_test 鐨勮琚洿鏂帮紝鎴愬姛鍚庣疆涓� pending_test銆�
+ *
+ * <p>鏍¢獙涓嬫矇鍒� service锛氳鐨� shifou_waijian 鐢� service 棰勬煡鍚庡喅瀹氬垎鏀繀濉瓧娈碉紝
+ * 涓嶅湪 controller 灞傛牴鎹叆鍙傜洿鎺ュ垽鏂紙閬垮厤鎶�"鏄惁澶栨"鐢卞墠绔喅瀹氾級銆�
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "妫�楠屼换鍔¢�愯鍒嗛厤")
+ @PostMapping("/assign")
+ public R<String> assign(@RequestBody LimsJianyanAssignParam param) {
+ if (param == null || param.getData() == null || param.getData().isEmpty()) {
+ return R.error("璇烽�夋嫨鑷冲皯涓�鏉℃楠屼换鍔�");
+ }
+ if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ for (int i = 0; i < param.getData().size(); i++) {
+ LimsJianyanAssignItem item = param.getData().get(i);
+ if (item == null || item.getId() == null || item.getId().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戜换鍔� id");
+ }
+ }
+
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ param.getBizSign(), SignOpType.JIANCE_TASK_ASSIGN, null);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ List<String> assignIds = param.getData().stream().map(d -> d.getId()).collect(Collectors.toList());
+
+ List<LimsSignUsageTarget> affectedTargets;
+ try {
+ affectedTargets = limsJianceTaskService.assignJianyanXiang(param);
+ } catch (DataException e) {
+ // 鏅�氭楠岄」缂� jiancha_ren / wancheng_riqi銆佹垨澶栨椤圭己 waijian_danwei /
+ // waijian_lururen 绛夎绾ф牎楠屽け璐ヨ蛋鍒拌繖閲�
+ return R.error(e.getMessage());
+ }
+ int affected = affectedTargets.size();
+ int total = param.getData().size();
+
+ if (affected <= 0) {
+ return R.error("鎵�閫変换鍔″凡杩涘叆瀹℃牳 / 宸插鏍搁樁娈碉紝鏃犳硶鍒嗛厤");
+ }
+ limsSignService.recordSignUsage(
+ sign, SignOpType.JIANCE_TASK_ASSIGN, assignIds, affectedTargets);
+ if (affected < total) {
+ return R.success("宸插垎閰� " + affected + " 鏉★紝" + (total - affected) + " 鏉″洜鐘舵�佷笉绗﹁璺宠繃");
+ }
+ return R.success("宸插垎閰� " + affected + " 鏉�");
+ }
+
+ /**
+ * 妫�楠屼换鍔℃壒閲忓綍鍏ョ粨鏋溿��
+ * 璇锋眰浣擄細{
+ * "biz_sign": "绛惧悕鏁版嵁",
+ * "data": [
+ * {"id": "id1", "jianyan_jieguo": "7.2", "jianyan_jielun": "compliant", "biz_files": ""},
+ * {"id": "id2", "jianyan_jieguo": "鐧借壊绮夋湯", "jianyan_jielun": "compliant", "biz_files": "f1;f2"}
+ * ]
+ * }
+ * biz_sign 涓烘湰娆″綍鍏ョ殑鍏变韩绛惧悕锛屼粎瀹¤璁板綍鍦� lims_sign锛堜笟鍔¤〃涓嶈惤绛惧悕鍒楋級銆�
+ * 鍙厑璁� jianyan_zhuangtai = pending_test 涓� jiancha_ren = 褰撳墠鐢ㄦ埛 鐨勮琚洿鏂帮紝
+ * 鎴愬姛鍚庣姸鎬佺疆涓� pending_audit銆�
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "妫�楠屼换鍔℃壒閲忓綍鍏ョ粨鏋�")
+ @PostMapping("/submit-result")
+ public R<String> submitResult(@RequestBody LimsJianyanResultParam param) {
+ if (param == null || param.getData() == null || param.getData().isEmpty()) {
+ return R.error("璇烽�夋嫨鑷冲皯涓�鏉℃楠屼换鍔�");
+ }
+ if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ for (int i = 0; i < param.getData().size(); i++) {
+ LimsJianyanResultItem item = param.getData().get(i);
+ if (item == null || item.getId() == null || item.getId().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戜换鍔� id");
+ }
+ if (item.getJianyanJieguo() == null || item.getJianyanJieguo().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戞楠岀粨鏋�");
+ }
+ if (item.getJianyanJielun() == null || item.getJianyanJielun().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戞楠岀粨璁�");
+ }
+ try {
+ JianyanJielun.fromCode(item.getJianyanJielun());
+ } catch (IllegalArgumentException e) {
+ return R.error("绗� " + (i + 1) + " 鏉℃楠岀粨璁哄�奸潪娉曪細" + item.getJianyanJielun());
+ }
+ }
+
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ param.getBizSign(), SignOpType.JIANCE_SUBMIT_RESULT, null);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ List<String> submitIds = param.getData().stream().map(d -> d.getId()).collect(Collectors.toList());
+
+ LimsSubmitResultOutcome outcome = limsJianceTaskService.submitJianyanResult(param);
+ int affected = outcome.getAffected();
+ int total = param.getData().size();
+ String flowWarn = "";
+ if (!outcome.getFlowLaunchFailed().isEmpty()) {
+ List<String> failed = outcome.getFlowLaunchFailed().stream().distinct().collect(Collectors.toList());
+ flowWarn = "锛涘彟鏈� " + failed.size() + " 鏉″凡鐧昏缁撴灉鏈兘鍙戣捣寮傚父璋冩煡娴佺▼锛堟祦姘村彿锛�"
+ + String.join("銆�", failed) + "锛夛紝璇锋鏌ュ伐浣滄祦寮曟搸";
+ }
+ if (affected <= 0) {
+ return R.error("鎵�閫変换鍔′笉鍦ㄥ緟褰曞叆鐘舵�侊紝鎴栧苟闈炴湰浜轰换鍔�");
+ }
+ limsSignService.recordSignUsage(
+ sign, SignOpType.JIANCE_SUBMIT_RESULT, submitIds, outcome.getAffectedTargets());
+ if (affected < total) {
+ return R.success("宸叉彁浜� " + affected + " 鏉★紝" + (total - affected) + " 鏉″洜鐘舵�佷笉瀵规垨闈炴湰浜轰换鍔¤璺宠繃" + flowWarn);
+ }
+ return R.success("宸叉彁浜� " + affected + " 鏉�" + flowWarn);
+ }
+
+ /**
+ * 妫�楠岀粨鏋滄壒閲忓鏍搁�氳繃銆�
+ * 璇锋眰浣擄細{
+ * "biz_sign": "瀹℃牳浜虹鍚�",
+ * "data": [{"id": "id1"}, {"id": "id2"}]
+ * }
+ * biz_sign 涓烘湰娆″鏍哥殑鍏变韩绛惧悕锛屼粎瀹¤璁板綍鍦� lims_sign锛堜笟鍔¤〃涓嶈惤绛惧悕鍒楋級锛�
+ * 瀹℃牳浜猴紙renwu_shenheren锛夊彇褰撳墠鐧诲綍鐢ㄦ埛 userId銆�
+ * 鍏佽 pending_audit 鐨勮琚洿鏂帮紝鎴愬姛鍚庣姸鎬佺疆涓� audited銆�
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "妫�楠岀粨鏋滄壒閲忓鏍搁�氳繃")
+ @PostMapping("/audit-pass")
+ public R<String> auditPass(@RequestBody LimsJianyanAuditParam param) {
+ if (param == null || param.getData() == null || param.getData().isEmpty()) {
+ return R.error("璇烽�夋嫨鑷冲皯涓�鏉℃楠屼换鍔�");
+ }
+ if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ for (int i = 0; i < param.getData().size(); i++) {
+ LimsJianyanAuditItem item = param.getData().get(i);
+ if (item == null || item.getId() == null || item.getId().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戜换鍔� id");
+ }
+ }
+
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ param.getBizSign(), SignOpType.JIANCE_AUDIT_PASS, null);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ List<String> auditPassIds = param.getData().stream().map(d -> d.getId()).collect(Collectors.toList());
+
+ LimsJianyanAuditOutcome outcome = limsJianceTaskService.passJianyanAudit(param);
+ int affected = outcome.getAffected();
+ int total = outcome.getTotal();
+ if (affected <= 0) {
+ return R.error(buildAuditOutcomeRejectMessage(outcome, "瀹℃牳"));
+ }
+ limsSignService.recordSignUsage(
+ sign, SignOpType.JIANCE_AUDIT_PASS, auditPassIds, outcome.getAffectedTargets());
+ if (affected < total) {
+ return R.success("宸插鏍� " + affected + " 鏉★紝" + (total - affected)
+ + " 鏉¤璺宠繃锛�" + buildSkippedReason(outcome) + "锛�");
+ }
+ return R.success("宸插鏍� " + affected + " 鏉�");
+ }
+
+ /**
+ * 妫�楠岀粨鏋滈」鐩��鍥炪��
+ * 璇锋眰浣擄細{
+ * "biz_sign": "閫�鍥炰汉绛惧悕",
+ * "note": "鍏变韩鐨勯��鍥炲師鍥�",
+ * "data": [{"id": "id1"}, {"id": "id2"}]
+ * }
+ * biz_sign 鍏变韩锛堜粎瀹¤鍦� lims_sign锛屼笟鍔¤〃涓嶈惤绛惧悕鍒楋級锛沶ote 鍏变韩 鈫� 姣忚 tuihui_yuanyin锛�
+ * 閫�鍥炰汉锛坮enwu_tuihuiren锛夊彇褰撳墠鐧诲綍鐢ㄦ埛 userId銆�
+ * 鍏ュ彛鐘舵�� audited锛屾垚鍔熷悗缃负 audit_rejected銆�
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "妫�楠岀粨鏋滄壒閲忛」鐩��鍥�")
+ @PostMapping("/audit-fail")
+ public R<String> auditFail(@RequestBody LimsJianyanAuditFailParam param) {
+ if (param == null || param.getData() == null || param.getData().isEmpty()) {
+ return R.error("璇烽�夋嫨鑷冲皯涓�鏉℃楠屼换鍔�");
+ }
+ if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ if (param.getNote() == null || param.getNote().isEmpty()) {
+ return R.error("璇峰~鍐欓��鍥炲師鍥�");
+ }
+ for (int i = 0; i < param.getData().size(); i++) {
+ LimsJianyanAuditItem item = param.getData().get(i);
+ if (item == null || item.getId() == null || item.getId().isEmpty()) {
+ return R.error("绗� " + (i + 1) + " 鏉$己灏戜换鍔� id");
+ }
+ }
+
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ param.getBizSign(), SignOpType.JIANCE_AUDIT_FAIL, null);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ List<String> auditFailIds = param.getData().stream().map(d -> d.getId()).collect(Collectors.toList());
+
+ LimsJianyanAuditOutcome outcome = limsJianceTaskService.failJianyanAudit(param);
+ int affected = outcome.getAffected();
+ int total = outcome.getTotal();
+ if (affected <= 0) {
+ return R.error(buildAuditOutcomeRejectMessage(outcome, "閫�鍥�"));
+ }
+ limsSignService.recordSignUsage(
+ sign, SignOpType.JIANCE_AUDIT_FAIL, auditFailIds, outcome.getAffectedTargets());
+ if (affected < total) {
+ return R.success("宸查��鍥� " + affected + " 鏉★紝" + (total - affected)
+ + " 鏉¤璺宠繃锛�" + buildSkippedReason(outcome) + "锛�");
+ }
+ return R.success("宸查��鍥� " + affected + " 鏉�");
+ }
+
+ /**
+ * 鍏ㄩ噺琚嫆鏃讹紝鎸� outcome 鍚勫垎妗跺ぇ灏忕粰鍑洪拡瀵规�у師鍥犮��
+ * 鍗曚竴鍘熷洜鏃剁洿鎺ヨ鏄庯紝澶氬師鍥犲苟瀛樻椂鍒嗙被鍒楀嚭銆俛udit-pass / audit-fail 鍏辩敤銆�
+ *
+ * @param action "瀹℃牳" 鎴� "閫�鍥�" 鈥� 鐢ㄤ簬鍔ㄨ瘝鏇挎崲
+ */
+ private String buildAuditOutcomeRejectMessage(LimsJianyanAuditOutcome o, String action) {
+ List<String> parts = new java.util.ArrayList<>();
+ if (!o.getNotFoundIds().isEmpty()) {
+ parts.add(o.getNotFoundIds().size() + " 鏉′换鍔′笉瀛樺湪鎴栧凡鍒犻櫎");
+ }
+ if (!o.getWrongStatusIds().isEmpty()) {
+ parts.add(o.getWrongStatusIds().size() + " 鏉′换鍔$姸鎬佷笉鏄�屽緟瀹℃牳銆嶏紝鏃犳硶" + action);
+ }
+ if (!o.getSelfRejectIds().isEmpty()) {
+ parts.add(o.getSelfRejectIds().size() + " 鏉′换鍔$殑褰曞叆浜烘槸鎮ㄦ湰浜猴紝涓嶈兘" + action + "鑷繁鐨勪换鍔�");
+ }
+ if (!o.getNotInBanzuIds().isEmpty()) {
+ parts.add(o.getNotInBanzuIds().size() + " 鏉′换鍔℃墍灞炵彮缁勬偍涓嶆槸鎴愬憳锛屾棤鏉�" + action);
+ }
+ if (parts.isEmpty()) {
+ return "娌℃湁鍙�" + action + "鐨勪换鍔�";
+ }
+ if (parts.size() == 1) {
+ return parts.get(0);
+ }
+ return "鏃犲彲" + action + "浠诲姟锛�" + String.join("锛�", parts);
+ }
+
+ /**
+ * 鍗曟潯鎾ゅ洖妫�楠岄」缁撴灉锛氫粎 pending_audit 鈫� cancelled銆�
+ * 璇锋眰浣擄細{ "id": "<lims_jianyan_xiang.f_id>", "biz_sign": "<lims_sign 涓婚敭>" }
+ * biz_sign 鏄鍏� /lims/biz/sign/verify 鎷垮埌鐨� sign_id锛宱p_type=jiance_result_cancel锛�
+ * data_id=鏈楠岄」 id銆傛湰鎺ュ彛浼氭牎楠岀湡瀹炴�� / op_type / data_id / 绛惧悕浜恒��
+ */
+ @Transactional(rollbackFor = Exception.class)
+ @Operation(summary = "鎾ゅ洖妫�楠岄」缁撴灉")
+ @PostMapping("/cancel-result")
+ public R<String> cancelResult(@RequestBody Map<String, String> params) {
+ String id = params == null ? null : params.get("id");
+ String bizSign = params == null ? null : params.get("biz_sign");
+ if (id == null || id.isEmpty()) {
+ return R.error("缂哄皯妫�楠岄」 id");
+ }
+ if (bizSign == null || bizSign.isEmpty()) {
+ return R.error("璇锋坊鍔犵數瀛愮鍚�");
+ }
+ LimsJianyanXiangEntity row = limsJianyanXiangService.getById(id);
+ if (row == null || (row.getDeleteMark() != null && row.getDeleteMark() == 1)) {
+ return R.error("褰撳墠妫�楠岄」涓嶅瓨鍦ㄦ垨宸插垹闄�");
+ }
+ LimsSignEntity sign;
+ try {
+ sign = limsSignService.requireValidSign(
+ bizSign, SignOpType.JIANCE_RESULT_CANCEL, id);
+ } catch (DataException e) {
+ return R.error(e.getMessage());
+ }
+ if (!limsJianyanXiangService.cancelResult(id)) {
+ return R.error("褰撳墠妫�楠岄」涓嶅湪寰呭鏍哥姸鎬侊紝鏃犳硶鎾ゅ洖");
+ }
+ LimsSignUsageTarget target = LimsSignUsageTarget.builder()
+ .targetTable("lims_jianyan_xiang")
+ .targetId(row.getId())
+ .bizType("JIANCE_TASK")
+ .bizModule("LIMS-妫�娴嬩换鍔�")
+ .bizCode(row.getJianyanLiushuihao())
+ .build();
+ limsSignService.recordSignUsage(sign, SignOpType.JIANCE_RESULT_CANCEL,
+ Collections.singletonList(id), Collections.singletonList(target));
+ return R.success("宸叉挙鍥�");
+ }
+
+ /** 閮ㄥ垎鎴愬姛鏃剁畝鐭弿杩拌璺宠繃鐨勫師鍥狅紝audit-pass / audit-fail 鍏辩敤銆� */
+ private String buildSkippedReason(LimsJianyanAuditOutcome o) {
+ List<String> parts = new java.util.ArrayList<>();
+ if (!o.getNotFoundIds().isEmpty()) {
+ parts.add("浠诲姟涓嶅瓨鍦� " + o.getNotFoundIds().size());
+ }
+ if (!o.getWrongStatusIds().isEmpty()) {
+ parts.add("鐘舵�佷笉绗� " + o.getWrongStatusIds().size());
+ }
+ if (!o.getSelfRejectIds().isEmpty()) {
+ parts.add("褰曞叆浜烘槸鏈汉 " + o.getSelfRejectIds().size());
+ }
+ if (!o.getNotInBanzuIds().isEmpty()) {
+ parts.add("闈炴湰鐝粍 " + o.getNotInBanzuIds().size());
+ }
+ return parts.isEmpty() ? "鏈煡鍘熷洜" : String.join("锛�", parts);
+ }
+
+ /**
+ * 鎵嬪姩瑙﹀彂"瑕佹眰瀹屾垚鏃ユ湡"鎻愰啋鎵弿锛堝紑鍙� / 绱ф�ヨˉ鍙戝満鏅級銆�
+ *
+ * <p>姝e父鐢熶骇鐢� cron 姣忓ぉ 8:00 鑷姩瑙﹀彂锛涙湰绔偣涓虹粫杩� cron 鐨勮繍缁村叆鍙c��
+ *
+ * @param dry true 浠呮棩蹇椾笉鍙戞秷鎭�佷笉鍐� remind_flags锛堝紑鍙戞湡璋冭瘯榛樿锛�
+ * @return 鍚勫垎鏀粺璁� {hit, sent, skipped}
+ */
+ @Operation(summary = "鎵嬪姩瑙﹀彂瀹屾垚鏃ユ湡鎻愰啋鎵弿")
+ @PostMapping("/remind/run-now")
+ public R<Map<String, Map<String, Integer>>> remindRunNow(
+ @RequestParam(value = "dry", defaultValue = "true") boolean dry) {
+ Map<String, Map<String, Integer>> stats = limsJianceTaskRemindService.scanAndRemind(dry);
+ return R.success(stats);
+ }
+}
--
Gitblit v1.8.0