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

diff --git a/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsQingyandanController.java b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsQingyandanController.java
new file mode 100644
index 0000000..bea1703
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsQingyandanController.java
@@ -0,0 +1,223 @@
+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.BizStatus;
+import jnpf.limsEntity.LimsQingyandanEntity;
+import jnpf.limsEntity.LimsSignEntity;
+import jnpf.limsEntity.LimsSignUsageTarget;
+import jnpf.limsEntity.SignOpType;
+import jnpf.limsService.LimsQingyandanService;
+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.RestController;
+
+import java.util.Collections;
+import java.util.Map;
+
+@Slf4j
+@Tag(name = "lims璇烽獙鍗曟帴鍙�", description = "璇烽獙鍗曟彁浜� / 鎾ゅ洖 / 搴熷純 / 缂栬緫")
+@RestController
+@RequestMapping("/lims/biz")
+public class LimsQingyandanController {
+
+    @Autowired
+    private LimsQingyandanService limsQingyandanService;
+    @Autowired
+    private LimsSignService limsSignService;
+
+    /**
+     * 鎴愬搧璇烽獙鍗曟彁浜� -> 澶嶅埗鏁版嵁鍒板彇鏍疯〃
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Operation(summary = "鎴愬搧璇烽獙鍗曟彁浜�")
+    @PostMapping("/qingyandan/submit")
+    public R<String> qingyandanSubmit(@RequestBody LimsQingyandanEntity formData) {
+        LimsQingyandanEntity qingyandan = limsQingyandanService.findByFormData(formData);
+        if (qingyandan == null) {
+            return R.error("璇烽獙鍗曟暟鎹笉瀛樺湪");
+        }
+
+        BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
+        if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
+                && currentStatus != BizStatus.REJECTED) {
+            return R.error("褰撳墠鐘舵�乕" + currentStatus.getLabel() + "]涓嶅厑璁告彁浜�");
+        }
+
+        LimsSignEntity sign;
+        try {
+            sign = requireQingyandanSign(qingyandan, formData, SignOpType.QINGYANDAN_SUBMIT);
+        } catch (DataException e) {
+            return R.error(e.getMessage());
+        }
+        limsQingyandanService.submit(qingyandan);
+        claimQingyandanSignUsageOnly(sign, SignOpType.QINGYANDAN_SUBMIT, qingyandan);
+        return R.success("鎻愪氦鎴愬姛");
+    }
+
+    /**
+     * 璇烽獙鍗曟搷浣滐細鎾ゅ洖 / 搴熷純
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Operation(summary = "璇烽獙鍗曟搷浣滐紙鎾ゅ洖/搴熷純锛�")
+    @PostMapping("/qingyandan/action")
+    public R<String> qingyandanAction(@RequestBody Map<String, String> params) {
+        if (params == null) {
+            return R.error("璇锋眰鍙傛暟涓嶈兘涓虹┖");
+        }
+        String action = params.get("action");
+        String liushuihao = params.get("jianyan_liushuihao");
+
+        if (liushuihao == null || liushuihao.isEmpty()) {
+            return R.error("妫�楠屾祦姘村彿涓嶈兘涓虹┖");
+        }
+
+        LimsQingyandanEntity qingyandan = limsQingyandanService.findByLiushuihao(liushuihao);
+        if (qingyandan == null) {
+            return R.error("璇烽獙鍗曟暟鎹笉瀛樺湪");
+        }
+
+        BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
+
+        switch (action) {
+            case "cancel":
+                if (currentStatus != BizStatus.CONFIRMED) {
+                    return R.error("鍙湁宸叉彁浜ょ姸鎬佺殑璇烽獙鍗曟墠鑳芥挙鍥�");
+                }
+                LimsSignEntity cancelSign;
+                try {
+                    cancelSign = requireQingyandanActionSign(
+                            qingyandan, params, SignOpType.QINGYANDAN_CANCEL);
+                } catch (DataException e) {
+                    return R.error(e.getMessage());
+                }
+                if (!limsQingyandanService.cancel(qingyandan)) {
+                    return R.error("璇烽獙鍗曠姸鎬佸凡鍙樻洿锛屾挙鍥炴湭鐢熸晥");
+                }
+                recordQingyandanSignUsage(
+                        cancelSign, SignOpType.QINGYANDAN_CANCEL, qingyandan);
+                return R.success("鎾ゅ洖鎴愬姛");
+
+            case "delete":
+                if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
+                && currentStatus != BizStatus.REJECTED) {
+                    return R.error("鍙湁宸插垱寤烘垨宸叉挙鍥炵姸鎬佺殑璇烽獙鍗曟墠鑳藉簾寮�");
+                }
+                limsQingyandanService.delete(qingyandan);
+                return R.success("搴熷純鎴愬姛");
+
+            default:
+                return R.error("涓嶆敮鎸佺殑鎿嶄綔: " + action);
+        }
+    }
+
+    /**
+     * 璇烽獙鍗曠紪杈�
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Operation(summary = "璇烽獙鍗曠紪杈�")
+    @PostMapping("/qingyandan/edit")
+    public R<String> qingyandanEdit(@RequestBody LimsQingyandanEntity formData) {
+        LimsQingyandanEntity qingyandan = limsQingyandanService.findByFormData(formData);
+        if (qingyandan == null) {
+            return R.error("璇烽獙鍗曟暟鎹笉瀛樺湪");
+        }
+
+        BizStatus currentStatus = BizStatus.fromCode(qingyandan.getBizStatus());
+        if (currentStatus != BizStatus.CREATED && currentStatus != BizStatus.CANCELLED
+                && currentStatus != BizStatus.REJECTED) {
+            return R.error("褰撳墠鐘舵�乕" + currentStatus.getLabel() + "]涓嶅厑璁哥紪杈�");
+        }
+
+        LimsSignEntity sign;
+        try {
+            sign = requireQingyandanSign(qingyandan, formData, SignOpType.QINGYANDAN_SUBMIT);
+        } catch (DataException e) {
+            return R.error(e.getMessage());
+        }
+        limsQingyandanService.edit(qingyandan, formData);
+        claimQingyandanSignUsageOnly(sign, SignOpType.QINGYANDAN_SUBMIT, qingyandan);
+        return R.success("缂栬緫鎴愬姛");
+    }
+
+    private LimsSignEntity requireQingyandanSign(LimsQingyandanEntity persisted,
+                                                  LimsQingyandanEntity request,
+                                                  SignOpType opType) {
+        String signId = request == null ? null : request.getBizSign();
+        if (signId == null || signId.trim().isEmpty()) {
+            throw new DataException("璇锋坊鍔犵數瀛愮鍚�");
+        }
+        signId = signId.trim();
+        persisted.setBizSign(signId);
+        if (request != null) {
+            request.setBizSign(signId);
+        }
+        return limsSignService.requireValidSign(
+                signId, opType, persisted.getJianyanLiushuihao());
+    }
+
+    private LimsSignEntity requireQingyandanActionSign(LimsQingyandanEntity persisted,
+                                                        Map<String, String> params,
+                                                        SignOpType opType) {
+        String signId = params.get("biz_sign");
+        if (signId == null || signId.trim().isEmpty()) {
+            signId = params.get("id");
+        }
+        if (signId == null || signId.trim().isEmpty()) {
+            throw new DataException("璇锋坊鍔犵數瀛愮鍚�");
+        }
+        return limsSignService.requireValidSign(
+                signId.trim(), opType, persisted.getJianyanLiushuihao());
+    }
+
+    /**
+     * 璁ら绛惧悕骞惰涓�鏉� {@code SIGN_USED}銆�
+     *
+     * <p>馃敶 <b>鍙墿鎾ゅ洖锛坽@code QINGYANDAN_CANCEL}锛夎蛋杩欐潯璺紝涓�瀛楁湭鍔�</b>锛氭挙鍥炴槸鍒楄〃鎸夐挳鍦烘櫙锛�
+     * 灞� 0 鏍规湰涓嶈鐩栵紙瀹炴祴鎾ゅ洖鍙骇鐢熷眰 2 + 灞� 3 涓ゆ潯浜嬩欢锛屾病鏈夊眰 0 浼撮殢浜嬩欢锛夈��
+     * 涓�鏃﹁瀹冧篃鏀硅蛋鍙棰嗭紝閭f绛惧悕灏卞交搴曟病鏈変换浣曡褰曘��
+     *
+     * <p>鎻愪氦 / 缂栬緫鏀硅蛋 {@link #claimQingyandanSignUsageOnly}鈥斺�旇閭i噷鐨勮鏄庛��
+     */
+    private void recordQingyandanSignUsage(LimsSignEntity sign,
+                                            SignOpType opType,
+                                            LimsQingyandanEntity qingyandan) {
+        limsSignService.recordSignUsage(sign, opType,
+                Collections.singletonList(qingyandan.getId()),
+                Collections.singletonList(qingyandanUsageTarget(qingyandan)));
+    }
+
+    /**
+     * 鍙仛绛惧悕鐨勫師瀛愯棰嗭紝<b>涓嶅彂 {@code SIGN_USED} 浜嬩欢</b>锛�2026-07-31锛孌8锛夈��
+     *
+     * <p>鎻愪氦 / 缂栬緫璧扮殑鏄湪绾胯〃鍗曡矾寰勶細琛ㄥ崟淇濆瓨鏈韩宸茬粡浜х敓灞� 0 浜嬩欢锛屽眰 0 浼氭嵁琛ㄥ崟閲岀殑
+     * {@code biz_sign} 瀛楁琛ュ啓涓�鏉$鍚嶄簨浠讹紱杩欓噷鍐嶅彂涓�鏉� {@code SIGN_USED} 灏辨槸鍚屼竴娆$鍚嶄袱鏉¤瘉鎹��
+     *
+     * <p><b>璁ら娌℃湁琚垹</b>锛氬畠鏄�岀鍚嶅崟娆℃秷璐广�嶇殑闃查噸鏀炬姢鏍忥紙ed4d908锛夛紝
+     * 鍘绘帀绛変簬鍚屼竴涓鍚嶅彲浠ュ弽澶嶄娇鐢ㄣ�傛湰娆℃敼鍔ㄥ彧鏄妸銆岃棰嗐�嶅拰銆屽彂浜嬩欢銆嶆媶寮�銆�
+     */
+    private void claimQingyandanSignUsageOnly(LimsSignEntity sign,
+                                               SignOpType opType,
+                                               LimsQingyandanEntity qingyandan) {
+        limsSignService.claimSignUsageOnly(sign, opType,
+                Collections.singletonList(qingyandan.getId()),
+                Collections.singletonList(qingyandanUsageTarget(qingyandan)));
+    }
+
+    private LimsSignUsageTarget qingyandanUsageTarget(LimsQingyandanEntity qingyandan) {
+        return LimsSignUsageTarget.builder()
+                .targetTable("lims_qingyandan")
+                .targetId(qingyandan.getId())
+                .bizType("QINGYANDAN")
+                .bizModule("LIMS-璇烽獙鍗�")
+                .bizCode(qingyandan.getJianyanLiushuihao())
+                .build();
+    }
+}

--
Gitblit v1.8.0