From abd339106ed3a6465293edccd33e5f87bd43bdde Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 20 十月 2026 18:01:42 +0800
Subject: [PATCH] feat: 质量监控/人员培训对齐程序文件,体系文件控制附件格式放开

---
 cnas-process/src/main/java/com/ruoyi/process/service/impl/QualitySuperviseServiceImpl.java |  424 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 files changed, 363 insertions(+), 61 deletions(-)

diff --git a/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualitySuperviseServiceImpl.java b/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualitySuperviseServiceImpl.java
index e4d082e..87f9a83 100644
--- a/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualitySuperviseServiceImpl.java
+++ b/cnas-process/src/main/java/com/ruoyi/process/service/impl/QualitySuperviseServiceImpl.java
@@ -20,7 +20,6 @@
 import com.ruoyi.common.constant.MenuJumpPathConstants;
 import com.ruoyi.common.core.domain.entity.InformationNotification;
 import com.ruoyi.common.core.domain.entity.User;
-import com.ruoyi.common.utils.DateImageUtil;
 import com.ruoyi.common.utils.QueryWrappers;
 import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.common.utils.WxCpUtils;
@@ -57,7 +56,11 @@
 import java.time.YearMonth;
 import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.Base64;
 import java.util.HashMap;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 
@@ -177,8 +180,8 @@
                         throw new ErrorException("鏈壘鍒拌鐩戠潱鍛橈細" + superviseDetails.getSupervisee());
                     }
                     superviseDetails.setSupervisedUserId(user.getId());
-                    // 鏍煎紡鍖栨椂闂�
-                    superviseDetails.setSuperviseTime(detailsUpload.getSuperviseTime());
+                    // 缁熶竴鏃ユ湡鏍煎紡锛岄伩鍏� Excel 鎶� 2026-03-15 瀛樻垚 2026/3/15 鍚庢棤娉曡В鏋�
+                    superviseDetails.setSuperviseTime(normalizeSuperviseTime(detailsUpload.getSuperviseTime()));
 
                     detailsUploadList.add(superviseDetails);
                 }
@@ -210,6 +213,92 @@
                 .set(QualitySupervise::getRatifyStatus, qualitySupervise.getRatifyStatus())
                 .set(QualitySupervise::getRatifyTime, LocalDateTime.now())
         );
