Bläddra i källkod

关键词推荐更新

liuchengsen 3 veckor sedan
förälder
incheckning
35509155b4

+ 5 - 0
backend-java/src/main/java/com/pharmacopoeia/controller/AdminKnowledgeController.java

@@ -333,6 +333,11 @@ public class AdminKnowledgeController {
         return ResponseEntity.ok(brandRecommendService.deleteRule(id));
     }
 
+    @PatchMapping("/brand-recommend-rules/{id}/toggle")
+    public ResponseEntity<Map<String, Object>> toggleBrandRecommendRule(@PathVariable int id) {
+        return ResponseEntity.ok(brandRecommendService.toggleRule(id));
+    }
+
     @PostMapping("/brand-recommend-rules/import")
     public ResponseEntity<Map<String, Object>> importBrandRecommendRules(
             @RequestBody Map<String, String> body) {

+ 53 - 30
backend-java/src/main/java/com/pharmacopoeia/service/BrandRecommendService.java

@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.util.*;
+import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 @Service
@@ -26,8 +27,9 @@ public class BrandRecommendService {
 
     public List<Map<String, Object>> match(List<Map<String, Object>> sources, String fullAnswer) {
         List<BrandRecommendRule> rules = ruleRepository.findByIsActiveTrueOrderByTierAscKeywordAsc();
-        if (rules.isEmpty()) return List.of();
-
+        if (rules.isEmpty()) {
+            return List.of();
+        }
         Map<Integer, List<BrandRecommendRule>> byTier = rules.stream()
                 .collect(Collectors.groupingBy(BrandRecommendRule::getTier, LinkedHashMap::new, Collectors.toList()));
 
@@ -61,12 +63,13 @@ public class BrandRecommendService {
         Map<Integer, Brand> brandMap = brandRepository.findAllById(brandIds).stream()
                 .collect(Collectors.toMap(Brand::getId, b -> b));
 
-        Set<Integer> seen = new HashSet<>();
+        Set<Integer> seenRuleIds = new HashSet<>();    // 规则去重(同规则只命中一次)
+        Set<Integer> seenBrandIds = new HashSet<>();   // 品牌去重(同品牌只返回一次)
         List<Map<String, Object>> result = new ArrayList<>();
         for (BrandRecommendRule rule : rules) {
             for (String target : targets) {
                 if (target.toLowerCase().contains(rule.getKeyword().toLowerCase())) {
-                    if (seen.add(rule.getId())) {
+                    if (seenRuleIds.add(rule.getId()) && seenBrandIds.add(rule.getBrandId())) {
                         Brand brand = brandMap.get(rule.getBrandId());
                         result.add(buildBrandResult(rule, brand));
                     }
@@ -84,18 +87,18 @@ public class BrandRecommendService {
         m.put("tier", rule.getTier());
         if (brand != null) {
             m.put("brand_name", brand.getName());
-            m.put("description", brand.getDescription() != null ? brand.getDescription() : "");
-            m.put("function", brand.getFunctionIndication() != null ? brand.getFunctionIndication() : "");
-            m.put("usage_dosage", brand.getUsageDosage() != null ? brand.getUsageDosage() : "");
-            m.put("contraindication", brand.getContraindication() != null ? brand.getContraindication() : "");
-            m.put("ingredients", brand.getIngredients() != null ? brand.getIngredients() : "");
-            m.put("properties", brand.getProperties() != null ? brand.getProperties() : "");
-            m.put("specification", brand.getSpecification() != null ? brand.getSpecification() : "");
-            m.put("adverse_reactions", brand.getAdverseReactions() != null ? brand.getAdverseReactions() : "");
-            m.put("precautions", brand.getPrecautions() != null ? brand.getPrecautions() : "");
-            m.put("execution_standard", brand.getExecutionStandard() != null ? brand.getExecutionStandard() : "");
-            m.put("storage", brand.getStorage() != null ? brand.getStorage() : "");
-            m.put("jump_url", brand.getJumpUrl() != null ? brand.getJumpUrl() : "");
+            m.put("description", nullToEmpty(brand.getDescription()));
+            m.put("function", nullToEmpty(brand.getFunctionIndication()));
+            m.put("usage_dosage", nullToEmpty(brand.getUsageDosage()));
+            m.put("contraindication", nullToEmpty(brand.getContraindication()));
+            m.put("ingredients", nullToEmpty(brand.getIngredients()));
+            m.put("properties", nullToEmpty(brand.getProperties()));
+            m.put("specification", nullToEmpty(brand.getSpecification()));
+            m.put("adverse_reactions", nullToEmpty(brand.getAdverseReactions()));
+            m.put("precautions", nullToEmpty(brand.getPrecautions()));
+            m.put("execution_standard", nullToEmpty(brand.getExecutionStandard()));
+            m.put("storage", nullToEmpty(brand.getStorage()));
+            m.put("jump_url", nullToEmpty(brand.getJumpUrl()));
         }
         return m;
     }
@@ -165,20 +168,20 @@ public class BrandRecommendService {
             }
             b.setName(name.trim());
         }
-        if (body.containsKey("function_indication")) b.setFunctionIndication(str(body, "function_indication"));
-        if (body.containsKey("usage_dosage")) b.setUsageDosage(str(body, "usage_dosage"));
-        if (body.containsKey("contraindication")) b.setContraindication(str(body, "contraindication"));
-        if (body.containsKey("ingredients")) b.setIngredients(str(body, "ingredients"));
-        if (body.containsKey("properties")) b.setProperties(str(body, "properties"));
-        if (body.containsKey("specification")) b.setSpecification(str(body, "specification"));
-        if (body.containsKey("adverse_reactions")) b.setAdverseReactions(str(body, "adverse_reactions"));
-        if (body.containsKey("precautions")) b.setPrecautions(str(body, "precautions"));
-        if (body.containsKey("execution_standard")) b.setExecutionStandard(str(body, "execution_standard"));
-        if (body.containsKey("storage")) b.setStorage(str(body, "storage"));
-        if (body.containsKey("jump_url")) b.setJumpUrl(str(body, "jump_url"));
-        if (body.containsKey("description")) b.setDescription(str(body, "description"));
-        if (body.get("sort_order") instanceof Number n) b.setSortOrder(n.intValue());
-        if (body.get("is_active") instanceof Boolean a) b.setIsActive(a);
+        applyStr(body, "function_indication", b::setFunctionIndication);
+        applyStr(body, "usage_dosage", b::setUsageDosage);
+        applyStr(body, "contraindication", b::setContraindication);
+        applyStr(body, "ingredients", b::setIngredients);
+        applyStr(body, "properties", b::setProperties);
+        applyStr(body, "specification", b::setSpecification);
+        applyStr(body, "adverse_reactions", b::setAdverseReactions);
+        applyStr(body, "precautions", b::setPrecautions);
+        applyStr(body, "execution_standard", b::setExecutionStandard);
+        applyStr(body, "storage", b::setStorage);
+        applyStr(body, "jump_url", b::setJumpUrl);
+        applyStr(body, "description", b::setDescription);
+        Optional.ofNullable((Number) body.get("sort_order")).ifPresent(n -> b.setSortOrder(n.intValue()));
+        Optional.ofNullable((Boolean) body.get("is_active")).ifPresent(b::setIsActive);
         brandRepository.save(b);
         return Map.of("ok", true, "id", id, "message", "已更新");
     }
@@ -309,6 +312,17 @@ public class BrandRecommendService {
         return Map.of("ok", true, "message", "已删除");
     }
 
+    @Transactional
+    public Map<String, Object> toggleRule(int id) {
+        var opt = ruleRepository.findById(id);
+        if (opt.isEmpty()) return Map.of("ok", false, "message", "未找到该规则");
+        BrandRecommendRule rule = opt.get();
+        rule.setIsActive(!Boolean.TRUE.equals(rule.getIsActive()));
+        ruleRepository.save(rule);
+        return Map.of("ok", true, "id", id, "is_active", rule.getIsActive(),
+                "message", Boolean.TRUE.equals(rule.getIsActive()) ? "已启用" : "已停用");
+    }
+
     // ==================== CSV 导入导出 ====================
 
     public Map<String, Object> importBrandsCsv(String csv) {
@@ -416,6 +430,15 @@ public class BrandRecommendService {
         return s.isEmpty() ? null : s;
     }
 
+    private static String nullToEmpty(String s) {
+        return s != null ? s : "";
+    }
+
+    /** map 中存在 key 时应用字符串值 */
+    private static void applyStr(Map<String, Object> body, String key, Consumer<String> setter) {
+        Optional.ofNullable(str(body, key)).ifPresent(setter);
+    }
+
     private static String col(String[] cols, int idx) {
         if (idx >= cols.length) return null;
         String s = cols[idx].trim();

+ 1 - 1
static/index.html

@@ -149,7 +149,7 @@
   <div class="input-area">
     <input type="file" id="imageInput" accept="image/*" onchange="onFilePicked('image')" style="display:none">
     <input id="userInput" placeholder="输入药品问题,或上传图片...">
-    <button class="btntn-danger" id="sendBtn" onclick="send()">发送</button>
+    <button class="btn btn-danger" id="sendBtn" onclick="send()">发送</button>
     <span style="display:flex;flex-direction:column;align-items:center;gap:2px;flex-shrink:0;cursor:pointer" onclick="document.getElementById('imageInput').click()"><button class="media-btn" type="button">+</button></span>
     <button class="btn btn-secondary" id="stopBtn" onclick="stopCurrent()" style="display:none">停止</button>
   </div>