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/LimsHaocaiController.java |  358 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 358 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsHaocaiController.java b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsHaocaiController.java
new file mode 100644
index 0000000..209a6ac
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsHaocaiController.java
@@ -0,0 +1,358 @@
+package jnpf.limsController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jnpf.exception.DataException;
+import jnpf.limsEntity.LimsHaocaiChukuEntity;
+import jnpf.limsEntity.LimsHaocaiChukuShenqingZixiangEntity;
+import jnpf.limsEntity.LimsHaocaiJiechuEntity;
+import jnpf.limsEntity.LimsHaocaiRukuBatchParam;
+import jnpf.limsEntity.LimsHaocaiRukuEntity;
+import jnpf.limsEntity.LimsHaocaiRukuSubmitResult;
+import jnpf.limsEntity.LimsHaocaiRukuZixiangEntity;
+import jnpf.limsEntity.LimsHaocaiYanshouLingquParam;
+import jnpf.limsEntity.LimsSxtFlowEventParam;
+import jnpf.limsService.LimsHaocaiAlertService;
+import jnpf.limsService.LimsHaocaiService;
+import jnpf.util.R;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+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;
+
+import java.math.BigDecimal;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+@Slf4j
+@Tag(name = "lims鑰楁潗鎺ュ彛", description = "鑰楁潗鍑哄叆搴撶瓑鎺ュ彛锛堝綋鍓嶏細鍏ュ簱鎻愪氦鐢熸垚鏉$爜瀛愯〃璁板綍锛�")
+@RestController
+@RequestMapping("/lims/haocai")
+public class LimsHaocaiController {
+
+    private static final String BIZ_STATUS_YIXIAOHUI = "yixiaohui";
+
+    @Autowired
+    private LimsHaocaiService limsHaocaiService;
+
+    @Autowired
+    private LimsHaocaiAlertService limsHaocaiAlertService;
+
+    /**
+     * 鑰楁潗鍏ュ簱鎻愪氦锛氭寜涓昏〃 f_id 鍙栨潯鐮佸彿骞剁敓鎴愬瓙琛ㄦ潯鐮佽褰曘��
+     * 涓嶉槻閲嶏紝閲嶅鎻愪氦浼氫互鏂版潯鐮佸啀鐢熸垚涓�鎵广��
+     */
+    @Operation(summary = "鑰楁潗鍏ュ簱鎻愪氦")
+    @PostMapping("/ruku/submit")
+    public R<LimsHaocaiRukuSubmitResult> rukuSubmit(@RequestBody LimsHaocaiRukuEntity formData) {
+        String id = formData.getId();
+        if (id == null || id.isEmpty()) {
+            return R.error("鍏ュ簱鍗曚富閿�(f_id)涓嶈兘涓虹┖");
+        }
+
+        LimsHaocaiRukuEntity ruku = limsHaocaiService.getById(id);
+        if (ruku == null || Integer.valueOf(1).equals(ruku.getDeleteMark())) {
+            return R.error("鍏ュ簱鍗曚笉瀛樺湪锛�" + id);
+        }
+
+        try {
+            LimsHaocaiRukuSubmitResult result = limsHaocaiService.submitRuku(ruku);
+            return R.success("鍏ュ簱鎴愬姛", result);
+        } catch (DataException e) {
+            log.warn("鑰楁潗鍏ュ簱鎻愪氦澶辫触锛歳ukuId={}, msg={}", id, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗鍏ュ簱楠屾敹鎻愪氦锛氭寜娴佺▼鍏宠仈楠屾敹璁板綍锛屽苟鍥炲啓瀵瑰簲楠屾敹鐢宠銆�
+     */
+    @Operation(summary = "鑰楁潗鍏ュ簱楠屾敹鎻愪氦")
+    @PostMapping("/ruku/yanshou/submit")
+    public R<String> rukuYanshouSubmit(@RequestBody LimsSxtFlowEventParam param) {
+        if (param == null || param.getFlowId() == null || param.getFlowId().trim().isEmpty()) {
+            return R.error("娴佺▼id(flow_id)涓嶈兘涓虹┖");
+        }
+        if (param.getTaskId() == null || param.getTaskId().trim().isEmpty()) {
+            return R.error("娴佺▼浠诲姟id(task_id)涓嶈兘涓虹┖");
+        }
+        try {
+            limsHaocaiService.submitRukuYanshou(param.getFlowId(), param.getTaskId());
+            return R.success("楠屾敹鎻愪氦鎴愬姛");
+        } catch (DataException e) {
+            log.warn("鑰楁潗鍏ュ簱楠屾敹鎻愪氦澶辫触锛歠lowId={}, taskId={}, msg={}",
+                    param.getFlowId(), param.getTaskId(), e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗鍏ュ簱楠屾敹瀹屾垚锛氭寜楠屾敹娴佺▼鐘舵�佹洿鏂版垨閲嶇疆鍏宠仈楠屾敹鐢宠銆�
+     */
+    @Operation(summary = "鑰楁潗鍏ュ簱楠屾敹瀹屾垚")
+    @PostMapping("/ruku/yanshou/finish")
+    public R<String> rukuYanshouFinish(@RequestBody LimsSxtFlowEventParam param) {
+        if (param == null || param.getFlowId() == null || param.getFlowId().trim().isEmpty()) {
+            return R.error("娴佺▼id(flow_id)涓嶈兘涓虹┖");
+        }
+        if (param.getTaskId() == null || param.getTaskId().trim().isEmpty()) {
+            return R.error("娴佺▼浠诲姟id(task_id)涓嶈兘涓虹┖");
+        }
+        try {
+            limsHaocaiService.finishRukuYanshou(param.getFlowId(), param.getTaskId());
+            return R.success("楠屾敹瀹屾垚澶勭悊鎴愬姛");
+        } catch (DataException e) {
+            log.warn("鑰楁潗鍏ュ簱楠屾敹瀹屾垚澶勭悊澶辫触锛歠lowId={}, taskId={}, msg={}",
+                    param.getFlowId(), param.getTaskId(), e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 棰嗗彇鑰楁潗楠屾敹鐢宠銆備粎寰呴獙鏀剁敵璇峰彲棰嗗彇锛岄鍙栧悗杞负楠屾敹涓苟鐧昏褰撳墠瀹為獙鍛樹笌棰嗗彇鏃堕棿銆�
+     * biz_sign 褰撳墠浠呮帴鏀讹紝涓嶅弬涓庝笟鍔″鐞嗐��
+     */
+    @Operation(summary = "棰嗗彇鑰楁潗楠屾敹鐢宠")
+    @PostMapping("/yanshou/lingqu")
+    public R<String> lingquYanshou(@RequestBody LimsHaocaiYanshouLingquParam param) {
+        if (param == null || param.getShenqingId() == null || param.getShenqingId().trim().isEmpty()) {
+            return R.error("鑰楁潗楠屾敹鐢宠涓婚敭(shenqing_id)涓嶈兘涓虹┖");
+        }
+        String shenqingId = param.getShenqingId().trim();
+        try {
+            limsHaocaiService.lingquYanshouShenqing(shenqingId);
+            return R.success("棰嗗彇鎴愬姛");
+        } catch (DataException e) {
+            log.warn("棰嗗彇鑰楁潗楠屾敹鐢宠澶辫触锛歴henqingId={}, msg={}", shenqingId, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗鍊熷嚭鎻愪氦锛氫笉鍐欏�熷嚭鍗曪紝鍙寜鏉$爜鏇存柊鍏ュ簱鎵规鐘舵�佸苟鍐欐棩蹇椼��
+     */
+    @Operation(summary = "鑰楁潗鍊熷嚭鎻愪氦")
+    @PostMapping("/jiechu/submit")
+    public R<String> jiechuSubmit(@RequestBody LimsHaocaiJiechuEntity formData) {
+        try {
+            limsHaocaiService.submitJiechu(formData);
+            return R.success("鍊熷嚭鎴愬姛");
+        } catch (DataException e) {
+            String tiaomaBianma = formData == null ? null : formData.getTiaomaBianma();
+            log.warn("鑰楁潗鍊熷嚭鎻愪氦澶辫触锛歵iaomaBianma={}, msg={}", tiaomaBianma, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗褰掕繕鎻愪氦锛氭寜鏉$爜鏇存柊鍊熷嚭璁板綍鍜屽叆搴撴壒娆$姸鎬侊紝骞跺啓鏃ュ織銆�
+     */
+    @Operation(summary = "鑰楁潗褰掕繕鎻愪氦")
+    @PostMapping("/guihuan/submit")
+    public R<String> guihuanSubmit(@RequestBody LimsHaocaiJiechuEntity formData) {
+        try {
+            limsHaocaiService.submitGuihuan(formData);
+            return R.success("褰掕繕鎴愬姛");
+        } catch (DataException e) {
+            String tiaomaBianma = formData == null ? null : formData.getTiaomaBianma();
+            log.warn("鑰楁潗褰掕繕鎻愪氦澶辫触锛歵iaomaBianma={}, msg={}", tiaomaBianma, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗鍑哄簱鎻愪氦锛氫笉鍐欏嚭搴撳崟锛屽彧鎸夊瓙琛ㄦ潯鐮佹墸鍑忓簱瀛樸�佹洿鏂扮敵棰嗗瓙椤圭姸鎬佸苟鍐欐棩蹇椼��
+     */
+    @Operation(summary = "鑰楁潗鍑哄簱鎻愪氦")
+    @PostMapping("/chuku/submit")
+    public R<String> chukuSubmit(@RequestBody LimsHaocaiChukuEntity formData) {
+        try {
+            limsHaocaiService.submitChuku(formData);
+            return R.success("鍑哄簱鎴愬姛");
+        } catch (DataException e) {
+            String shenlingDanhao = formData == null ? null : formData.getShenlingDanhao();
+            log.warn("鑰楁潗鍑哄簱鎻愪氦澶辫触锛歴henlingDanhao={}, msg={}", shenlingDanhao, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗閿�姣佹彁浜わ細鎸夐攢姣佸崟鍙烽攢姣佸瓙琛ㄦ潯鐮佸搴旂殑鍏ュ簱鎵规銆�
+     */
+    @Operation(summary = "鑰楁潗閿�姣佹彁浜�")
+    @PostMapping("/xiaohui/submit")
+    public R<String> xiaohuiSubmit(@RequestBody(required = false) Map<String, Object> formData,
+                                   @RequestParam(value = "xiaohui_danhao", required = false) String xiaohuiDanhao) {
+        String danhao = resolveXiaohuiDanhao(formData, xiaohuiDanhao);
+        try {
+            limsHaocaiService.submitXiaohui(danhao);
+            return R.success("閿�姣佹垚鍔�");
+        } catch (DataException e) {
+            log.warn("鑰楁潗閿�姣佹彁浜ゅけ璐ワ細xiaohuiDanhao={}, msg={}", danhao, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鑰楁潗閿�姣佺敵璇峰彂璧凤細鎸夋祦绋嬪弬鏁板皢閿�姣佺敵璇峰瓙椤规潯鐮佸搴旂殑鍏ュ簱瀛愰」鏍囪涓哄緟閿�姣併��
+     */
+    @Operation(summary = "鑰楁潗閿�姣佺敵璇峰彂璧�")
+    @PostMapping("/xiaohui/start")
+    public R<Map<String, Object>> xiaohuiStart(@RequestBody(required = false) String formData,
+                                               @RequestParam(value = "flow_id", required = false) String flowId,
+                                               @RequestParam(value = "task_id", required = false) String taskId) {
+        String resolvedFlowId = resolveParam(formData, flowId, "flow_id", "flowId");
+        String resolvedTaskId = resolveParam(formData, taskId, "task_id", "taskId");
+        try {
+            int zixiangCount = limsHaocaiService.startXiaohuiShenqing(resolvedFlowId, resolvedTaskId);
+            Map<String, Object> data = new java.util.HashMap<>(3);
+            data.put("flow_id", resolvedFlowId);
+            data.put("task_id", resolvedTaskId);
+            data.put("zixiang_count", zixiangCount);
+            return R.success("閿�姣佺敵璇峰彂璧锋垚鍔�", data);
+        } catch (DataException e) {
+            log.warn("鑰楁潗閿�姣佺敵璇峰彂璧峰け璐ワ細flowId={}, taskId={}, msg={}",
+                    resolvedFlowId, resolvedTaskId, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎵归噺杞緟閿�姣侊細鎸夊叆搴撲富琛� f_id 鎵归噺灏嗗墿浣欓噺澶т簬 0 鐨勫叆搴撳瓙椤规爣璁颁负寰呴攢姣併��
+     */
+    @Operation(summary = "鑰楁潗鍏ュ簱鎵归噺杞緟閿�姣�")
+    @PostMapping("/ruku/batch-to-xiaohui")
+    public R<Map<String, Object>> batchToXiaohui(@RequestBody LimsHaocaiRukuBatchParam param) {
+        try {
+            int zixiangCount = limsHaocaiService.batchMarkRukuZixiangDaixiaohui(
+                    param == null ? null : param.getRukuIds());
+            Map<String, Object> data = new java.util.HashMap<>(2);
+            data.put("ruku_ids", param == null ? null : param.getRukuIds());
+            data.put("zixiang_count", zixiangCount);
+            return R.success("鎵归噺杞緟閿�姣佹垚鍔�", data);
+        } catch (DataException e) {
+            log.warn("鑰楁潗鍏ュ簱鎵归噺杞緟閿�姣佸け璐ワ細rukuIds={}, msg={}",
+                    param == null ? null : param.getRukuIds(), e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    /**
+     * 鎸夋潯鐮佺紪鐮佹煡璇㈣�楁潗鍏ュ簱瀛愰」銆�
+     */
+    @Operation(summary = "鎸夋潯鐮佺紪鐮佹煡璇㈣�楁潗鍏ュ簱瀛愰」")
+    @GetMapping("/ruku-zixiang/by-tiaoma")
+    public R<LimsHaocaiRukuZixiangEntity> getRukuZixiangByTiaomaBianma(
+            @RequestParam("tiaomaBianma") String tiaomaBianma,
+            @RequestParam(value = "shifou_guihuan", defaultValue = "no") String shifouGuihuan) {
+        if (tiaomaBianma == null || tiaomaBianma.trim().isEmpty()) {
+            return R.error("鏉$爜缂栫爜涓嶈兘涓虹┖");
+        }
+        java.util.List<LimsHaocaiRukuZixiangEntity> list =
+                limsHaocaiService.listRukuZixiangByTiaomaBianma(tiaomaBianma);
+        if (list == null || list.isEmpty()) {
+            return R.error("鏈壘鍒板搴旂殑鑰楁潗鍏ュ簱瀛愰」锛�" + tiaomaBianma);
+        }
+        LimsHaocaiRukuZixiangEntity entity = list.get(0);
+        if (isYes(shifouGuihuan)) {
+            return R.success(entity);
+        }
+        if (BIZ_STATUS_YIXIAOHUI.equalsIgnoreCase(trimToEmpty(entity.getBizStatus()))) {
+            return R.error("璇ヨ�楁潗宸查攢姣侊紝鏃犳硶浣跨敤锛�" + tiaomaBianma);
+        }
+        BigDecimal shengyuliang = parseShengyuliang(entity.getShengyuliang());
+        if (shengyuliang == null) {
+            return R.error("鑰楁潗鍓╀綑閲忔棤鏁堬細" + entity.getShengyuliang());
+        }
+        if (shengyuliang.compareTo(BigDecimal.ZERO) <= 0) {
+            return R.error("璇ヨ�楁潗鍓╀綑閲忎笉瓒筹紝鏃犳硶浣跨敤锛�" + tiaomaBianma);
+        }
+        return R.success(entity);
+    }
+
+    /**
+     * 鏍规嵁鐢抽鍗曞彿鍜屾潯鐮佺紪鐮佹煡璇㈠彲鍑哄簱鐨勮�楁潗鐢抽瀛愰」銆�
+     */
+    @Operation(summary = "鏍规嵁鐢抽鍗曞彿鍜屾潯鐮佺紪鐮佹煡璇㈣�楁潗鐢抽瀛愰」")
+    @GetMapping("/chuku-shenqing-zixiang/by-shenling-tiaoma")
+    public R<LimsHaocaiChukuShenqingZixiangEntity> getChukuShenqingZixiangByShenlingAndTiaoma(
+            @RequestParam(value = "shenlingDanhao", required = false) String shenlingDanhao,
+            @RequestParam(value = "shenling_danhao", required = false) String shenlingDanhaoAlias,
+            @RequestParam("tiaomaBianma") String tiaomaBianma) {
+        String danhao = trimToEmpty(shenlingDanhao).isEmpty() ? shenlingDanhaoAlias : shenlingDanhao;
+        try {
+            return R.success(limsHaocaiService.getChukuShenqingZixiangForChuku(danhao, tiaomaBianma));
+        } catch (DataException e) {
+            log.warn("鎸夌敵棰嗗崟鍙峰拰鏉$爜鏌ヨ鑰楁潗鐢抽瀛愰」澶辫触锛歴henlingDanhao={}, tiaomaBianma={}, msg={}",
+                    danhao, tiaomaBianma, e.getMessage());
+            return R.error(e.getMessage());
+        }
+    }
+
+    private String trimToEmpty(String value) {
+        return value == null ? "" : value.trim();
+    }
+
+    private boolean isYes(String value) {
+        return "yes".equalsIgnoreCase(trimToEmpty(value));
+    }
+
+    private BigDecimal parseShengyuliang(String shengyuliang) {
+        if (shengyuliang == null || shengyuliang.trim().isEmpty()) {
+            return null;
+        }
+        try {
+            return new BigDecimal(shengyuliang.trim());
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    private String resolveXiaohuiDanhao(Map<String, Object> formData, String xiaohuiDanhao) {
+        if (xiaohuiDanhao != null && !xiaohuiDanhao.trim().isEmpty()) {
+            return xiaohuiDanhao;
+        }
+        if (formData == null) {
+            return null;
+        }
+        Object value = formData.get("xiaohui_danhao");
+        return value == null ? null : String.valueOf(value);
+    }
+
+    private String resolveParam(String formData, String requestParam, String... bodyKeys) {
+        if (requestParam != null && !requestParam.trim().isEmpty()) {
+            return requestParam.trim();
+        }
+        String body = trimToEmpty(formData);
+        if (body.isEmpty()) {
+            return null;
+        }
+        for (String bodyKey : bodyKeys) {
+            Matcher matcher = Pattern.compile("[\"']?" + Pattern.quote(bodyKey) + "[\"']?\\s*[:=]\\s*[\"']?([^\"',}\\s]+)")
+                    .matcher(body);
+            if (matcher.find()) {
+                return matcher.group(1).trim();
+            }
+        }
+        return null;
+    }
+
+    /**
+     * 鎵嬪姩瑙﹀彂鑰楁潗缂鸿揣棰勮鎵弿锛堝紑鍙� / 杩愮淮琛ュ彂鍦烘櫙锛夈��
+     * 姝e父鐢熶骇鐢� cron 姣忓ぉ 01:00 鑷姩瑙﹀彂銆�
+     *
+     * @param dry true 浠呮棩蹇椼�佷笉瀹為檯鍙戞秷鎭�
+     * @return 缁熻 {hit, sent, recipients}
+     */
+    @Operation(summary = "鎵嬪姩瑙﹀彂鑰楁潗缂鸿揣棰勮鎵弿")
+    @PostMapping("/alert/run-now")
+    public R<Map<String, Integer>> alertRunNow(
+            @RequestParam(value = "dry", defaultValue = "true") boolean dry) {
+        return R.success(limsHaocaiAlertService.scanAndAlert(dry));
+    }
+}

--
Gitblit v1.8.0