+        return true;
+    }
+
+    /**
+     * 鐩戠潱璁″垝涓嬪彂
+     * @param qualitySupervise
+     * @return
+     */
+    @Override
+    public boolean issueQualitySupervise(QualitySupervise qualitySupervise) {
+        if (qualitySupervise.getSuperviseId() == null) {
+            throw new ErrorException("缂哄皯鐩戠潱璁″垝id");
+        }
+        QualitySupervise supervise = baseMapper.selectById(qualitySupervise.getSuperviseId());
+        if (supervise == null) {
+            throw new ErrorException("鏈壘鍒扮洃鐫h鍒�");
+        }
+        if (!Integer.valueOf(1).equals(supervise.getRatifyStatus())) {
+            throw new ErrorException("璁″垝鏈壒鍑嗭紝涓嶈兘涓嬪彂");
+        }
+        if (Integer.valueOf(1).equals(supervise.getIssueStatus())) {
+            throw new ErrorException("璁″垝宸蹭笅鍙戯紝涓嶈兘閲嶅涓嬪彂");
+        }
+
+        baseMapper.update(null, Wrappers.<QualitySupervise>lambdaUpdate()
+                .eq(QualitySupervise::getSuperviseId, supervise.getSuperviseId())
+                .set(QualitySupervise::getIssueStatus, 1)
+                .set(QualitySupervise::getIssueTime, LocalDateTime.now())
+        );
+
+        // 褰撳墠鐧诲綍鐢ㄦ埛
+        Integer userId = SecurityUtils.getUserId().intValue();
+        User user = userMapper.selectById(userId);
+
+        // 鎺ユ敹浜�: 璁″垝鐩戠潱鍛� + 鎵�鏈夋槑缁嗙殑琚洃鐫d汉(鍘婚噸)
+        LinkedHashSet<Integer> receiveUserIds = new LinkedHashSet<>();
+        if (StringUtils.isNotBlank(supervise.getRecordUserIds())) {
+            for (String idStr : supervise.getRecordUserIds().split(",")) {
+                if (StringUtils.isNotBlank(idStr)) {
+                    receiveUserIds.add(Integer.valueOf(idStr.trim()));
+                }
+            }
+        }
+        List<QualitySuperviseDetails> detailsList = qualitySuperviseDetailsMapper.selectList(Wrappers.<QualitySuperviseDetails>lambdaQuery()
+                .eq(QualitySuperviseDetails::getSuperviseId, supervise.getSuperviseId()));
+        for (QualitySuperviseDetails detail : detailsList) {
+            if (detail.getSupervisedUserId() != null) {
+                receiveUserIds.add(detail.getSupervisedUserId());
+            }
+        }
+
+        // 娑堟伅鍙戦��
+        for (Integer receiveUserId : receiveUserIds) {
+            InformationNotification info = new InformationNotification();
+            // 鍙戦�佷汉
+            info.setCreateUser(user.getName());
+            info.setMessageType("6");
+            info.setTheme("CNAS璐ㄩ噺鐩戠潱璁″垝涓嬪彂閫氱煡");
+            info.setContent(supervise.getSuperviseName() + " 璐ㄩ噺鐩戠潱璁″垝宸蹭笅鍙戯紝璇锋寜璁″垝寮�灞曡川閲忕洃鐫o紝骞跺強鏃跺~鍐欐棩甯哥洃鐫f鏌ヨ褰�");
+            info.setSenderId(userId);
+            // 鎺ユ敹浜�
+            info.setConsigneeId(receiveUserId);
+            info.setJumpPath(MenuJumpPathConstants.QUALITY_SUPERVISE);
+            informationNotificationService.addInformationNotification(info);
+        }
+
+        // 鍙戦�佷紒涓氬井淇¢�氱煡
+        threadPoolTaskExecutor.execute(() -> {
+            String message = "";
+            message += "CNAS璐ㄩ噺鐩戠潱璁″垝涓嬪彂閫氱煡";
+            message += "\n璇峰幓杩囩▼瑕佹眰-璐ㄩ噺鐩戠潱璁″垝";
+            message += "\n" + supervise.getSuperviseName() + " 璐ㄩ噺鐩戠潱璁″垝宸蹭笅鍙戯紝璇锋寜璁″垝寮�灞曡川閲忕洃鐫o紝骞跺強鏃跺~鍐欐棩甯哥洃鐫f鏌ヨ褰�";
+            for (Integer receiveUserId : receiveUserIds) {
+                User receiveUser = userMapper.selectById(receiveUserId);
+                if (receiveUser == null) {
+                    continue;
+                }
+                //鍙戦�佷紒涓氬井淇℃秷鎭�氱煡
+                try {
+                    WxCpUtils.inform(receiveUser.getAccount(), message, null);
+                } catch (Exception e) {
+                    throw new RuntimeException(e);
+                }
+            }
+        });
+
         return true;
     }
 
@@ -283,10 +372,8 @@
             for (String userIdStr : userIds) {
                 // 杞崲涓篿nt绫诲瀷
                 Integer userId = Integer.valueOf(userIdStr);
-                // 鑾峰彇浜哄憳绛惧悕瀵硅薄
-                PictureRenderData finalUserSignatureUrl = UserUtils.getFinalUserSignatureUrl(userId);
-                // 灏嗕汉鍛樼鍚嶅璞℃坊鍔犲埌闆嗗悎涓�
-                recordUserDataList.add(finalUserSignatureUrl);
+                // 鑾峰彇浜哄憳绛惧悕瀵硅薄(缂哄け鏃跺厹搴曢�忔槑鍥�, 閬垮厤瀵煎嚭澶辫触)
+                recordUserDataList.add(signatureOrTransparent(userId));
             }
         }
 
@@ -302,6 +389,8 @@
         int index = 1;
         for (QualitySuperviseDetails qualitySuperviseDetail : qualitySuperviseDetails) {
             qualitySuperviseDetail.setIndex(index);
+            qualitySuperviseDetail.setSuperviseFrequency(buildSuperviseFrequency(qualitySuperviseDetail.getSuperviseTime()));
+            qualitySuperviseDetail.setSuperviseTime(toMonthText(qualitySuperviseDetail.getSuperviseTime()));
             index++;
         }
 
