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

diff --git a/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsBaogaoController.java b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsBaogaoController.java
new file mode 100644
index 0000000..2fc28e4
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-controller/src/main/java/jnpf/limsController/LimsBaogaoController.java
@@ -0,0 +1,278 @@
+package jnpf.limsController;
+
+import io.swagger.v3.oas.annotations.Operation;
+import io.swagger.v3.oas.annotations.tags.Tag;
+import jnpf.base.UserInfo;
+import jnpf.exception.DataException;
+import jnpf.limsEntity.BaogaoShenheJibie;
+import jnpf.limsEntity.BaogaoStatus;
+import jnpf.limsEntity.LimsBaogaoSubmitParam;
+import jnpf.limsEntity.LimsQingyandanEntity;
+import jnpf.limsEntity.LimsSignEntity;
+import jnpf.limsEntity.LimsSignUsageTarget;
+import jnpf.limsEntity.SignOpType;
+import jnpf.limsService.LimsBaogaoShouquanService;
+import jnpf.limsService.LimsQingyandanReportService;
+import jnpf.limsService.LimsQingyandanService;
+import jnpf.limsService.LimsSignService;
+import jnpf.util.R;
+import jnpf.util.UserProvider;
+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/baogao")
+public class LimsBaogaoController {
+
+    @Autowired
+    private LimsQingyandanReportService limsQingyandanReportService;
+
+    @Autowired
+    private LimsQingyandanService limsQingyandanService;
+
+    @Autowired
+    private LimsSignService limsSignService;
+
+    @Autowired
+    private LimsBaogaoShouquanService limsBaogaoShouquanService;
+
+    /**
+     * 鐢熸垚鎶ュ憡锛坰tatus: created 鈫� pending_audit锛夈��
+     * 璇锋眰浣擄細{ "id": "<reportId>", "baogao_riqi": "yyyy-MM-dd",
+     *          "beizhu": "...", "biz_sign": "<lims_sign.f_id>" }
+     * 椤圭洰鍏ㄥ眬 JacksonConfig 宸查厤 SnakeCaseStrategy锛屽墠绔� baogao_riqi / beizhu / biz_sign
+     * 绛� snake_case 瀛楁浼氳嚜鍔ㄥ弽搴忓垪鍖栧埌椹煎嘲鍛藉悕灞炴�с��
+     *
+     * <p>鎺堟潈锛氬綋鍓嶇敤鎴烽』鍦� lims_baogao_shouquan 涓嫢鏈�
+     * shenhe_jibie 鍖呭惈 baogao_zhizhuo 涓� shenhe_fenlei 鍖呭惈鎶ュ憡 biz_type 鐨勬巿鏉冭褰曘��
+     * 锛坰henhe_jibie 鑷� 2026-05-21 璧蜂负澶氶�� JSON 鏁扮粍锛屼竴鏉℃巿鏉冨彲鍚屾椂瑕嗙洊澶氫釜绾у埆锛�
+     *
+     * <p>鐢靛瓙绛惧悕锛氫笌 audit_pass / approve / reject 鍚屾ā寮忋��
+     * 璋冪敤鏂瑰厛璧� /lims/biz/sign/verify 鎷垮埌 sign_id锛坥p_type=baogao_submit, data_id=鎶ュ憡 id锛夛紝
+     * 鍐嶅甫鍏ユ澶勩�傛湰鎺ュ彛浼氭牎楠� sign 鐪熷疄鎬� / op_type / data_id / 绛惧悕浜猴紝鍏ㄨ繃鎵嶆斁琛屻��
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Operation(summary = "鐢熸垚鎶ュ憡")
+    @PostMapping("/submit")
+    public R<String> submit(@RequestBody LimsBaogaoSubmitParam param) {
+        if (param == null || param.getId() == null || param.getId().isEmpty()) {
+            return R.error("缂哄皯鎶ュ憡 id");
+        }
+        if (param.getBaogaoRiqi() == null) {
+            return R.error("璇烽�夋嫨鎶ュ憡鏃ユ湡");
+        }
+        if (param.getBizSign() == null || param.getBizSign().isEmpty()) {
+            return R.error("璇锋坊鍔犵數瀛愮鍚�");
+        }
+
+        LimsQingyandanEntity report = limsQingyandanService.getById(param.getId());
+        if (report == null
+                || (report.getDeleteMark() != null && report.getDeleteMark() == 1)) {
+            return R.error("鎶ュ憡涓嶅瓨鍦ㄦ垨宸插垹闄�");
+        }
+        R<String> authErr = checkAuthority(report, BaogaoShenheJibie.ZHIZHUO);
+        if (authErr != null) {
+            return authErr;
+        }
+        LimsSignEntity sign;
+        try {
+            sign = limsSignService.requireValidSign(
+                    param.getBizSign(), SignOpType.BAOGAO_SUBMIT, param.getId());
+        } catch (DataException e) {
+            return R.error(e.getMessage());
+        }
+
+        boolean ok = limsQingyandanReportService.submitReport(param);
+        if (!ok) {
+            return R.error("褰撳墠鎶ュ憡涓嶅湪寰呯敓鎴愮姸鎬侊紝鏃犳硶閲嶅鐢熸垚");
+        }
+        recordReportSignUsage(sign, SignOpType.BAOGAO_SUBMIT, report);
+        return R.success("宸茬敓鎴愭姤鍛�");
+    }
+
+    /**
+     * 鎶ュ憡鎿嶄綔锛堟寜 action 鍒嗗彂锛夛細
+     * <ul>
+     *   <li>action=reject锛氶��鍥� / 涓嶉�氳繃锛坈reated / pending_audit / audited 閮藉彲閫�锛�
+     *       鎶ュ憡杞垹锛沯ianyan_xiang 鐢� created 閫�鍥炴椂鏀� audit_rejected锛�
+     *       鐢� pending_audit / audited 閫�鍥炴椂鏀� pending_audit锛�</li>
+     *   <li>action=audit_pass锛氬鏍搁�氳繃锛坧ending_audit 鈫� audited锛屽啓 shenhe_ren / shenhe_riqi锛�</li>
+     *   <li>action=approve锛氭壒鍑嗭紙audited 鈫� approved锛屽啓 pizhun_ren / pizhun_riqi锛�</li>
+     * </ul>
+     * 璇锋眰浣擄細{ "action": "...", "id": "<reportId>", "biz_sign": "<lims_sign.f_id>",
+     *          "tuihuiyuanyin": "..."锛堜粎 reject 蹇呭~锛� }
+     *
+     * <p>biz_sign 鑷� wave 2 璧峰惈涔夊彉鏇达細浠庡墠绔浘褰㈢鍚� base64 鏀逛负 lims_sign 涓婚敭銆�
+     * 璋冪敤鏂瑰繀椤诲厛璧� /lims/biz/sign/verify 鎷垮埌 sign_id锛坥p_type 涓哄搴斿姩浣� / data_id=reportId锛夛紝
+     * 鍐嶅甫鍏ユ澶勩�傛湰鎺ュ彛浼氭牎楠� sign 鐪熷疄鎬� / op_type / data_id / 绛惧悕浜猴紝鍏ㄨ繃鎵嶆斁琛屻��
+     *
+     * <p>鎺堟潈锛氭瘡涓姩浣滃潎闇�瀵瑰簲 shenhe_jibie 鐨勬巿鏉冭褰曡鐩栨姤鍛� biz_type锛�
+     * audit_pass=baogao_shenhe / approve=baogao_qianfa /
+     * reject 鎸夊綋鍓嶇姸鎬佹槧灏勶紙created鈫抸hizhuo / pending_audit鈫抯henhe / audited鈫抭ianfa锛夈��
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Operation(summary = "鎶ュ憡鎿嶄綔锛堥��鍥為」鐩瓑锛�")
+    @PostMapping("/action")
+    public R<String> baogaoAction(@RequestBody Map<String, String> params) {
+        String action = params.get("action");
+        String id = params.get("id");
+        String tuihuiyuanyin = params.get("tuihuiyuanyin");
+        String bizSign = params.get("biz_sign");
+
+        if (action == null || action.isEmpty()) {
+            return R.error("缂哄皯 action");
+        }
+        if (id == null || id.isEmpty()) {
+            return R.error("缂哄皯鎶ュ憡 id");
+        }
+
+        LimsQingyandanEntity report = limsQingyandanService.getById(id);
+        if (report == null
+                || (report.getDeleteMark() != null && report.getDeleteMark() == 1)) {
+            return R.error("鎶ュ憡涓嶅瓨鍦ㄦ垨宸插垹闄�");
+        }
+
+        switch (action) {
+            case "reject":
+                if (tuihuiyuanyin == null || tuihuiyuanyin.isEmpty()) {
+                    return R.error("璇峰~鍐欓��鍥炲師鍥�");
+                }
+                if (bizSign == null || bizSign.isEmpty()) {
+                    return R.error("璇锋坊鍔犵數瀛愮鍚�");
+                }
+                BaogaoStatus currentStatus = parseBaogaoStatus(report.getBaogaoZhuangtai());
+                BaogaoShenheJibie rejectJibie = BaogaoShenheJibie.forReject(currentStatus);
+                if (rejectJibie == null) {
+                    return R.error("褰撳墠鎶ュ憡鐘舵�佷笉鍏佽閫�鍥�");
+                }
+                R<String> rejectAuthErr = checkAuthority(report, rejectJibie);
+                if (rejectAuthErr != null) {
+                    return rejectAuthErr;
+                }
+                LimsSignEntity rejectSign;
+                try {
+                    rejectSign = limsSignService.requireValidSign(
+                            bizSign, SignOpType.BAOGAO_REJECT, id);
+                } catch (DataException e) {
+                    return R.error(e.getMessage());
+                }
+                boolean ok = limsQingyandanReportService.rejectReport(id, tuihuiyuanyin);
+                if (!ok) {
+                    return R.error("褰撳墠鎶ュ憡鐘舵�佷笉鍏佽閫�鍥�");
+                }
+                recordReportSignUsage(rejectSign, SignOpType.BAOGAO_REJECT, report);
+                return R.success("宸查��鍥�");
+
+            case "audit_pass":
+                if (bizSign == null || bizSign.isEmpty()) {
+                    return R.error("璇锋坊鍔犵數瀛愮鍚�");
+                }
+                R<String> auditAuthErr = checkAuthority(report, BaogaoShenheJibie.SHENHE);
+                if (auditAuthErr != null) {
+                    return auditAuthErr;
+                }
+                LimsSignEntity auditSign;
+                try {
+                    auditSign = limsSignService.requireValidSign(
+                            bizSign, SignOpType.BAOGAO_AUDIT_PASS, id);
+                } catch (DataException e) {
+                    return R.error(e.getMessage());
+                }
+                if (!limsQingyandanReportService.auditPassReport(id)) {
+                    return R.error("褰撳墠鎶ュ憡涓嶅湪寰呭鏍哥姸鎬侊紝鏃犳硶瀹℃牳閫氳繃");
+                }
+                recordReportSignUsage(auditSign, SignOpType.BAOGAO_AUDIT_PASS, report);
+                return R.success("宸插鏍搁�氳繃");
+
+            case "approve":
+                if (bizSign == null || bizSign.isEmpty()) {
+                    return R.error("璇锋坊鍔犵數瀛愮鍚�");
+                }
+                R<String> approveAuthErr = checkAuthority(report, BaogaoShenheJibie.QIANFA);
+                if (approveAuthErr != null) {
+                    return approveAuthErr;
+                }
+                LimsSignEntity approveSign;
+                try {
+                    approveSign = limsSignService.requireValidSign(
+                            bizSign, SignOpType.BAOGAO_APPROVE, id);
+                } catch (DataException e) {
+                    return R.error(e.getMessage());
+                }
+                if (!limsQingyandanReportService.approveReport(id)) {
+                    return R.error("褰撳墠鎶ュ憡涓嶅湪宸插鏍哥姸鎬侊紝鏃犳硶鎵瑰噯");
+                }
+                recordReportSignUsage(approveSign, SignOpType.BAOGAO_APPROVE, report);
+                return R.success("宸叉壒鍑�");
+
+            default:
+                return R.error("涓嶆敮鎸佺殑鎿嶄綔: " + action);
+        }
+    }
+
+    /**
+     * 褰撳墠鐢ㄦ埛瀵硅鎶ュ憡鐨� jibie 鏄惁鏈夋巿鏉冿紱鏃犳巿鏉冭繑鍥為敊璇� R锛屽凡鎺堟潈杩斿洖 null銆�
+     * 鎶ュ憡 biz_type 涓虹┖ 鈫� 涓�寰嬫嫆缁濓紙涓嶅厑璁�"鏃犲垎绫�"鎶ュ憡缁曡繃鎺堟潈锛夈��
+     */
+    private R<String> checkAuthority(LimsQingyandanEntity report, BaogaoShenheJibie jibie) {
+        String bizType = report.getBizType();
+        if (bizType == null || bizType.isEmpty()) {
+            return R.error("鎶ュ憡缂哄皯鍒嗙被(biz_type)锛屾棤娉曞垽瀹氭巿鏉�");
+        }
+        UserInfo userInfo = UserProvider.getUser();
+        String userId = userInfo == null ? null : userInfo.getUserId();
+        if (userId == null || userId.isEmpty()) {
+            return R.error("鏃犳硶鑾峰彇褰撳墠鐢ㄦ埛");
+        }
+        if (!limsBaogaoShouquanService.hasAuthority(userId, jibie, bizType)) {
+            return R.error("鏃犳潈鎿嶄綔锛氬綋鍓嶇敤鎴锋湭琚巿浜堣鍒嗙被鐨�"
+                    + jibieLabel(jibie) + "鏉冮檺");
+        }
+        return null;
+    }
+
+    private void recordReportSignUsage(LimsSignEntity sign,
+                                       SignOpType opType,
+                                       LimsQingyandanEntity report) {
+        LimsSignUsageTarget target = LimsSignUsageTarget.builder()
+                .targetTable("lims_qingyandan")
+                .targetId(report.getId())
+                .bizType("BAOGAO")
+                .bizModule("LIMS-璇烽獙鍗�")
+                .bizCode(report.getJianyanLiushuihao())
+                .build();
+        limsSignService.recordSignUsage(sign, opType,
+                Collections.singletonList(report.getId()), Collections.singletonList(target));
+    }
+
+    private String jibieLabel(BaogaoShenheJibie jibie) {
+        switch (jibie) {
+            case ZHIZHUO: return "鎶ュ憡鍒朵綔";
+            case SHENHE:  return "鎶ュ憡瀹℃牳";
+            case QIANFA:  return "鎶ュ憡绛惧彂";
+            default:      return jibie.getCode();
+        }
+    }
+
+    /** 鐘舵�佸瓧绗︿覆 鈫� 鏋氫妇锛涙湭鐭ュ�艰繑鍥� null锛坮eject 璺緞鎹鎻愮ず鐘舵�佷笉鍏佽锛夈�� */
+    private BaogaoStatus parseBaogaoStatus(String code) {
+        if (code == null) {
+            return null;
+        }
+        try {
+            return BaogaoStatus.fromCode(code);
+        } catch (IllegalArgumentException ex) {
+            return null;
+        }
+    }
+}

--
Gitblit v1.8.0