|
|
@@ -0,0 +1,165 @@
|
|
|
+package com.etotem.cfc.service;
|
|
|
+
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
+import com.etotem.cfc.common.Result;
|
|
|
+import com.etotem.cfc.dto.FamilyMemberVO;
|
|
|
+import com.etotem.cfc.dto.GlobalSearchResult;
|
|
|
+import com.etotem.cfc.entity.GrowthPlan;
|
|
|
+import com.etotem.cfc.entity.HealthReport;
|
|
|
+import com.etotem.cfc.entity.Product;
|
|
|
+import com.etotem.cfc.entity.Task;
|
|
|
+import com.etotem.cfc.mapper.GrowthPlanMapper;
|
|
|
+import com.etotem.cfc.mapper.HealthReportMapper;
|
|
|
+import com.etotem.cfc.mapper.ProductMapper;
|
|
|
+import com.etotem.cfc.mapper.TaskMapper;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+
|
|
|
+import javax.annotation.Resource;
|
|
|
+import java.util.ArrayList;
|
|
|
+import java.util.HashMap;
|
|
|
+import java.util.List;
|
|
|
+import java.util.Map;
|
|
|
+import java.util.stream.Collectors;
|
|
|
+
|
|
|
+/**
|
|
|
+ * 聚合搜索服务(SC-P-82 全局搜索:报告/方案/任务/商品,按类型分流)。
|
|
|
+ *
|
|
|
+ * <p>入口 {@link #globalSearch(String, Long, Long, Integer, Integer)}:
|
|
|
+ * keyword 可空(空结果不报错,返回空四桶);越权过滤:报告/方案/任务桶按调用者
|
|
|
+ * familyId(方案进一步按家庭内成员 id)收敛范围,商品桶仅返回已上架公共商品。</p>
|
|
|
+ */
|
|
|
+@Service
|
|
|
+public class GlobalSearchService {
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private HealthReportMapper healthReportMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private GrowthPlanMapper growthPlanMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private TaskMapper taskMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private ProductMapper productMapper;
|
|
|
+
|
|
|
+ @Resource
|
|
|
+ private FamilyMemberService familyMemberService;
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 聚合搜索入口。
|
|
|
+ *
|
|
|
+ * @param keyword 搜索关键词(null/空白 = 空结果不报错,返回空四桶)
|
|
|
+ * @param userId 登录用户 ID(越权过滤数据维度)
|
|
|
+ * @param familyId 家庭 ID(越权过滤数据维度;null=无法取到家庭上下文,仅返回商品桶)
|
|
|
+ * @param page 页码(null/<1 归一化为 1)
|
|
|
+ * @param pageSize 每页数量(null/<1 默认 10)
|
|
|
+ * @return 统一 Result 包裹四类分流桶(reports/plans/tasks/products,各为 List)
|
|
|
+ */
|
|
|
+ public Result<GlobalSearchResult> globalSearch(String keyword, Long userId, Long familyId,
|
|
|
+ Integer page, Integer pageSize) {
|
|
|
+ String kw = (keyword != null) ? keyword.trim() : null;
|
|
|
+ boolean hasKw = kw != null && !kw.isEmpty();
|
|
|
+ int p = (page == null || page < 1) ? 1 : page;
|
|
|
+ int size = (pageSize == null || pageSize < 1) ? 10 : pageSize;
|
|
|
+
|
|
|
+ GlobalSearchResult result = new GlobalSearchResult();
|
|
|
+
|
|
|
+ // 1) 报告桶:按家庭范围过滤(越权结果过滤)
|
|
|
+ List<Map<String, Object>> reports = new ArrayList<>();
|
|
|
+ if (familyId != null && hasKw) {
|
|
|
+ LambdaQueryWrapper<HealthReport> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(HealthReport::getFamilyId, familyId);
|
|
|
+ wrapper.and(w -> w.like(HealthReport::getPersonName, kw)
|
|
|
+ .or().like(HealthReport::getReportType, kw)
|
|
|
+ .or().like(HealthReport::getReportNumber, kw));
|
|
|
+ wrapper.orderByDesc(HealthReport::getCreatedAt);
|
|
|
+ wrapper.last("LIMIT " + ((p - 1) * size) + ", " + size);
|
|
|
+ for (HealthReport r : healthReportMapper.selectList(wrapper)) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ m.put("id", r.getId());
|
|
|
+ m.put("personName", r.getPersonName());
|
|
|
+ m.put("reportType", r.getReportType());
|
|
|
+ m.put("reportNumber", r.getReportNumber());
|
|
|
+ m.put("status", r.getStatus());
|
|
|
+ m.put("createdAt", r.getCreatedAt());
|
|
|
+ reports.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.setReports(reports);
|
|
|
+
|
|
|
+ // 2) 方案桶:按调用者家庭内成员范围过滤(越权结果过滤)
|
|
|
+ List<Map<String, Object>> plans = new ArrayList<>();
|
|
|
+ if (familyId != null && hasKw) {
|
|
|
+ List<Long> memberIds = familyMemberService.listMembersOfFamily(familyId, userId).stream()
|
|
|
+ .map(FamilyMemberVO::getFamilyMemberId)
|
|
|
+ .filter(id -> id != null)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (!memberIds.isEmpty()) {
|
|
|
+ LambdaQueryWrapper<GrowthPlan> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.in(GrowthPlan::getMemberId, memberIds);
|
|
|
+ wrapper.and(w -> w.like(GrowthPlan::getPlanTitle, kw)
|
|
|
+ .or().like(GrowthPlan::getPlanContent, kw));
|
|
|
+ wrapper.orderByDesc(GrowthPlan::getCreatedAt);
|
|
|
+ wrapper.last("LIMIT " + ((p - 1) * size) + ", " + size);
|
|
|
+ for (GrowthPlan plan : growthPlanMapper.selectList(wrapper)) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ m.put("id", plan.getId());
|
|
|
+ m.put("planTitle", plan.getPlanTitle());
|
|
|
+ m.put("planContent", plan.getPlanContent());
|
|
|
+ m.put("memberId", plan.getMemberId());
|
|
|
+ m.put("createdAt", plan.getCreatedAt());
|
|
|
+ plans.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.setPlans(plans);
|
|
|
+
|
|
|
+ // 3) 任务桶:按家庭范围过滤(越权结果过滤)
|
|
|
+ List<Map<String, Object>> tasks = new ArrayList<>();
|
|
|
+ if (familyId != null && hasKw) {
|
|
|
+ LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(Task::getFamilyId, familyId);
|
|
|
+ wrapper.and(w -> w.like(Task::getTitle, kw)
|
|
|
+ .or().like(Task::getDescription, kw));
|
|
|
+ wrapper.orderByDesc(Task::getCreatedAt);
|
|
|
+ wrapper.last("LIMIT " + ((p - 1) * size) + ", " + size);
|
|
|
+ for (Task t : taskMapper.selectList(wrapper)) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ m.put("id", t.getId());
|
|
|
+ m.put("title", t.getTitle());
|
|
|
+ m.put("description", t.getDescription());
|
|
|
+ m.put("category", t.getCategory());
|
|
|
+ m.put("status", t.getStatus());
|
|
|
+ m.put("createdAt", t.getCreatedAt());
|
|
|
+ tasks.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.setTasks(tasks);
|
|
|
+
|
|
|
+ // 4) 商品桶:公共上架商品(keyword 匹配名称/描述/品类)
|
|
|
+ List<Map<String, Object>> products = new ArrayList<>();
|
|
|
+ if (hasKw) {
|
|
|
+ LambdaQueryWrapper<Product> wrapper = new LambdaQueryWrapper<>();
|
|
|
+ wrapper.eq(Product::getStatus, "on_shelf");
|
|
|
+ wrapper.and(w -> w.like(Product::getName, kw)
|
|
|
+ .or().like(Product::getDescription, kw)
|
|
|
+ .or().like(Product::getProductType, kw));
|
|
|
+ wrapper.orderByDesc(Product::getCreatedAt);
|
|
|
+ wrapper.last("LIMIT " + ((p - 1) * size) + ", " + size);
|
|
|
+ for (Product prod : productMapper.selectList(wrapper)) {
|
|
|
+ Map<String, Object> m = new HashMap<>();
|
|
|
+ m.put("id", prod.getId());
|
|
|
+ m.put("name", prod.getName());
|
|
|
+ m.put("description", prod.getDescription());
|
|
|
+ m.put("productType", prod.getProductType());
|
|
|
+ m.put("price", prod.getPrice());
|
|
|
+ m.put("createdAt", prod.getCreatedAt());
|
|
|
+ products.add(m);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ result.setProducts(products);
|
|
|
+
|
|
|
+ return Result.success(result);
|
|
|
+ }
|
|
|
+}
|