@@ -315,14 +404,14 @@
                 new HashMap<String, Object>() {{
                     put("year", qualitySupervise.getSuperviseYear());
                     put("superviseDetailList", qualitySuperviseDetails);
-                    put("writeUrl", StringUtils.isNotBlank(writeUrl) ? Pictures.ofLocal(imgUrl + "/" + writeUrl).create() : null);
-                    put("ratifyUrl", StringUtils.isNotBlank(finalRatifyUrl) ? Pictures.ofLocal(imgUrl + "/" + finalRatifyUrl).create() : null);
-                    put("writeDateUrl", qualitySupervise.getWriteTime() != null ?
-                            Pictures.ofStream(DateImageUtil.createDateImage(qualitySupervise.getWriteTime())).create() : null);
-                    put("ratifyDateUrl", qualitySupervise.getRatifyTime() != null ?
-                            Pictures.ofStream(DateImageUtil.createDateImage(qualitySupervise.getRatifyTime())).create() : null);
-                    put("recordUserUrl1", recordUserDataList.get(0));
-                    put("recordUserUrl2", recordUserDataList.get(1));
+                    put("writeUrl", orTransparentPicture(StringUtils.isNotBlank(writeUrl) ? Pictures.ofLocal(imgUrl + "/" + writeUrl).create() : null));
+                    put("ratifyUrl", orTransparentPicture(StringUtils.isNotBlank(finalRatifyUrl) ? Pictures.ofLocal(imgUrl + "/" + finalRatifyUrl).create() : null));
+                    put("writeDateStr", qualitySupervise.getWriteTime() != null ?
+                            qualitySupervise.getWriteTime().format(SUPERVISE_DATE_FORMATTER) : "");
+                    put("ratifyDateStr", qualitySupervise.getRatifyTime() != null ?
+                            qualitySupervise.getRatifyTime().format(SUPERVISE_DATE_FORMATTER) : "");
+                    put("recordUserUrl1", orTransparentPicture(recordUserDataList.get(0)));
+                    put("recordUserUrl2", orTransparentPicture(recordUserDataList.get(1)));
                 }});
 
         try {
@@ -344,6 +433,102 @@
 
     }
 
