|
@@ -8,6 +8,7 @@ import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
|
|
import java.util.*;
|
|
import java.util.*;
|
|
|
|
|
+import java.util.function.Consumer;
|
|
|
import java.util.stream.Collectors;
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
@Service
|
|
@Service
|
|
@@ -26,8 +27,9 @@ public class BrandRecommendService {
|
|
|
|
|
|
|
|
public List<Map<String, Object>> match(List<Map<String, Object>> sources, String fullAnswer) {
|
|
public List<Map<String, Object>> match(List<Map<String, Object>> sources, String fullAnswer) {
|
|
|
List<BrandRecommendRule> rules = ruleRepository.findByIsActiveTrueOrderByTierAscKeywordAsc();
|
|
List<BrandRecommendRule> rules = ruleRepository.findByIsActiveTrueOrderByTierAscKeywordAsc();
|
|
|
- if (rules.isEmpty()) return List.of();
|
|
|
|
|
-
|
|
|
|
|
|
|
+ if (rules.isEmpty()) {
|
|
|
|
|
+ return List.of();
|
|
|
|
|
+ }
|
|
|
Map<Integer, List<BrandRecommendRule>> byTier = rules.stream()
|
|
Map<Integer, List<BrandRecommendRule>> byTier = rules.stream()
|
|
|
.collect(Collectors.groupingBy(BrandRecommendRule::getTier, LinkedHashMap::new, Collectors.toList()));
|
|
.collect(Collectors.groupingBy(BrandRecommendRule::getTier, LinkedHashMap::new, Collectors.toList()));
|
|
|
|
|
|
|
@@ -61,12 +63,13 @@ public class BrandRecommendService {
|
|
|
Map<Integer, Brand> brandMap = brandRepository.findAllById(brandIds).stream()
|
|
Map<Integer, Brand> brandMap = brandRepository.findAllById(brandIds).stream()
|
|
|
.collect(Collectors.toMap(Brand::getId, b -> b));
|
|
.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<>();
|
|
List<Map<String, Object>> result = new ArrayList<>();
|
|
|
for (BrandRecommendRule rule : rules) {
|
|
for (BrandRecommendRule rule : rules) {
|
|
|
for (String target : targets) {
|
|
for (String target : targets) {
|
|
|
if (target.toLowerCase().contains(rule.getKeyword().toLowerCase())) {
|
|
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());
|
|
Brand brand = brandMap.get(rule.getBrandId());
|
|
|
result.add(buildBrandResult(rule, brand));
|
|
result.add(buildBrandResult(rule, brand));
|
|
|
}
|
|
}
|
|
@@ -84,18 +87,18 @@ public class BrandRecommendService {
|
|
|
m.put("tier", rule.getTier());
|
|
m.put("tier", rule.getTier());
|
|
|
if (brand != null) {
|
|
if (brand != null) {
|
|
|
m.put("brand_name", brand.getName());
|
|
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;
|
|
return m;
|
|
|
}
|
|
}
|
|
@@ -165,20 +168,20 @@ public class BrandRecommendService {
|
|
|
}
|
|
}
|
|
|
b.setName(name.trim());
|
|
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);
|
|
brandRepository.save(b);
|
|
|
return Map.of("ok", true, "id", id, "message", "已更新");
|
|
return Map.of("ok", true, "id", id, "message", "已更新");
|
|
|
}
|
|
}
|
|
@@ -309,6 +312,17 @@ public class BrandRecommendService {
|
|
|
return Map.of("ok", true, "message", "已删除");
|
|
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 导入导出 ====================
|
|
// ==================== CSV 导入导出 ====================
|
|
|
|
|
|
|
|
public Map<String, Object> importBrandsCsv(String csv) {
|
|
public Map<String, Object> importBrandsCsv(String csv) {
|
|
@@ -416,6 +430,15 @@ public class BrandRecommendService {
|
|
|
return s.isEmpty() ? null : s;
|
|
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) {
|
|
private static String col(String[] cols, int idx) {
|
|
|
if (idx >= cols.length) return null;
|
|
if (idx >= cols.length) return null;
|
|
|
String s = cols[idx].trim();
|
|
String s = cols[idx].trim();
|