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