+    /**
+     * 缁熶竴鐩戠潱鏃ユ湡鏍煎紡涓� yyyy.M锛�
+     * 2026-03-15 / 2026/3/15 / 2026.3.15 / 2026骞�3鏈�15鏃� -> 2026.3锛�
+     * 宸茬粡鏄� 2026.3 鎴� "3鏈堛��12鏈�" 绛夊啓娉曚繚鎸佸師鏍枫��
+     */
+    private static String normalizeSuperviseTime(String superviseTime) {
+        if (StringUtils.isBlank(superviseTime)) {
+            return superviseTime;
+        }
+        String text = superviseTime.trim();
+        Matcher matcher = Pattern.compile("^(\\d{4})\\s*[./骞�-]\\s*(\\d{1,2})(?:\\s*[./鏈�-]\\s*(\\d{1,2})\\s*鏃�?)?$")
+                .matcher(text);
+        if (matcher.matches()) {
+            int month = Integer.parseInt(matcher.group(2));
+            if (month >= 1 && month <= 12) {
+                return matcher.group(1) + "." + month;
+            }
+        }
+        return text;
+    }
+
+    /**
+     * 瑙f瀽鐩戠潱鏃ユ湡瀵瑰簲鐨勫勾鏈堬紝鍏煎鍘嗗彶鏁版嵁涓殑瀹屾暣鏃ユ湡鍐欐硶銆�
+     */
+    private static YearMonth parseSuperviseYearMonth(String superviseTime, String fallbackYear) {
+        if (StringUtils.isNotBlank(superviseTime)) {
+            String first = superviseTime.split("[銆�,锛�;锛沒+")[0].trim();
+            Matcher fullDate = Pattern.compile("(\\d{4})\\s*[./骞�-]\\s*(\\d{1,2})").matcher(first);
+            if (fullDate.find()) {
+                int month = Integer.parseInt(fullDate.group(2));
+                if (month >= 1 && month <= 12) {
+                    return YearMonth.of(Integer.parseInt(fullDate.group(1)), month);
+                }
+            }
+            // "3鏈堛��12鏈�" 杩欑被娌℃湁骞翠唤鐨勫啓娉曪紝浣跨敤璁″垝骞村害
+            Matcher monthOnly = Pattern.compile("^(\\d{1,2})").matcher(first);
+            if (monthOnly.find()) {
+                int month = Integer.parseInt(monthOnly.group(1));
+                if (month >= 1 && month <= 12) {
+                    int year = LocalDate.now().getYear();
+                    if (StringUtils.isNotBlank(fallbackYear)) {
+                        Matcher yearMatcher = Pattern.compile("(\\d{4})").matcher(fallbackYear);
+                        if (yearMatcher.find()) {
+                            year = Integer.parseInt(yearMatcher.group(1));
+                        }
+                    }
+                    return YearMonth.of(year, month);
+                }
+            }
+        }
+        return YearMonth.now();
+    }
+
+    /**
+     * 鐩戠潱棰戞: 鎸夌洃鐫f椂闂翠腑鐨勬湀浠戒釜鏁拌绠�, 濡� "2026.3" -> "1娆�/骞�", "3鏈堛��12鏈�" -> "2娆�/骞�"
+     */
+    private static String buildSuperviseFrequency(String superviseTime) {
+        if (StringUtils.isBlank(superviseTime)) {
+            return "";
+        }
+        int count = 0;
+        for (String part : normalizeSuperviseTime(superviseTime).split("[銆�,锛�;锛�/\\s]+")) {
+            if (part.matches(".*\\d.*")) {
+                count++;
+            }
+        }
+        return count > 0 ? count + "娆�/骞�" : "";
+    }
+
+    /**
+     * 鐩戠潱鏃堕棿: 琛ㄦ牸涓彧灞曠ず鏈堜唤, 濡� "2026.3" -> "3鏈�", "3鏈堛��12鏈�" -> "3鏈堛��12鏈�"
+     */
+    private static String toMonthText(String superviseTime) {
+        if (StringUtils.isBlank(superviseTime)) {
+            return "";
+        }
+        StringBuilder sb = new StringBuilder();
+        for (String part : superviseTime.split("[銆�,锛�;锛沒+")) {
+            String s = normalizeSuperviseTime(part.trim());
+            if (s.isEmpty()) {
+                continue;
+            }
+            int idx = s.lastIndexOf('.');
+            String month = (idx >= 0 ? s.substring(idx + 1) : s).replaceAll("\\D", "");
+            if (month.isEmpty() || month.length() > 2) {
+                return superviseTime;
+            }
+            month = month.replaceFirst("^0+(?=\\d)", "");
+            if (sb.length() > 0) {
+                sb.append("銆�");
+            }
+            sb.append(month).append("鏈�");
+        }
+        return sb.length() > 0 ? sb.toString() : superviseTime;
+    }
+
     /************************************************ 璁板綍 ******************************************************/
 
     /**
@@ -361,9 +546,11 @@
         if (detailsRecord == null) {
             // 鏌ヨ璇︽儏淇℃伅
             QualitySuperviseDetails qualitySuperviseDetails = qualitySuperviseDetailsMapper.selectById(superviseDetailsId);
+            QualitySupervise qualitySupervise = baseMapper.selectById(qualitySuperviseDetails.getSuperviseId());
             // 鏌ヨ涓婁竴涓湀绗竴鏉$殑璁板綍淇℃伅, 鍒ゆ柇琚洃鐫d汉鍘诲彟澶栦竴涓�
-            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.M");
-            YearMonth yearMonth = YearMonth.parse(qualitySuperviseDetails.getSuperviseTime(), formatter);
+            // 鐩戠潱鏃ユ湡鍏煎 2026.3 / 2026-03-15 / 2026/3/15 / 2026骞�3鏈�15鏃� 绛夊啓娉�
+            YearMonth yearMonth = parseSuperviseYearMonth(qualitySuperviseDetails.getSuperviseTime(),
+                    qualitySupervise == null ? null : qualitySupervise.getSuperviseYear());
             YearMonth previousYearMonth = yearMonth.minusMonths(1);
 
             LocalDate startDate = previousYearMonth.atDay(1);
@@ -385,21 +572,17 @@
 
             String supervisor = null;
 
-            if (laseRecord != null) {
-                // 鏌ヨ璁″垝鑾峰彇鍙︿竴涓洃鐫e憳
-                QualitySupervise qualitySupervise = baseMapper.selectById(qualitySuperviseDetails.getSuperviseId());
-                if (StringUtils.isNotBlank(qualitySupervise.getRecordUserIds())) {
-                    List<String> recordUserIds = StrUtil.split(qualitySupervise.getRecordUserIds(), ',');
-                    List<User> users = userMapper.selectList(Wrappers.<User>lambdaQuery()
-                            .in(User::getId, recordUserIds));
-                    if (CollectionUtils.isNotEmpty(users) && users.size() == 2) {
-                        // 鍒ゆ柇鏄惁涓�鏍�, 涓�鏍疯幏鍙栧彟澶栦竴涓�
-                        if (users.get(0).getName().equals(laseRecord.getSupervisor())) {
-                            supervisor = users.get(1).getName();
-                        } else {
-                            supervisor = users.get(0).getName();
-                        }
-
+            // 璁″垝鎸囧畾鐨勭洃鐫e憳锛氶娆″~鍐欐椂榛樿甯﹀嚭绗竴浣嶏紱鏈変笂鏈堣褰曟椂涓や綅鐩戠潱鍛樹氦鏇�
+            if (qualitySupervise != null && StringUtils.isNotBlank(qualitySupervise.getRecordUserIds())) {
+                List<String> recordUserIds = StrUtil.split(qualitySupervise.getRecordUserIds(), ',');
+                List<User> users = userMapper.selectList(Wrappers.<User>lambdaQuery()
+                        .in(User::getId, recordUserIds));
+                if (CollectionUtils.isNotEmpty(users)) {
+                    if (laseRecord != null && users.size() > 1
+                            && users.get(0).getName().equals(laseRecord.getSupervisor())) {
+                        supervisor = users.get(1).getName();
+                    } else {
+                        supervisor = users.get(0).getName();
                     }
                 }
             }
@@ -408,13 +591,9 @@
             detailsRecord.setSuperviseDetailsId(superviseDetailsId);
             detailsRecord.setTestMember(qualitySuperviseDetails.getSupervisee());
             detailsRecord.setSupervisor(supervisor);
-
-            detailsRecord.setPersonnel(SecurityUtils.getLoginUser().getUser().getNickName() + "鏈夌浉搴旀娴嬪憳鐨勪笂宀楄瘉");
-            detailsRecord.setEnvironment("娓╁害锛�()鈩� 婀垮害锛�()%");
-            detailsRecord.setInspectionRecord("妫�娴嬩汉鍛�" + SecurityUtils.getLoginUser().getUser().getNickName() + "杩涜妫�娴嬭褰曪紝璁板綍鍐呭鐪熷疄鏈夋晥");
-            detailsRecord.setExaminingReport("鐢�" + SecurityUtils.getLoginUser().getUser().getNickName() + "鍑虹ず鐨勬娴嬫姤鍛婄鍚堣鑼冭姹�");
-            detailsRecord.setSupervisionEvaluation("妫�娴嬫寜鐓ц姹傝繘琛岋紝鍒ゅ畾涓烘弧鎰�");
-            detailsRecord.setHandlingAdvice("/");
+            // 榛樿鐩戠潱妫�娴嬫棩鏈熶负褰撳ぉ
+            detailsRecord.setTestDate(LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy.M.d")));
+            detailsRecord.setProblemDesc("鏃�");
 
         }
         // 娣诲姞鎵瑰噯浜哄悕绉�
@@ -517,7 +696,48 @@
     }
 
     /**
-     * 瀵煎嚭鐩戠潱璁板綍琛�
+     * 琚洃鐫d汉鍛樼‘璁ょ洃鐫h褰�(绋嬪簭4.2.5: 琚洃鐫e璞″鐩戠潱璁板綍鐪熷疄鎬ф彁鍑虹‘璁ゆ剰瑙�)
+     */
+    @Override
+    public boolean confirmSuperviseRecord(QualitySuperviseDetailsRecord input) {
+        if (input == null || input.getSuperviseDetailsId() == null) {
+            throw new ErrorException("缂哄皯鐩戠潱璇︾粏淇℃伅id");
+        }
+        QualitySuperviseDetailsRecord record = qualitySuperviseDetailsRecordMapper.selectOne(
+                Wrappers.<QualitySuperviseDetailsRecord>lambdaQuery()
+                        .eq(QualitySuperviseDetailsRecord::getSuperviseDetailsId, input.getSuperviseDetailsId())
+                        .last("limit 1"));
+        if (record == null) {
+            throw new ErrorException("鐩戠潱鍛樺皻鏈彁浜ょ洃鐫h褰曪紝鏃犳硶纭");
+        }
+        Integer userId = SecurityUtils.getUserId().intValue();
+        User user = userMapper.selectById(userId);
+        if (user == null) {
+            throw new ErrorException("褰撳墠鐢ㄦ埛涓嶅瓨鍦�");
+        }
+        // 鍙湁琚洃鐫d汉鍛樻湰浜烘墠鍙互纭
+        boolean isSupervisee = StrUtil.split(record.getTestMember(), '銆�').stream()
+                .map(String::trim)
+                .anyMatch(name -> name.equals(user.getName()));
+        if (!isSupervisee) {
+            QualitySuperviseDetails detail = qualitySuperviseDetailsMapper.selectById(input.getSuperviseDetailsId());
+            isSupervisee = detail != null && userId.equals(detail.getSupervisedUserId());
+        }
+        if (!isSupervisee) {
+            throw new ErrorException("鍙湁琚洃鐫d汉鍛樺彲浠ョ‘璁ょ洃鐫h褰�");
+        }
+        String opinion = StringUtils.isBlank(input.getConfirmOpinion())
+                ? "瀵圭洃鐫h褰曠湡瀹炴�ц〃绀鸿鍙��" : input.getConfirmOpinion().trim();
+        qualitySuperviseDetailsRecordMapper.update(null,
+                Wrappers.<QualitySuperviseDetailsRecord>lambdaUpdate()
+                        .eq(QualitySuperviseDetailsRecord::getSuperviseDetailsRecordId,
+                                record.getSuperviseDetailsRecordId())
+                        .set(QualitySuperviseDetailsRecord::getConfirmOpinion, opinion));
+        return true;
+    }
+
+    /**
+     * 瀵煎嚭鐩戠潱璁板綍琛�(鏃ュ父鐩戠潱妫�鏌ヨ褰曡〃)
      * @param superviseDetailsId
      * @param response
      */
