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

diff --git a/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/print/JianyanBaogaoRenderer.java b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/print/JianyanBaogaoRenderer.java
new file mode 100644
index 0000000..c522b16
--- /dev/null
+++ b/jnpf-lims/jnpf-lims-biz/src/main/java/jnpf/limsService/print/JianyanBaogaoRenderer.java
@@ -0,0 +1,296 @@
+package jnpf.limsService.print;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import jnpf.exception.DataException;
+import jnpf.limsEntity.JianyanXiangmuGuanliEntity;
+import jnpf.limsEntity.LimsJianyanXiangEntity;
+import jnpf.limsEntity.LimsPrintTemplateEntity;
+import jnpf.limsEntity.LimsQingyandanEntity;
+import jnpf.limsMapper.JianyanXiangmuGuanliMapper;
+import jnpf.limsService.LimsJianyanXiangService;
+import jnpf.limsService.LimsQingyandanService;
+import jnpf.permission.UserApi;
+import jnpf.permission.entity.UserEntity;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ * "鎴愬搧璐ㄩ噺鎶ュ憡"妯℃澘娓叉煋鍣紙template_id = jianyan_baogao锛夈��
+ *
+ * <p>杈撳叆锛歭ims_qingyandan.f_id锛涙姤鍛婃暟鎹笌琛ㄥご閮藉湪杩欏紶琛屼笂锛坆aogao_* 鍒楋級銆�
+ * 杈撳嚭鍖呭惈琛ㄥご銆佹娴嬮」鍒嗙粍锛堟寜 fenxi_leixing 鑱氬悎 lims_jianyan_xiang锛夈��
+ * 绛惧悕鍖猴紙鎶ュ憡浜�/澶嶆牳浜�/QC 缁忕悊锛夈�佷緷鎹� / 缁撹 / 澶囨敞锛堟潵鑷ā鏉� config锛夈��
+ */
+@Component
+public class JianyanBaogaoRenderer implements PrintTemplateRenderer {
+
+    public static final String TEMPLATE_ID = "jianyan_baogao";
+
+    @Autowired
+    private LimsQingyandanService qingyandanService;
+
+    @Autowired
+    private LimsJianyanXiangService jianyanXiangService;
+
+    @Autowired
+    private JianyanXiangmuGuanliMapper xiangmuGuanliMapper;
+
+    @Autowired
+    private UserApi usersApi;
+
+    @Autowired
+    private ObjectMapper objectMapper;
+
+    @Override
+    public String getTemplateId() {
+        return TEMPLATE_ID;
+    }
+
+    @Override
+    public Map<String, Object> render(String qingyandanId, LimsPrintTemplateEntity tpl) {
+        if (qingyandanId == null || qingyandanId.isEmpty()) {
+            throw new DataException("缂哄皯璇烽獙鍗� id");
+        }
+
+        // 1. 璇烽獙鍗曪紙鎶ュ憡鏁版嵁 + 琛ㄥご閮藉湪杩欎竴琛屼笂锛屽繀椤诲瓨鍦� + 鏈蒋鍒狅級
+        LimsQingyandanEntity qingyandan = qingyandanService.getById(qingyandanId);
+        if (qingyandan == null
+                || (qingyandan.getDeleteMark() != null && qingyandan.getDeleteMark() == 1)) {
+            throw new DataException("璇烽獙鍗曚笉瀛樺湪鎴栧凡鍒犻櫎: " + qingyandanId);
+        }
+
+        // 2. 璇ユ祦姘村彿涓嬫湭杞垹鐨勬墍鏈� jianyan_xiang
+        QueryWrapper<LimsJianyanXiangEntity> wrapper = new QueryWrapper<>();
+        wrapper.eq("jianyan_liushuihao", qingyandan.getJianyanLiushuihao());
+        wrapper.and(w -> w.isNull("f_delete_mark").or().ne("f_delete_mark", 1));
+        wrapper.orderByAsc("f_id");
+        List<LimsJianyanXiangEntity> rows = jianyanXiangService.list(wrapper);
+
+        // 3.1 鎺掗櫎 lims_jianyan_xiang.shifou_chuju_baogao = 'no' 鐨勬楠岄」銆�
+        // 浠呮樉寮� "no" 鎵嶄細琚墧闄わ紱NULL / 鍏跺畠鍊兼寜"鍑烘姤鍛�"澶勭悊锛堜繚瀹堜笉璇激锛夈��
+        // 璇ュ瓧娈靛湪 buildJianyanXiang / buildWaijianJianyanXiang 闃舵灏变粠 jiancexiang 蹇収杩囨潵锛�
+        // 娓叉煋鏃朵笉鍐嶅洖鏌� lims_qingyandan_jiancexiang銆�
+        rows = rows.stream()
+                .filter(r -> !"no".equalsIgnoreCase(r.getShifouChujuBaogao()))
+                .collect(Collectors.toList());
+
+        // 4. 瑙f瀽妯℃澘 config
+        Map<String, Object> cfg = parseConfig(tpl);
+        Map<String, Object> labels = asMap(cfg.get("labels"));
+
+        // 5. 鎷艰杩斿洖缁撴瀯锛堜繚鎸� LinkedHashMap 椤哄簭渚夸簬闃呰锛�
+        // 椤跺眰 companyName / docNumber / reportBookNumber 鍛藉悕涓庡墠绔ā鏉�
+        // jianyan_baogao.html 鍐� templateData.* 鐨勮鍙栭敭涓�鑷达紝閬垮厤鍓嶇鍐嶅仛鏄犲皠銆�
+        //
+        // docNumber 鐨勭湡瀹炰笟鍔¤涔夋槸"浜у搧璐ㄩ噺鏍囧噯鏂囨。缂栧彿 + 鐗堟"锛堝
+        // STP-QAD-Z003-MP-R01-06锛夛紝搴旇窡闅忎骇鍝�/浜у搧鏍囧噯缁存姢銆傚綋鍓嶄笟鍔″皻鏈�
+        // 鍦ㄤ富鏁版嵁閲屽缓绔嬭瀛楁锛�**涓存椂**鐢� jianyan_liushuihao锛堟瘡娆¤楠屽敮涓�锛�
+        // 鍗犱綅锛岄伩鍏嶉〉澶寸┖鐧姐�傜瓑 lims_quality_manager 绛変富鏁版嵁琛ラ綈 doc_number
+        // 鍚庡垏鍥� cfg.doc_number 鎴栨柊婧愬嵆鍙紙cfg.doc_number 瀛楁淇濈暀涓嶅垹锛夈��
+        Map<String, Object> data = new LinkedHashMap<>();
+        data.put("template_id", tpl.getTemplateId());
+        data.put("template_name", tpl.getTemplateName());
+        data.put("companyName", asString(cfg.get("company_name")));
+        data.put("docNumber", asString(qingyandan.getJianyanLiushuihao()));
+        data.put("reportBookNumber", asString(qingyandan.getBaogaoBianhao()));
+        data.put("yiju", asString(cfg.get("yiju")));
+        data.put("header", buildHeader(qingyandan));
+        data.put("groups", buildGroups(rows));
+        data.put("jielun", asString(cfg.get("jielun")));
+        data.put("signatures", buildSignatures(qingyandan, labels));
+        data.put("beizhu", asString(cfg.get("beizhu")));
+        return data;
+    }
+
+    private Map<String, Object> buildHeader(LimsQingyandanEntity q) {
+        Map<String, Object> h = new LinkedHashMap<>();
+        h.put("chanpin_mingcheng", q.getChanpinMingcheng());
+        h.put("pihao", q.getPihao());
+        h.put("laiyuan_bianma", q.getLaiyuanBianma());
+        h.put("chanpin_bianma", q.getChanpinBianma());
+        h.put("guige", q.getGuige());
+        h.put("shengchan_riqi", q.getShengchanRiqi());
+        h.put("piliang", q.getPiliang());
+        h.put("piliang_danwei", q.getPiliangDanwei());
+        h.put("youxiaoqi", q.getYouxiaoqi());
+        return h;
+    }
+
+    /**
+     * 鎸�"璐ㄩ噺灞炴�т腑鏂囧悕"鍒嗙粍锛岀粍鍐呮寜 jianyan_xiang 鑷劧椤哄簭锛坒_id 鍗囧簭锛夈��
+     *
+     * <p>"璐ㄩ噺灞炴��"鍙栬嚜妫�楠岄」鐩富鏁版嵁 jianyan_xiangmu_guanli锛�
+     * <ol>
+     *   <li>鐢� lims_jianyan_xiang.jianyan_xiangmu_bianma 鍙嶆煡涓绘暟鎹紝寰楀埌 self锛�</li>
+     *   <li>self.fenxi_leixing 涓虹┖ 鈫� 璐ㄩ噺灞炴�� = self.mingcheng锛堣嚜韬棦鏄川閲忓睘鎬у張鏄楠岄」鐩級锛�</li>
+     *   <li>self.fenxi_leixing 涓嶇┖ 鈫� 鐢ㄥ叾鍊煎啀娆″弽鏌ヤ富鏁版嵁 bianma 寰楀埌 parent锛�
+     *       璐ㄩ噺灞炴�� = parent.mingcheng銆�</li>
+     * </ol>
+     *
+     * <p>涓绘暟鎹己澶辩殑瀹归敊锛歴elf 缂� 鈫� 鐢� lims_jianyan_xiang.jianyan_xiangmu_mingcheng 鍏滃簳锛�
+     * parent 缂� 鈫� 鐢� self.mingcheng 鍏滃簳锛涢兘娌℃湁灏辩┖瀛楃涓层��
+     *
+     * <p>涓嬪彂瀛楁 key 浠嶅彨 "fenxi_leixing"锛屼笌鍘嗗彶鍓嶇妯℃澘鍏煎锛涘叾鍊肩殑璇箟宸插彉鎴�
+     * "璐ㄩ噺灞炴�т腑鏂囧悕"锛堜笉鍐嶆槸鍒嗙被缂栫爜锛夈��
+     */
+    private List<Map<String, Object>> buildGroups(List<LimsJianyanXiangEntity> rows) {
+        if (rows.isEmpty()) {
+            return new ArrayList<>();
+        }
+
+        // 1. 涓�娆℃�ф妸杩欐壒 jianyan_xiang 娑夊強鍒扮殑"鑷韩 bianma"鐨勪富鏁版嵁鎹炲嚭鏉�
+        Set<String> selfBianmas = rows.stream()
+                .map(LimsJianyanXiangEntity::getJianyanXiangmuBianma)
+                .filter(s -> s != null && !s.isEmpty())
+                .collect(Collectors.toSet());
+        Map<String, JianyanXiangmuGuanliEntity> selfMap =
+                fetchXiangmuByBianmas(selfBianmas);
+
+        // 2. 鍦� self 闆嗗悎閲屾壘闇�瑕佽繘涓�姝ュ弽鏌ョ殑 parent bianma
+        Set<String> parentBianmas = selfMap.values().stream()
+                .map(JianyanXiangmuGuanliEntity::getFenxiLeixing)
+                .filter(s -> s != null && !s.isEmpty())
+                .collect(Collectors.toSet());
+        Map<String, JianyanXiangmuGuanliEntity> parentMap =
+                fetchXiangmuByBianmas(parentBianmas);
+
+        // 3. 鎸�"璐ㄩ噺灞炴�т腑鏂囧悕"鍒嗙粍锛屼繚鐣欏嚭鐜伴『搴�
+        Map<String, List<LimsJianyanXiangEntity>> grouped = new LinkedHashMap<>();
+        for (LimsJianyanXiangEntity r : rows) {
+            String key = resolveZhiliangShuxing(r, selfMap, parentMap);
+            grouped.computeIfAbsent(key, k -> new ArrayList<>()).add(r);
+        }
+
+        // 4. 鎷艰 groups
+        List<Map<String, Object>> groups = new ArrayList<>();
+        for (Map.Entry<String, List<LimsJianyanXiangEntity>> e : grouped.entrySet()) {
+            Map<String, Object> g = new LinkedHashMap<>();
+            g.put("fenxi_leixing", e.getKey());
+            List<Map<String, Object>> items = new ArrayList<>();
+            for (LimsJianyanXiangEntity r : e.getValue()) {
+                Map<String, Object> item = new LinkedHashMap<>();
+                item.put("jianyan_xiangmu_mingcheng", r.getJianyanXiangmuMingcheng());
+                item.put("biaozhun_guiding", r.getBiaozhunGuiding());
+                item.put("jianyan_jieguo", r.getJianyanJieguo());
+                items.add(item);
+            }
+            g.put("items", items);
+            groups.add(g);
+        }
+        return groups;
+    }
+
+    /** 鎵归噺鎸� bianma 鍙嶆煡涓绘暟鎹紙鑷姩杩囨护杞垹 + 澶氱鎴锋嫤鎴櫒鑷姩鍔� tenant 鏉′欢锛夈�� */
+    private Map<String, JianyanXiangmuGuanliEntity> fetchXiangmuByBianmas(Set<String> bianmas) {
+        if (bianmas == null || bianmas.isEmpty()) {
+            return new HashMap<>();
+        }
+        QueryWrapper<JianyanXiangmuGuanliEntity> qw = new QueryWrapper<>();
+        qw.in("bianma", bianmas);
+        qw.and(w -> w.isNull("f_delete_mark").or().ne("f_delete_mark", 1));
+        Map<String, JianyanXiangmuGuanliEntity> map = new HashMap<>();
+        for (JianyanXiangmuGuanliEntity e : xiangmuGuanliMapper.selectList(qw)) {
+            if (e != null && e.getBianma() != null) {
+                map.put(e.getBianma(), e);
+            }
+        }
+        return map;
+    }
+
+    private String resolveZhiliangShuxing(LimsJianyanXiangEntity r,
+                                          Map<String, JianyanXiangmuGuanliEntity> selfMap,
+                                          Map<String, JianyanXiangmuGuanliEntity> parentMap) {
+        JianyanXiangmuGuanliEntity self = selfMap.get(r.getJianyanXiangmuBianma());
+        if (self == null) {
+            return r.getJianyanXiangmuMingcheng() != null ? r.getJianyanXiangmuMingcheng() : "";
+        }
+        if (self.getFenxiLeixing() == null || self.getFenxiLeixing().isEmpty()) {
+            return self.getMingcheng() != null ? self.getMingcheng() : "";
+        }
+        JianyanXiangmuGuanliEntity parent = parentMap.get(self.getFenxiLeixing());
+        if (parent == null || parent.getMingcheng() == null) {
+            return self.getMingcheng() != null ? self.getMingcheng() : "";
+        }
+        return parent.getMingcheng();
+    }
+
+    private Map<String, Object> buildSignatures(LimsQingyandanEntity r, Map<String, Object> labels) {
+        // 涓変釜绛惧悕浜� id 涓�娆℃壒閲忔煡鍥炵湡瀹炲鍚�
+        List<String> userIds = Arrays.asList(r.getBaogaoRen(), r.getBaogaoShenheRen(), r.getBaogaoPizhunRen())
+                .stream()
+                .filter(s -> s != null && !s.isEmpty())
+                .distinct()
+                .collect(Collectors.toList());
+        Map<String, String> idToRealName = new HashMap<>();
+        if (!userIds.isEmpty()) {
+            List<UserEntity> users = usersApi.getUserName(userIds);
+            if (users != null) {
+                for (UserEntity u : users) {
+                    if (u != null && u.getId() != null) {
+                        idToRealName.put(u.getId(), u.getRealName());
+                    }
+                }
+            }
+        }
+
+        Map<String, Object> sig = new LinkedHashMap<>();
+        sig.put("baogaoren", makeSignatureBlock(asString(labels.get("baogaoren"), "鎶ュ憡浜�/鏃ユ湡"),
+                r.getBaogaoRen(), r.getBaogaoRiqi(), idToRealName));
+        sig.put("fuheren", makeSignatureBlock(asString(labels.get("fuheren"), "澶嶆牳浜�/鏃ユ湡"),
+                r.getBaogaoShenheRen(), r.getBaogaoShenheRiqi(), idToRealName));
+        sig.put("pizhunren", makeSignatureBlock(asString(labels.get("pizhunren"), "QC缁忕悊/鏃ユ湡"),
+                r.getBaogaoPizhunRen(), r.getBaogaoPizhunRiqi(), idToRealName));
+        return sig;
+    }
+
+    private Map<String, Object> makeSignatureBlock(String label, String userId, Date date,
+                                                   Map<String, String> idToRealName) {
+        Map<String, Object> m = new LinkedHashMap<>();
+        m.put("label", label);
+        m.put("user_id", userId);
+        m.put("name", userId != null ? idToRealName.get(userId) : null);
+        m.put("date", date);
+        return m;
+    }
+
+    private Map<String, Object> parseConfig(LimsPrintTemplateEntity tpl) {
+        String json = tpl.getConfig();
+        if (json == null || json.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        try {
+            return objectMapper.readValue(json, new TypeReference<Map<String, Object>>() {});
+        } catch (Exception e) {
+            throw new DataException("鎵撳嵃妯℃澘 config JSON 瑙f瀽澶辫触: " + e.getMessage());
+        }
+    }
+
+    private static String asString(Object v) {
+        return v == null ? "" : v.toString();
+    }
+
+    private static String asString(Object v, String fallback) {
+        return v == null ? fallback : v.toString();
+    }
+
+    @SuppressWarnings("unchecked")
+    private static Map<String, Object> asMap(Object v) {
+        if (v instanceof Map) {
+            return (Map<String, Object>) v;
+        }
+        return Collections.emptyMap();
+    }
+}

--
Gitblit v1.8.0