|
|
@@ -51,6 +51,9 @@ public class EnergyService {
|
|
|
@Resource
|
|
|
private HealthReportService healthReportService;
|
|
|
|
|
|
+ @Resource
|
|
|
+ private HealthReportMapper healthReportMapper;
|
|
|
+
|
|
|
@Resource
|
|
|
private FamilyMemberAttributesMapper familyMemberAttributesMapper;
|
|
|
|
|
|
@@ -152,22 +155,31 @@ public class EnergyService {
|
|
|
/**
|
|
|
* 家长 身 — 运动健康类任务完成率
|
|
|
* 注:家长如绑定了孩子(child ID),可通过该孩子的健康报告间接获取身体数据
|
|
|
+ * Optimized: batch-fetch all children's health scores in a single query
|
|
|
*/
|
|
|
private int calcParentBody(User parent) {
|
|
|
- // 尝试通过家庭中任意孩子的健康报告取分(家长关注的是家庭整体健康)
|
|
|
if (parent.getFamilyId() != null) {
|
|
|
List<Child> children = childMapper.selectList(
|
|
|
new LambdaQueryWrapper<Child>()
|
|
|
.eq(Child::getFamilyId, parent.getFamilyId())
|
|
|
);
|
|
|
- for (Child child : children) {
|
|
|
- Integer healthScore = healthReportService.calculateBodyScore(child.getUserId());
|
|
|
- if (healthScore != null) {
|
|
|
- return healthScore;
|
|
|
+ // Batch query: fetch health reports for ALL children at once
|
|
|
+ List<Long> childUserIds = children.stream()
|
|
|
+ .map(Child::getUserId)
|
|
|
+ .collect(Collectors.toList());
|
|
|
+ if (!childUserIds.isEmpty()) {
|
|
|
+ // Single query for all children — picks the first available health score
|
|
|
+ LambdaQueryWrapper<HealthReport> hrWrapper = new LambdaQueryWrapper<HealthReport>()
|
|
|
+ .in(HealthReport::getUserId, childUserIds)
|
|
|
+ .eq(HealthReport::getStatus, "active")
|
|
|
+ .orderByDesc(HealthReport::getReportDate)
|
|
|
+ .last("LIMIT 1");
|
|
|
+ HealthReport report = healthReportMapper.selectOne(hrWrapper);
|
|
|
+ if (report != null && report.getOverallScore() != null) {
|
|
|
+ return report.getOverallScore();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- // 降级:运动健康类任务完成率
|
|
|
return calcTaskCompletionRate(parent.getId(), "parent",
|
|
|
Arrays.asList("运动", "体育", "户外", "健康"), 365);
|
|
|
}
|
|
|
@@ -492,6 +504,7 @@ public class EnergyService {
|
|
|
|
|
|
/**
|
|
|
* 计算指定成员的任务完成率得分 (0-100)
|
|
|
+ * Optimized: single query for all categories instead of N queries per category
|
|
|
*
|
|
|
* @param memberId executorId (家长=userId, 孩子=childId)
|
|
|
* @param memberType 'parent' 或 'child'
|
|
|
@@ -504,26 +517,19 @@ public class EnergyService {
|
|
|
cal.add(Calendar.DAY_OF_YEAR, -daysWindow);
|
|
|
Date since = cal.getTime();
|
|
|
|
|
|
- long total = 0;
|
|
|
- long completed = 0;
|
|
|
-
|
|
|
- for (String category : categories) {
|
|
|
- LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<Task>()
|
|
|
- .eq(Task::getExecutorType, memberType)
|
|
|
- .eq(Task::getExecutorId, memberId)
|
|
|
- .eq(Task::getCategory, category)
|
|
|
- .ne(Task::getIsTemplate, 1)
|
|
|
- .ne(Task::getStatus, "cancelled")
|
|
|
- .gt(Task::getCreatedAt, since);
|
|
|
-
|
|
|
- List<Task> tasks = taskMapper.selectList(wrapper);
|
|
|
- for (Task t : tasks) {
|
|
|
- total++;
|
|
|
- if ("completed".equals(t.getStatus())) {
|
|
|
- completed++;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ // Single query for ALL categories — eliminates N+1
|
|
|
+ LambdaQueryWrapper<Task> wrapper = new LambdaQueryWrapper<Task>()
|
|
|
+ .eq(Task::getExecutorType, memberType)
|
|
|
+ .eq(Task::getExecutorId, memberId)
|
|
|
+ .in(Task::getCategory, categories)
|
|
|
+ .ne(Task::getIsTemplate, 1)
|
|
|
+ .ne(Task::getStatus, "cancelled")
|
|
|
+ .gt(Task::getCreatedAt, since);
|
|
|
+
|
|
|
+ List<Task> tasks = taskMapper.selectList(wrapper);
|
|
|
+
|
|
|
+ long total = tasks.size();
|
|
|
+ long completed = tasks.stream().filter(t -> "completed".equals(t.getStatus())).count();
|
|
|
|
|
|
if (total == 0) return 0;
|
|
|
return (int) Math.round((double) completed / total * 100);
|