@@ -525,33 +745,36 @@
     public void exportSuperviseDetailRecord(Integer superviseDetailsId, HttpServletResponse response) {
         QualitySuperviseDetailsRecord recordDto = qualitySuperviseDetailsRecordMapper.selectSuperviseDetailRecord(superviseDetailsId);
 
-        // 鏌ヨ妫�娴嬩汉鍛�
-        User tserUser = new User();
+        // 鐩戠潱鍐呭鍕鹃�夊睍绀轰覆(鐓х焊寮犺〃鎺掑竷)
+        recordDto.setSuperviseContentStr(buildSuperviseContentStr(recordDto.getSuperviseContent()));
+
+        // 鍙楃洃鐫d汉澶氫釜鏃舵瘡琛屼竴涓�
         if (StringUtils.isNotBlank(recordDto.getTestMember())) {
-            tserUser = userMapper.selectOne(Wrappers.<User>lambdaQuery()
-                    .eq(User::getName, recordDto.getTestMember())
-                    .last("limit 1"));
+            recordDto.setTestMember(recordDto.getTestMember().replace("銆�", "\n"));
         }
 
         // 鑾峰彇璺緞
-        InputStream inputStream = this.getClass().getResourceAsStream("/static/supervision-detail-record.docx");
+        InputStream inputStream = this.getClass().getResourceAsStream("/static/daily-supervise-record.docx");
         ConfigureBuilder builder = Configure.builder();
         builder.useSpringEL(true);
-        User finalTserUser = tserUser;
         XWPFTemplate template = XWPFTemplate.compile(inputStream, builder.build()).render(
                 new HashMap<String, Object>() {{
                     put("supervision", recordDto);
-                    put("testMemberUrl", UserUtils.getFinalUserSignatureUrl(finalTserUser.getId()));
-                    put("supervisoruUrl", UserUtils.getFinalUserSignatureUrl(recordDto.getSupervisor()));
-                    put("technicalDirectorUrl", UserUtils.getFinalUserSignatureUrl(recordDto.getRatifyUserId()));
-                    put("technicalDirectorDateUrl", recordDto.getRatifyTime() != null ?
-                            Pictures.ofStream(DateImageUtil.createDateImage(recordDto.getRatifyTime())).create() : null);
+                    put("supervisorUrl", signatureOrTransparent(recordDto.getSupervisor()));
+                    put("qualityUrl", signatureOrTransparent(recordDto.getRatifyUserId()));
+                    put("qualityDateStr", recordDto.getRatifyTime() != null ?
+                            recordDto.getRatifyTime().format(RECORD_DATE_FORMATTER) : "");
+                    // 绋嬪簭4.2.5: 琚洃鐫d汉鍛樺鐩戠潱璁板綍鐪熷疄鎬х殑纭鎰忚, 鏈‘璁ゆ椂娌跨敤绾稿紶琛ㄧ殑榛樿琛ㄨ堪
+                    put("confirmOpinionText", StringUtils.isNotBlank(recordDto.getConfirmOpinion())
+                            ? recordDto.getConfirmOpinion() : "瀵圭洃鐫h褰曠湡瀹炴�ц〃绀鸿鍙��");
+                    put("superviseMethodText", StringUtils.isNotBlank(recordDto.getSuperviseMethod())
+                            ? recordDto.getSuperviseMethod() : "");
                 }});
 
         try {
             response.setContentType("application/msword");
             String fileName = URLEncoder.encode(
-                    "瀵煎嚭鐩戠潱璁板綍琛�", "UTF-8");
+                    "鏃ュ父鐩戠潱妫�鏌ヨ褰曡〃", "UTF-8");
             response.setHeader("Content-disposition",
                     "attachment;filename=" + fileName + ".docx");
             OutputStream os = response.getOutputStream();
@@ -562,6 +785,85 @@
         } catch (Exception e) {
             e.printStackTrace();
             throw new RuntimeException("瀵煎嚭澶辫触");
+        }
+    }
+
+    /**
+     * 鐩戠潱鍐呭(9椤�, 涓庡彈鎺ц褰曡〃 LMSY-CX29-JL02 鐨勬í鎺掗『搴忋�佹枃瀛楀畬鍏ㄤ竴鑷�)
+     */
+    private static final String[] SUPERVISE_CONTENT_ITEMS = {
+            "浜哄憳鎸佽瘉鍙婂煿璁�", "璁炬柦鍙婄幆澧冩潯浠�", "妫�娴嬫柟娉�",
+            "浠櫒璁惧绠$悊", "娴嬮噺婧簮鎬�", "鏍囧噯鐗╄川绠$悊",
+            "鏍峰搧鍒跺鍜屽缃�", "妫�娴嬫暟鎹鐞�", "缁撴灉鎶ュ憡缁撴灉"
+    };
+
+    /**
+     * 鎷兼帴"鐩戠潱鍐呭"鍕鹃�夊睍绀轰覆, 姣�3椤逛竴琛�
+     */
+    private String buildSuperviseContentStr(String superviseContent) {
+        String content = StringUtils.isNotBlank(superviseContent) ? superviseContent : "";
+        StringBuilder sb = new StringBuilder();
+        for (int i = 0; i < SUPERVISE_CONTENT_ITEMS.length; i++) {
+            String item = SUPERVISE_CONTENT_ITEMS[i];
+            sb.append(content.contains(item) ? "鈽�" : "鈻�").append(item).append("銆�銆�");
+            if (i % 3 == 2) {
+                sb.append("\n");
+            }
+        }
+        return sb.toString().trim();
+    }
+
+    /**
+     * 鍏ㄩ�忔槑 1x1 PNG(宸查獙璇佸儚绱� A=0, 鎷変几鍒板師鏍囩灏哄鍚庝笉鍙), 鍥剧墖缂哄け鏃跺厹搴�
+     */
+    private static final byte[] TRANSPARENT_PNG = Base64.getDecoder().decode(
+            "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAANSURBVBhXY2BgYGAAAAAFAAGKM+MAAAAAAElFTkSuQmCC");
+
+    /**
+     * 鍥剧墖鏍囩鍊煎厹搴�: null 鎴栨湰鍦板浘鐗囪鍙栧け璐�(濡傜鍚嶆枃浠剁己澶�)鏃惰繑鍥為�忔槑鍥剧墖
+     */
+    private PictureRenderData orTransparentPicture(PictureRenderData picture) {
+        if (picture != null) {
+            try {
+                byte[] data = picture.readPictureData();
+                if (data != null && data.length > 0) {
+                    return picture;
+                }
+            } catch (Exception ignored) {
+            }
+        }
+        return Pictures.ofBytes(TRANSPARENT_PNG).create();
+    }
+
+    /**
+     * 瀵煎嚭鏃ユ湡鏂囧瓧鏍煎紡(濡� 2026骞�1鏈�9鏃�)
+     */
+    private static final DateTimeFormatter SUPERVISE_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy骞碝鏈坉鏃�");
+
+    /**
+     * 璁板綍琛ㄦ棩鏈熸枃瀛楁牸寮�(涓庤〃鍐�"妫�娴嬫棩鏈�"瀛楁涓�鑷�, 濡� 2026.10.14)
+     */
+    private static final DateTimeFormatter RECORD_DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy.M.d");
+
+    /**
+     * 鐢ㄦ埛绛惧悕鍏滃簳(鎸塈D): 浜哄憳涓嶅瓨鍦ㄦ垨绛惧悕鏈厤缃椂鎺ュ彛浼氱洿鎺ユ姏閿�, 姝ゅ杩斿洖閫忔槑鍥剧墖
+     */
+    private PictureRenderData signatureOrTransparent(Integer userId) {
+        try {
+            return orTransparentPicture(UserUtils.getFinalUserSignatureUrl(userId));
+        } catch (Exception e) {
+            return Pictures.ofBytes(TRANSPARENT_PNG).create();
+        }
+    }
+
+    /**
+     * 鐢ㄦ埛绛惧悕鍏滃簳(鎸夊鍚�): 浜哄憳涓嶅瓨鍦ㄦ垨绛惧悕鏈厤缃椂鎺ュ彛浼氱洿鎺ユ姏閿�, 姝ゅ杩斿洖閫忔槑鍥剧墖
+     */
+    private PictureRenderData signatureOrTransparent(String userName) {
+        try {
+            return orTransparentPicture(UserUtils.getFinalUserSignatureUrl(userName));
+        } catch (Exception e) {
+            return Pictures.ofBytes(TRANSPARENT_PNG).create();
         }
     }
 
