|
@@ -559,9 +559,9 @@ public class ProfileComputeServiceImpl implements ProfileComputeService {
|
|
|
LambdaQueryWrapper<PointsLog> pqw = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<PointsLog> pqw = new LambdaQueryWrapper<>();
|
|
|
pqw.eq(PointsLog::getFamilyMemberId, memberId)
|
|
pqw.eq(PointsLog::getFamilyMemberId, memberId)
|
|
|
.ge(PointsLog::getCreatedAt, java.sql.Timestamp.valueOf(sevenDaysAgo.atStartOfDay()))
|
|
.ge(PointsLog::getCreatedAt, java.sql.Timestamp.valueOf(sevenDaysAgo.atStartOfDay()))
|
|
|
- .eq(PointsLog::getAmount, e -> true); // positive amounts only
|
|
|
|
|
- long pointsEarned = pointsLogMapper.selectList(pqw).stream()
|
|
|
|
|
- .filter(p -> p.getAmount() != null && p.getAmount() > 0)
|
|
|
|
|
|
|
+ .gt(PointsLog::getAmount, 0);
|
|
|
|
|
+ List<PointsLog> pointsList = pointsLogMapper.selectList(pqw);
|
|
|
|
|
+ long pointsEarned = pointsList.stream()
|
|
|
.mapToInt(PointsLog::getAmount).sum();
|
|
.mapToInt(PointsLog::getAmount).sum();
|
|
|
m.put("points_velocity_7d", pointsEarned);
|
|
m.put("points_velocity_7d", pointsEarned);
|
|
|
return m;
|
|
return m;
|
|
@@ -897,30 +897,6 @@ import java.util.*;
|
|
|
@Service
|
|
@Service
|
|
|
public class RecommendServiceImpl implements RecommendService {
|
|
public class RecommendServiceImpl implements RecommendService {
|
|
|
|
|
|
|
|
- // 规则映射:标签code -> 匹配的Content查询条件
|
|
|
|
|
- private static final Map<String, RecommendRule> RULES = new LinkedHashMap<>();
|
|
|
|
|
- static {
|
|
|
|
|
- RULES.put("sleep_deficit", new RecommendRule("sleep", 0.8,
|
|
|
|
|
- q -> q.like(Article::getTags, "睡眠").or().like(Article::getTags, "助眠"),
|
|
|
|
|
- q -> q.eq(Activity::getDimensionCode, "body").or().like(Activity::getDescription, "睡眠")));
|
|
|
|
|
- RULES.put("low_activity", new RecommendRule("action", 0.7,
|
|
|
|
|
- q -> q.like(Task::getCategory, "运动").or().eq(Task::getDimensionCode, "body"),
|
|
|
|
|
- null));
|
|
|
|
|
- RULES.put("high_stress", new RecommendRule("mind", 0.85,
|
|
|
|
|
- q -> q.like(Article::getTags, "压力").or().like(Article::getTags, "情绪管理"),
|
|
|
|
|
- q -> q.eq(Activity::getDimensionCode, "mind").or().like(Activity::getTitle, "冥想")));
|
|
|
|
|
- RULES.put("low_mood", new RecommendRule("mind", 0.9,
|
|
|
|
|
- q -> q.like(Article::getTags, "情绪").or().like(Article::getTags, "心理"),
|
|
|
|
|
- q -> q.eq(Activity::getDimensionCode, "mind")));
|
|
|
|
|
- RULES.put("attention_weak", new RecommendRule("wisdom", 0.85,
|
|
|
|
|
- q -> q.like(Article::getTags, "注意力").or().like(Article::getTags, "专注力"),
|
|
|
|
|
- q -> q.eq(Activity::getDimensionCode, "wisdom").or().like(Activity::getTitle, "注意力")),
|
|
|
|
|
- null); // task已包含
|
|
|
|
|
- RULES.put("task_avoidance", new RecommendRule("action", 0.75,
|
|
|
|
|
- q -> q.like(Task::getCategory, "入门").or().eq(Task::getDifficultyLevel, 1),
|
|
|
|
|
- null));
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
@Autowired private ProfileSnapshotMapper snapshotMapper;
|
|
@Autowired private ProfileSnapshotMapper snapshotMapper;
|
|
|
@Autowired private ArticleMapper articleMapper;
|
|
@Autowired private ArticleMapper articleMapper;
|
|
|
@Autowired private ActivityMapper activityMapper;
|
|
@Autowired private ActivityMapper activityMapper;
|
|
@@ -985,8 +961,8 @@ public class RecommendServiceImpl implements RecommendService {
|
|
|
for (String tag : tags) {
|
|
for (String tag : tags) {
|
|
|
LambdaQueryWrapper<Task> qw = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<Task> qw = new LambdaQueryWrapper<>();
|
|
|
qw.like(Task::getCategory, tag).or().like(Task::getDescription, tag)
|
|
qw.like(Task::getCategory, tag).or().like(Task::getDescription, tag)
|
|
|
- .eq(Task::getStatus, "active")
|
|
|
|
|
- .orderByAsc(Task::getSortOrder)
|
|
|
|
|
|
|
+ .eq(Task::getStatus, "pending")
|
|
|
|
|
+ .
|
|
|
.last("LIMIT 5");
|
|
.last("LIMIT 5");
|
|
|
List<Task> tasks = taskMapper.selectList(qw);
|
|
List<Task> tasks = taskMapper.selectList(qw);
|
|
|
for (Task t : tasks) {
|
|
for (Task t : tasks) {
|
|
@@ -1052,7 +1028,7 @@ public class RecommendServiceImpl implements RecommendService {
|
|
|
private List<Map<String, Object>> queryProducts(Set<String> tags) {
|
|
private List<Map<String, Object>> queryProducts(Set<String> tags) {
|
|
|
List<Map<String, Object>> list = new ArrayList<>();
|
|
List<Map<String, Object>> list = new ArrayList<>();
|
|
|
LambdaQueryWrapper<Product> qw = new LambdaQueryWrapper<>();
|
|
LambdaQueryWrapper<Product> qw = new LambdaQueryWrapper<>();
|
|
|
- qw.eq(Product::getStatus, "on_sale")
|
|
|
|
|
|
|
+ qw.eq(Product::getStatus, "on_shelf")
|
|
|
.isNotNull(Product::getGrowthCategory)
|
|
.isNotNull(Product::getGrowthCategory)
|
|
|
.last("LIMIT 8");
|
|
.last("LIMIT 8");
|
|
|
List<Product> products = productMapper.selectList(qw);
|
|
List<Product> products = productMapper.selectList(qw);
|
|
@@ -1482,6 +1458,6 @@ git push origin cfclub
|
|
|
1. **异步触发画像计算**:使用 `new Thread(...)` 避免阻塞接口响应,生产环境建议替换为 Spring 的 `@Async` 或消息队列
|
|
1. **异步触发画像计算**:使用 `new Thread(...)` 避免阻塞接口响应,生产环境建议替换为 Spring 的 `@Async` 或消息队列
|
|
|
2. **画像计算幂等性**:`computeAndSave` 内部用 upsert,重复调用不会产生脏数据
|
|
2. **画像计算幂等性**:`computeAndSave` 内部用 upsert,重复调用不会产生脏数据
|
|
|
3. **历史数据清理**:`cleanOldHistory` 只在写入新记录时顺带清理,不单独定时任务
|
|
3. **历史数据清理**:`cleanOldHistory` 只在写入新记录时顺带清理,不单独定时任务
|
|
|
-4. **规则可扩展**:`RULES` Map 集中在 `RecommendServiceImpl` 顶部,后续添加新规则只需在此增改
|
|
|
|
|
|
|
+4. **规则可扩展**:推荐匹配逻辑集中在 `RecommendServiceImpl.getRecommendations()` 中,后续添加新规则只需在此增改 if 分支
|
|
|
5. **前端 no `?.` 语法**:小程序禁止可选链,用 `&&` 替代
|
|
5. **前端 no `?.` 语法**:小程序禁止可选链,用 `&&` 替代
|
|
|
6. **日期格式**:统一使用 `yyyy-MM-dd` 或 ISO 8601,禁止 `toLocaleString()`
|
|
6. **日期格式**:统一使用 `yyyy-MM-dd` 或 ISO 8601,禁止 `toLocaleString()`
|