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-biz/src/main/java/jnpf/limsService/support/LimsSignUsageSupport.java |  217 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 217 insertions(+), 0 deletions(-)

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSignUsageSupport.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSignUsageSupport.java
new file mode 100644
index 0000000..d8eb55a
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/support/LimsSignUsageSupport.java
@@ -0,0 +1,217 @@
+package jnpf.limsService.support;
+
+import jnpf.limsEntity.LimsSignEntity;
+import jnpf.limsEntity.LimsSignUsageTarget;
+import jnpf.limsEntity.SignOpType;
+
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TimeZone;
+
+/** 鐢靛瓙绛惧悕浣跨敤浜嬩欢鐨勮鑼冨寲銆佸垎缁勫拰骞傜瓑閿敓鎴愩�� */
+public final class LimsSignUsageSupport {
+
+    public static final int SCHEMA_VERSION = 1;
+    public static final String ACTION_SIGN_USED = "SIGN_USED";
+
+    private LimsSignUsageSupport() {
+    }
+
+    /**
+     * 娓呯悊绌虹洰鏍囧苟鎸� table + id 鍘婚噸锛屼繚鎸佷笟鍔℃墽琛岄『搴忋��
+     */
+    public static List<LimsSignUsageTarget> normalizeTargets(Collection<LimsSignUsageTarget> targets) {
+        if (targets == null || targets.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<LimsSignUsageTarget> normalized = new ArrayList<>();
+        Set<String> seen = new LinkedHashSet<>();
+        for (LimsSignUsageTarget target : targets) {
+            if (target == null || isBlank(target.getTargetTable()) || isBlank(target.getTargetId())
+                    || isBlank(target.getBizType()) || isBlank(target.getBizModule())
+                    || isBlank(target.getBizCode())) {
+                continue;
+            }
+            String key = target.getTargetTable().trim().toLowerCase(Locale.ROOT)
+                    + "\n" + target.getTargetId().trim();
+            if (!seen.add(key)) {
+                continue;
+            }
+            normalized.add(LimsSignUsageTarget.builder()
+                    .targetTable(target.getTargetTable().trim().toLowerCase(Locale.ROOT))
+                    .targetId(target.getTargetId().trim())
+                    .bizType(target.getBizType().trim())
+                    .bizModule(target.getBizModule().trim())
+                    .bizCode(target.getBizCode().trim())
+                    .build());
+        }
+        return normalized;
+    }
+
+    /** 姣忎釜鍞竴 bizCode 褰㈡垚涓�涓椂闂寸嚎浜嬩欢锛涚粍鍐呬繚鐣欏疄闄呯洰鏍囥�� */
+    public static List<UsageGroup> groupByBizCode(Collection<LimsSignUsageTarget> targets) {
+        Map<String, UsageGroup> groups = new LinkedHashMap<>();
+        for (LimsSignUsageTarget target : normalizeTargets(targets)) {
+            UsageGroup group = groups.get(target.getBizCode());
+            if (group == null) {
+                group = new UsageGroup(target.getBizType(), target.getBizModule(), target.getBizCode());
+                groups.put(target.getBizCode(), group);
+            }
+            group.targets.add(target);
+        }
+        return new ArrayList<>(groups.values());
+    }
+
+    /**
+     * SIGN_USED 鐨勭‘瀹氭�у箓绛夐敭銆傜洰鏍囬『搴忎笌閲嶅椤逛笉浼氭敼鍙樼粨鏋滐紝闀垮害鍥哄畾涓� 67锛屽皬浜� DB varchar(70)銆�
+     */
+    public static String clientEventId(String signId, SignOpType opType, UsageGroup group) {
+        List<String> targetKeys = new ArrayList<>();
+        for (LimsSignUsageTarget target : group.targets) {
+            targetKeys.add(target.getTargetTable() + ":" + target.getTargetId());
+        }
+        targetKeys.sort(Comparator.naturalOrder());
+        String source = value(signId) + "\n" + (opType == null ? "" : opType.getCode())
+                + "\n" + value(group.bizType) + "\n" + value(group.bizModule)
+                + "\n" + value(group.bizCode)
+                + "\n" + String.join("\n", targetKeys);
+        return "SU-" + sha256(source);
+    }
+
+    /** lims_sign.extra_json锛氳姹傜洰鏍囦粎浣滆姹傛憳瑕侊紝affectedTargets 鎵嶆槸鎵ц浜嬪疄銆� */
+    public static Map<String, Object> signExtra(Collection<String> requestedTargetIds,
+                                                Collection<LimsSignUsageTarget> actualTargets) {
+        List<LimsSignUsageTarget> targets = normalizeTargets(actualTargets);
+        targets.sort(Comparator.comparing(LimsSignUsageTarget::getTargetTable)
+                .thenComparing(LimsSignUsageTarget::getTargetId)
+                .thenComparing(LimsSignUsageTarget::getBizType)
+                .thenComparing(LimsSignUsageTarget::getBizModule)
+                .thenComparing(LimsSignUsageTarget::getBizCode));
+        Map<String, Object> extra = new LinkedHashMap<>();
+        extra.put("schemaVersion", SCHEMA_VERSION);
+        extra.put("requestedTargets", normalizeIds(requestedTargetIds));
+        List<Map<String, Object>> affectedTargets = new ArrayList<>();
+        for (LimsSignUsageTarget target : targets) {
+            Map<String, Object> item = new LinkedHashMap<>();
+            item.put("targetTable", target.getTargetTable());
+            item.put("targetId", target.getTargetId());
+            item.put("bizType", target.getBizType());
+            item.put("bizModule", target.getBizModule());
+            item.put("bizCode", target.getBizCode());
+            affectedTargets.add(item);
+        }
+        extra.put("affectedTargets", affectedTargets);
+        extra.put("affected", targets.size());
+        return extra;
+    }
+
+    /** 鍗曚釜 SIGN_USED 浜嬩欢鐨� extra锛屼粎鍖呭惈璇� bizCode 涓嬬殑瀹為檯鎴愬姛鐩爣銆� */
+    public static Map<String, Object> eventExtra(LimsSignEntity sign, SignOpType opType, UsageGroup group) {
+        Map<String, Object> extra = new LinkedHashMap<>();
+        extra.put("schemaVersion", SCHEMA_VERSION);
+        extra.put("opType", opType == null ? null : opType.getCode());
+        extra.put("bizModule", group.getBizModule());
+        extra.put("signerUserId", sign == null ? null : sign.getCreatorUserId());
+        extra.put("signerAccount", sign == null ? null : sign.getAccountName());
+        extra.put("signedAt", sign == null ? null : formatDate(sign.getCreatorTime()));
+
+        Set<String> tables = new LinkedHashSet<>();
+        List<String> ids = new ArrayList<>();
+        for (LimsSignUsageTarget target : group.targets) {
+            tables.add(target.getTargetTable());
+            ids.add(target.getTargetId());
+        }
+        extra.put("businessTargetTable", tables.size() == 1 ? tables.iterator().next() : null);
+        extra.put("affectedTargetIds", ids);
+        extra.put("affected", ids.size());
+        return extra;
+    }
+
+    private static List<String> normalizeIds(Collection<String> ids) {
+        if (ids == null || ids.isEmpty()) {
+            return Collections.emptyList();
+        }
+        Set<String> normalized = new LinkedHashSet<>();
+        for (String id : ids) {
+            if (!isBlank(id)) {
+                normalized.add(id.trim());
+            }
+        }
+        List<String> result = new ArrayList<>(normalized);
+        result.sort(Comparator.naturalOrder());
+        return result;
+    }
+
+    private static String formatDate(Date date) {
+        if (date == null) {
+            return null;
+        }
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.ROOT);
+        format.setTimeZone(TimeZone.getDefault());
+        return format.format(date);
+    }
+
+    private static String sha256(String value) {
+        try {
+            byte[] digest = MessageDigest.getInstance("SHA-256").digest(value.getBytes(StandardCharsets.UTF_8));
+            StringBuilder out = new StringBuilder(digest.length * 2);
+            for (byte b : digest) {
+                out.append(String.format(Locale.ROOT, "%02x", b & 0xff));
+            }
+            return out.toString();
+        } catch (NoSuchAlgorithmException e) {
+            throw new IllegalStateException("JDK 缂哄皯 SHA-256", e);
+        }
+    }
+
+    private static String value(String value) {
+        return value == null ? "" : value;
+    }
+
+    private static boolean isBlank(String value) {
+        return value == null || value.trim().isEmpty();
+    }
+
+    /** 涓�涓鑼冧笟鍔$紪鍙峰搴旂殑绛惧悕浣跨敤浜嬩欢鍒嗙粍銆� */
+    public static final class UsageGroup {
+        private final String bizType;
+        private final String bizModule;
+        private final String bizCode;
+        private final List<LimsSignUsageTarget> targets = new ArrayList<>();
+
+        private UsageGroup(String bizType, String bizModule, String bizCode) {
+            this.bizType = bizType;
+            this.bizModule = bizModule;
+            this.bizCode = bizCode;
+        }
+
+        public String getBizType() {
+            return bizType;
+        }
+
+        public String getBizCode() {
+            return bizCode;
+        }
+
+        public String getBizModule() {
+            return bizModule;
+        }
+
+        public List<LimsSignUsageTarget> getTargets() {
+            return Collections.unmodifiableList(targets);
+        }
+    }
+}

--
Gitblit v1.8.0