@@ -861,10 +1163,10 @@
         XWPFTemplate template = XWPFTemplate.compile(inputStream, builder.build()).render(
                 new HashMap<String, Object>() {{
                     put("control", exportDto);
-                    put("discovererUrl", StringUtils.isNotBlank(finalDiscovererUrl) ? Pictures.ofLocal(imgUrl + "/" + finalDiscovererUrl).create() : null);
-                    put("responsibleUrl", StringUtils.isNotBlank(finalResponsibleUrl) ? Pictures.ofLocal(imgUrl + "/" + finalResponsibleUrl).create() : null);
-                    put("correctiveUrl", StringUtils.isNotBlank(finalCorrectiveUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCorrectiveUrl).create() : null);
-                    put("qualityUrl", StringUtils.isNotBlank(finalQualityUrl) ? Pictures.ofLocal(imgUrl + "/" + finalQualityUrl).create() : null);
+                    put("discovererUrl", orTransparentPicture(StringUtils.isNotBlank(finalDiscovererUrl) ? Pictures.ofLocal(imgUrl + "/" + finalDiscovererUrl).create() : null));
+                    put("responsibleUrl", orTransparentPicture(StringUtils.isNotBlank(finalResponsibleUrl) ? Pictures.ofLocal(imgUrl + "/" + finalResponsibleUrl).create() : null));
+                    put("correctiveUrl", orTransparentPicture(StringUtils.isNotBlank(finalCorrectiveUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCorrectiveUrl).create() : null));
+                    put("qualityUrl", orTransparentPicture(StringUtils.isNotBlank(finalQualityUrl) ? Pictures.ofLocal(imgUrl + "/" + finalQualityUrl).create() : null));
                 }});
 
         try {
@@ -1240,10 +1542,10 @@
         XWPFTemplate template = XWPFTemplate.compile(inputStream, builder.build()).render(
                 new HashMap<String, Object>() {{
                     put("correct", detailsCorrectDto);
-                    put("raiseUrl", StringUtils.isNotBlank(finalRaiseUrl) ? Pictures.ofLocal(imgUrl + "/" + finalRaiseUrl).create() : null);
-                    put("causeUrl", StringUtils.isNotBlank(finalCauseUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCauseUrl).create() : null);
-                    put("correctUrl", StringUtils.isNotBlank(finalCorrectUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCorrectUrl).create() : null);
-                    put("validationUrl", StringUtils.isNotBlank(finalValidationUrl) ? Pictures.ofLocal(imgUrl + "/" + finalValidationUrl).create() : null);
+                    put("raiseUrl", orTransparentPicture(StringUtils.isNotBlank(finalRaiseUrl) ? Pictures.ofLocal(imgUrl + "/" + finalRaiseUrl).create() : null));
+                    put("causeUrl", orTransparentPicture(StringUtils.isNotBlank(finalCauseUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCauseUrl).create() : null));
+                    put("correctUrl", orTransparentPicture(StringUtils.isNotBlank(finalCorrectUrl) ? Pictures.ofLocal(imgUrl + "/" + finalCorrectUrl).create() : null));
+                    put("validationUrl", orTransparentPicture(StringUtils.isNotBlank(finalValidationUrl) ? Pictures.ofLocal(imgUrl + "/" + finalValidationUrl).create() : null));
                 }});
 
         try {

--
Gitblit v1.9.3