From e45e74e5ae207f7642be9a88ed99b593de89fb3b Mon Sep 17 00:00:00 2001
From: longz <longzhaoz@qq.com>
Date: 星期三, 24 六月 2026 16:54:46 +0800
Subject: [PATCH] 1.0

---
 backend/src/main/java/com/sinata/appointment/service/ConfigApplicationService.java |  105 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 105 insertions(+), 0 deletions(-)

diff --git a/backend/src/main/java/com/sinata/appointment/service/ConfigApplicationService.java b/backend/src/main/java/com/sinata/appointment/service/ConfigApplicationService.java
index c151b00..9dc8214 100644
--- a/backend/src/main/java/com/sinata/appointment/service/ConfigApplicationService.java
+++ b/backend/src/main/java/com/sinata/appointment/service/ConfigApplicationService.java
@@ -3,6 +3,7 @@
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.alibaba.excel.EasyExcel;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.sinata.appointment.constants.BizErrorCode;
@@ -17,6 +18,7 @@
 import com.sinata.appointment.dto.FieldConfigPatchRequest;
 import com.sinata.appointment.dto.SensitiveWordCreateRequest;
 import com.sinata.appointment.dto.SensitiveWordEnabledPatchRequest;
+import com.sinata.appointment.dto.SensitiveWordImportResultVo;
 import com.sinata.appointment.dto.SensitiveWordListVo;
 import com.sinata.appointment.dto.SensitiveWordUpdateRequest;
 import com.sinata.appointment.dto.VisitReasonCreateRequest;
@@ -38,12 +40,17 @@
 import com.sinata.appointment.mapper.SysBizSettingMapper;
 import com.sinata.appointment.mapper.SysDeptMapper;
 import com.sinata.appointment.util.UuidAssert;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
 import java.time.LocalDate;
 import java.time.YearMonth;
 import java.time.format.DateTimeParseException;
+import java.util.ArrayList;
 import java.util.Comparator;
+import java.util.HashSet;
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 import java.util.stream.Collectors;
@@ -53,6 +60,7 @@
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
 
 @Service
 @RequiredArgsConstructor
@@ -180,6 +188,74 @@
         loadActiveSensitiveWord(id, uuid);
         bizSensitiveWordMapper.deleteById(id);
         sensitiveWordFilterService.evictCache();
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public SensitiveWordImportResultVo importSensitiveWords(MultipartFile file) {
+        if (file == null || file.isEmpty()) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "导入文件不能为空");
+        }
+        String filename = file.getOriginalFilename() == null ? "" : file.getOriginalFilename().toLowerCase();
+        if (!filename.endsWith(".xlsx") && !filename.endsWith(".xls")) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "请上传 xls 或 xlsx 文件");
+        }
+
+        List<Map<Integer, String>> rows;
+        try {
+            rows = EasyExcel.read(file.getInputStream()).sheet().headRowNumber(0).doReadSync();
+        } catch (IOException ex) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "读取导入文件失败");
+        }
+
+        SensitiveWordImportResultVo result = new SensitiveWordImportResultVo();
+        Set<String> fileWords = new LinkedHashSet<>();
+        Set<String> existingWords = loadExistingSensitiveWords();
+        int rowNo = 0;
+        for (Map<Integer, String> row : rows) {
+            rowNo++;
+            String raw = row == null ? null : row.get(0);
+            if (rowNo == 1 && isSensitiveWordHeader(raw)) {
+                continue;
+            }
+            if (!StringUtils.hasText(raw)) {
+                continue;
+            }
+            result.setTotalRows(result.getTotalRows() + 1);
+            String word;
+            try {
+                word = normalizeSensitiveWord(raw);
+            } catch (BizException ex) {
+                result.setInvalidCount(result.getInvalidCount() + 1);
+                addImportError(result, "第" + rowNo + "行:" + ex.getMessage());
+                continue;
+            }
+            if (fileWords.contains(word) || existingWords.contains(word)) {
+                result.setDuplicateCount(result.getDuplicateCount() + 1);
+                continue;
+            }
+            BizSensitiveWord entity = new BizSensitiveWord();
+            entity.setWord(word);
+            entity.setEnabled(1);
+            bizSensitiveWordMapper.insert(entity);
+            fileWords.add(word);
+            existingWords.add(word);
+            result.setSuccessCount(result.getSuccessCount() + 1);
+        }
+        if (result.getSuccessCount() > 0) {
+            sensitiveWordFilterService.evictCache();
+        }
+        return result;
+    }
+
+    public byte[] sensitiveWordImportTemplateExcel() {
+        try (ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
+            List<List<String>> head = List.of(List.of("敏感词"));
+            List<List<String>> rows = List.of(List.of("示例敏感词"));
+            EasyExcel.write(bos).head(head).sheet("敏感词导入").doWrite(rows);
+            return bos.toByteArray();
+        } catch (IOException ex) {
+            throw new BizException(BizErrorCode.AUTH_INVALID_CREDENTIALS, "生成导入模板失败");
+        }
     }
 
     public List<BizFieldConfig> listFieldConfigs() {
@@ -572,6 +648,35 @@
         return normalized;
     }
 
+    private Set<String> loadExistingSensitiveWords() {
+        List<BizSensitiveWord> rows =
+                bizSensitiveWordMapper.selectList(
+                        new LambdaQueryWrapper<BizSensitiveWord>()
+                                .eq(BizSensitiveWord::getDelFlag, 0)
+                                .select(BizSensitiveWord::getWord));
+        Set<String> words = new HashSet<>();
+        for (BizSensitiveWord row : rows) {
+            if (StringUtils.hasText(row.getWord())) {
+                words.add(row.getWord().trim());
+            }
+        }
+        return words;
+    }
+
+    private boolean isSensitiveWordHeader(String value) {
+        if (!StringUtils.hasText(value)) {
+            return false;
+        }
+        String text = value.trim();
+        return "敏感词".equals(text) || "敏感词库".equals(text) || "word".equalsIgnoreCase(text);
+    }
+
+    private void addImportError(SensitiveWordImportResultVo result, String message) {
+        if (result.getErrorMessages().size() < 20) {
+            result.getErrorMessages().add(message);
+        }
+    }
+
     private void assertReasonNameUnique(String reasonName, Long excludeId) {
         LambdaQueryWrapper<BizVisitReason> q =
                 new LambdaQueryWrapper<BizVisitReason>()

--
Gitblit v1.7.1