Sfoglia il codice sorgente

导入导出模块更新

liuchengsen 3 settimane fa
parent
commit
2f8f04f5dc

+ 26 - 2
backend-java/src/main/java/com/pharmacopoeia/controller/AdminKnowledgeController.java

@@ -287,7 +287,7 @@ public class AdminKnowledgeController {
     @PostMapping("/brands/import")
     public ResponseEntity<Map<String, Object>> importBrands(@RequestParam("file") MultipartFile file) {
         try {
-            String csv = new String(file.getBytes(), StandardCharsets.UTF_8);
+            String csv = readCsvWithAutoEncoding(file.getBytes());
             return ResponseEntity.ok(brandRecommendService.importBrandsCsv(csv));
         } catch (IllegalArgumentException e) {
             return ResponseEntity.badRequest().body(Map.of("ok", false, "message", e.getMessage()));
@@ -361,7 +361,7 @@ public class AdminKnowledgeController {
     public ResponseEntity<Map<String, Object>> importBrandRecommendRules(
             @RequestParam("file") MultipartFile file) {
         try {
-            String csv = new String(file.getBytes(), StandardCharsets.UTF_8);
+            String csv = readCsvWithAutoEncoding(file.getBytes());
             return ResponseEntity.ok(brandRecommendService.importRulesCsv(csv));
         } catch (IllegalArgumentException e) {
             return ResponseEntity.badRequest().body(Map.of("ok", false, "message", e.getMessage()));
@@ -402,6 +402,30 @@ public class AdminKnowledgeController {
     }
    // ==================== Helpers ====================
 
+    /**
+     * Read CSV bytes with auto-encoding detection.
+     * Tries UTF-8 first (with BOM handling), falls back to GBK for Chinese Windows Excel exports.
+     */
+    private String readCsvWithAutoEncoding(byte[] bytes) {
+        // Try UTF-8 first
+        String s = new String(bytes, StandardCharsets.UTF_8);
+        byte[] header = new byte[Math.min(bytes.length, 200)];
+        if (bytes.length >= 3 && bytes[0] == (byte) 0xEF && bytes[1] == (byte) 0xBB && bytes[2] == (byte) 0xBF) {
+            // Has UTF-8 BOM — definitely UTF-8
+            return s;
+        }
+        // Quick check: if the first 200 bytes look like garbled CJK, try GBK
+        for (int i = 0; i < header.length; i++) {
+            header[i] = bytes[i];
+        }
+        String utf8Head = new String(header, StandardCharsets.UTF_8);
+        // If UTF-8 result contains replacement chars (�), it's likely GBK
+        if (utf8Head.contains("�")) {
+            return new String(bytes, java.nio.charset.Charset.forName("GBK"));
+        }
+        return s;
+    }
+
     private int parseCount(ImportRequest req) {
         try {
             if ("json".equals(req.getFormat()) && req.getData() != null) {

+ 7 - 4
backend-java/src/main/java/com/pharmacopoeia/service/BrandRecommendService.java

@@ -318,12 +318,14 @@ public class BrandRecommendService {
         Map<String, Integer> colIdx = buildColIdx(rows, BRAND_CSV_COLS);
 
         int success = 0, skipped = 0, failed = 0;
+        // Track names already in this batch to avoid duplicate-key violation within same import
+        Set<String> batchNames = new HashSet<>();
         for (int i = 1; i < rows.size(); i++) {
             String[] cols = rows.get(i);
             try {
                 String name = cell(cols, colIdx, "品牌名称");
                 if (name == null || name.isEmpty()) { failed++; continue; }
-                if (brandRepository.existsByNameAndIdNot(name, null)) { skipped++; continue; }
+                if (!batchNames.add(name) || brandRepository.existsByNameAndIdNot(name, null)) { skipped++; continue; }
                 brandRepository.save(Brand.builder()
                         .name(name)
                         .functionIndication(cell(cols, colIdx, "功能主治"))
@@ -387,7 +389,7 @@ public class BrandRecommendService {
     }
 
     public String exportBrandsCsv(Set<Integer> ids) {
-        List<Brand> brands = ids.isEmpty() ? List.of() : brandRepository.findAllById(ids);
+        List<Brand> brands = (ids != null && !ids.isEmpty()) ? brandRepository.findAllById(ids) : List.of();
         StringBuilder sb = new StringBuilder(BRANDS_CSV_HEADER).append("\n");
         List<Function<Brand, String>> extractors = List.of(
                 b -> esc(b.getName()),
@@ -415,12 +417,13 @@ public class BrandRecommendService {
         Map<String, Integer> colIdx = buildColIdx(rows, RULES_CSV_COLS);
 
         int success = 0, skipped = 0, failed = 0;
+        Set<String> batchKw = new HashSet<>();
         for (int i = 1; i < rows.size(); i++) {
             String[] cols = rows.get(i);
             try {
                 String kw = cell(cols, colIdx, "关键词");
                 if (kw == null || kw.isEmpty()) { failed++; continue; }
-                if (ruleRepository.existsByKeywordAndIdNot(kw, null)) { skipped++; continue; }
+                if (!batchKw.add(kw) || ruleRepository.existsByKeywordAndIdNot(kw, null)) { skipped++; continue; }
 
                 int brandId = cellInt(cols, colIdx, "品牌ID");
                 String brandName = cell(cols, colIdx, "品牌名称");
@@ -444,7 +447,7 @@ public class BrandRecommendService {
     }
 
     public String exportRulesCsv(Set<Integer> ids) {
-        List<BrandRecommendRule> rules = ids.isEmpty() ? List.of() : ruleRepository.findAllById(ids);
+        List<BrandRecommendRule> rules = (ids != null && !ids.isEmpty()) ? ruleRepository.findAllById(ids) : List.of();
         Set<Integer> brandIds = rules.stream().map(BrandRecommendRule::getBrandId).collect(Collectors.toSet());
         Map<Integer, String> brandNames = brandRepository.findAllById(brandIds).stream()
                 .collect(Collectors.toMap(Brand::getId, Brand::getName));