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