2026-06-02-care-family-club-design.md 89 KB

溪福俱乐部 (Care Family Club) 平台升级实现计划

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: 将知心益家 (ZXYJ) 从家庭教育任务管理小程序升级为溪福俱乐部 — 身/心/智/行四维主动健康管理平台,集成Lilishop电商。

Architecture: 增量重构(非重建),保留现有ZXYJ代码作为智(Wisdom)域基础,围绕其新增身(Body)/心(Mind)/行(Action)三个域。前端首页改为四象限矩阵入口,后端按域新增controller包和entity。Lilishop作为独立微服务通过REST API集成。

Tech Stack:

  • 后端: Java 1.8 + Spring Boot 2.7.18 + MyBatis-Plus
  • 前端小程序: uni-app (Vue 2 Options API)
  • Web管理端: Vue 2 + Element UI
  • 数据库: MySQL 8.0 (cfc主库 + sfms只读源库)
  • 认证: JWT Bearer Token
  • 电商: Lilishop (独立微服务, REST API集成)

架构决策

# 决策点 选择 理由
1 Entity组织 A) flat entity/ + 域前缀命名 (BodyHealthMetric, MindMoodLog) 现有49个实体全平铺,最小破坏性,MyBatis-Plus不关心包路径
2 Controller包 B) 新增域包,旧包保留 (新增controller/body/, controller/mind/, controller/action/) 不打断现有功能,增量迁移
3 前端导航 4-tab: 首页(四象限) / 智(任务+成长) / 行(活动+商城) / 我的 微信tabBar最多5项,4项最简洁;身/心通过首页象限入口进入
4 数据库策略 A) DatabaseInitializer.java 遵循"不建索引,不建约束,不建自定义SQL"约束,保持现有模式
5 积分系统 跨域共享PointsService + PointsLog增加domain字段 积分来源扩展到四域,统一管理避免碎片化
6 Lilishop集成 C) 混合模式 — 服务账号(商品浏览) + 委托认证(购物车/结算) 浏览无需用户上下文,交易需用户身份
7 构建顺序 框架→行→身→心→电商 行域有部分代码可扩展,身域较简单,心域最复杂,电商独立最后加

四域内容定义

身 (Body) — 细胞抗衰

功能 说明 新entity
健康指标记录 体重/血压/心率/睡眠 BodyHealthMetric
水营养追踪 每日饮水/营养素摄入 BodyNutritionLog
抗衰评估 细胞抗衰测评+报告 BodyAntiAgingAssessment
运动追踪 运动记录+步数 BodyExerciseLog

心 (Mind) — 心理情绪

功能 说明 新entity
情绪日志 每日心情记录+标签 MindMoodLog
心理测评 专业心理评估问卷 MindPsychologicalAssessment
家庭氛围指数 家庭关系评估 MindFamilyAtmosphereIndex
冥想/放松 引导式冥想计时 MindMeditationSession

智 (Wisdom) — 教育 (现有)

功能 现有entity 状态
任务管理 Task, TaskTemplate, TaskPlanInstance ✅ 已完成
积分系统 PointsLog, StreakMilestone ✅ 已完成
奖励体系 Reward, Wish, RewardWishlist ✅ 已完成
成长档案 GrowthRecord, GrowthPlan ✅ 已完成
DAN测评 DanAssessmentResult, AssessmentOrder ✅ 已完成
小游戏 MiniGame ✅ 已完成
规划师 Guide, GuidePackage, GuideFamily ✅ 已完成

行 (Action) — 关系践行

功能 说明 新entity
活动/事件 线上线下活动组织 ActionActivity, ActionActivityRegistration
内容科普 文章/视频/知识库 ActionArticle, ActionArticleCategory
长者关怀 养老计划+关怀记录 ActionElderCarePlan, ActionElderCareLog
家庭实践 关系践行任务(区别于智域学习任务) ActionPracticeTask
社区互动 家庭间交流 ActionCommunityPost, ActionCommunityComment

文件结构变更

后端新增文件

cfc-backend/src/main/java/com/etotem/cfc/
├── controller/
│   ├── body/                          # NEW
│   │   ├── BodyHealthController.java
│   │   ├── BodyNutritionController.java
│   │   ├── BodyAntiAgingController.java
│   │   └── BodyExerciseController.java
│   ├── mind/                          # NEW
│   │   ├── MindMoodController.java
│   │   ├── MindAssessmentController.java
│   │   ├── MindFamilyAtmosphereController.java
│   │   └── MindMeditationController.java
│   ├── action/                        # NEW
│   │   ├── ActionController.java
│   │   ├── ActionArticleController.java
│   │   ├── ActionElderCareController.java
│   │   └── ActionCommunityController.java
│   └── commerce/                      # NEW (Phase 5)
│       └── CommerceController.java
├── entity/
│   ├── BodyHealthMetric.java          # NEW
│   ├── BodyNutritionLog.java          # NEW
│   ├── BodyAntiAgingAssessment.java   # NEW
│   ├── BodyExerciseLog.java           # NEW
│   ├── MindMoodLog.java               # NEW
│   ├── MindPsychologicalAssessment.java # NEW
│   ├── MindFamilyAtmosphereIndex.java # NEW
│   ├── MindMeditationSession.java     # NEW
│   ├── ActionActivity.java            # NEW
│   ├── ActionActivityRegistration.java # NEW
│   ├── ActionArticle.java             # NEW
│   ├── ActionArticleCategory.java     # NEW
│   ├── ActionElderCarePlan.java       # NEW
│   ├── ActionElderCareLog.java        # NEW
│   ├── ActionPracticeTask.java        # NEW
│   ├── ActionCommunityPost.java       # NEW
│   └── ActionCommunityComment.java    # NEW
├── service/
│   ├── BodyHealthService.java         # NEW
│   ├── BodyNutritionService.java      # NEW
│   ├── BodyAntiAgingService.java      # NEW
│   ├── BodyExerciseService.java       # NEW
│   ├── MindMoodService.java           # NEW
│   ├── MindAssessmentService.java     # NEW
│   ├── MindFamilyAtmosphereService.java # NEW
│   ├── MindMeditationService.java     # NEW
│   ├── ActionActivityService.java     # NEW
│   ├── ActionArticleService.java      # NEW
│   ├── ActionElderCareService.java    # NEW
│   ├── ActionPracticeService.java     # NEW
│   ├── ActionCommunityService.java    # NEW
│   └── LilishopApiClient.java         # NEW (Phase 5)
└── mapper/
    ├── BodyHealthMetricMapper.java    # NEW
    ├── BodyNutritionLogMapper.java     # NEW
    ├── BodyAntiAgingAssessmentMapper.java # NEW
    ├── BodyExerciseLogMapper.java      # NEW
    ├── MindMoodLogMapper.java          # NEW
    ├── MindPsychologicalAssessmentMapper.java # NEW
    ├── MindFamilyAtmosphereIndexMapper.java # NEW
    ├── MindMeditationSessionMapper.java # NEW
    ├── ActionActivityMapper.java       # NEW
    ├── ActionActivityRegistrationMapper.java # NEW
    ├── ActionArticleMapper.java        # NEW
    ├── ActionArticleCategoryMapper.java # NEW
    ├── ActionElderCarePlanMapper.java  # NEW
    ├── ActionElderCareLogMapper.java   # NEW
    ├── ActionPracticeTaskMapper.java   # NEW
    ├── ActionCommunityPostMapper.java  # NEW
    └── ActionCommunityCommentMapper.java # NEW

前端新增/修改文件

cfc-frontend/
├── pages/
│   ├── index/
│   │   ├── index.vue                  # MODIFY - 四象限首页
│   │   ├── parent-index.vue           # MODIFY - 增加四象限入口
│   │   └── child-index.vue            # MODIFY - 增加四象限入口
│   ├── body/                          # NEW
│   │   ├── index.vue                  # 身域首页
│   │   ├── health-metric.vue          # 健康指标记录
│   │   ├── nutrition-log.vue          # 水营养追踪
│   │   ├── anti-aging.vue             # 抗衰评估
│   │   └── exercise-log.vue           # 运动追踪
│   ├── mind/                          # NEW
│   │   ├── index.vue                  # 心域首页
│   │   ├── mood-log.vue               # 情绪日志
│   │   ├── assessment.vue             # 心理测评
│   │   ├── family-atmosphere.vue      # 家庭氛围
│   │   └── meditation.vue             # 冥想
│   ├── action/                        # NEW
│   │   ├── index.vue                  # 行域首页
│   │   ├── activity-list.vue          # 活动列表
│   │   ├── activity-detail.vue        # 活动详情
│   │   ├── article-list.vue           # 文章/科普列表
│   │   ├── article-detail.vue         # 文章详情
│   │   ├── elder-care.vue             # 长者关怀
│   │   └── community.vue              # 社区互动
│   ├── commerce/                      # NEW (Phase 5)
│   │   ├── product-list.vue           # 商品列表
│   │   ├── product-detail.vue         # 商品详情
│   │   ├── cart.vue                   # 购物车
│   │   └── order-list.vue             # 订单列表
│   └── ...                            # 现有页面保留
├── pages.json                         # MODIFY - 新增页面注册 + tabBar调整
├── utils/
│   └── api.js                         # MODIFY - 新增域API封装
└── store/
    └── index.js                       # MODIFY - 增加 domain state

Phase 1: 框架搭建(首页 + 核心实体 + 导航重构)

目标: 建立四域框架,替换首页为四象限入口,创建所有新entity/mapper/service骨架,调整导航结构。此阶段完成后,用户可以看到四象限首页,点击进入各域,但各域功能为空壳。


Task 1: 创建 Body 域 Entity

Files:

  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/BodyHealthMetric.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/BodyNutritionLog.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/BodyAntiAgingAssessment.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/BodyExerciseLog.java

  • [ ] Step 1: 创建 BodyHealthMetric entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    @Data
    @TableName("body_health_metrics")
    public class BodyHealthMetric {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private BigDecimal weight;
    private Integer bloodPressureSystolic;
    private Integer bloodPressureDiastolic;
    private Integer heartRate;
    private Integer sleepHours;
    private Integer sleepQuality;
    private String note;
    private LocalDateTime recordDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 2: 创建 BodyNutritionLog entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    @Data
    @TableName("body_nutrition_logs")
    public class BodyNutritionLog {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private Integer waterMl;
    private Integer calorieIntake;
    private BigDecimal proteinG;
    private BigDecimal vitaminC;
    private BigDecimal vitaminD;
    private String mealType;
    private String note;
    private LocalDateTime recordDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 3: 创建 BodyAntiAgingAssessment entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("body_anti_aging_assessments")
    public class BodyAntiAgingAssessment {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private Long guideId;
    private String assessmentType;
    private Integer cellularAge;
    private Integer biologicalAge;
    private String resultSummary;
    private String resultDetail;
    private String reportUrl;
    private String status;
    private LocalDateTime assessmentDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 4: 创建 BodyExerciseLog entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("body_exercise_logs")
    public class BodyExerciseLog {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private String exerciseType;
    private Integer durationMinutes;
    private Integer steps;
    private Integer caloriesBurned;
    private Integer intensity;
    private String note;
    private LocalDateTime exerciseDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 5: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 2: 创建 Mind 域 Entity

Files:

  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/MindMoodLog.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/MindPsychologicalAssessment.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/MindFamilyAtmosphereIndex.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/MindMeditationSession.java

  • [ ] Step 1: 创建 MindMoodLog entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("mind_mood_logs")
    public class MindMoodLog {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private Integer moodScore;
    private String moodLabel;
    private String tags;
    private String note;
    private String weather;
    private LocalDateTime recordDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 2: 创建 MindPsychologicalAssessment entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("mind_psychological_assessments")
    public class MindPsychologicalAssessment {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private Long guideId;
    private String assessmentType;
    private Integer totalScore;
    private String resultLevel;
    private String resultSummary;
    private String resultDetail;
    private String reportUrl;
    private String status;
    private LocalDateTime assessmentDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 3: 创建 MindFamilyAtmosphereIndex entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("mind_family_atmosphere_indices")
    public class MindFamilyAtmosphereIndex {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Integer communicationScore;
    private Integer emotionalScore;
    private Integer supportScore;
    private Integer conflictScore;
    private Integer overallScore;
    private String note;
    private LocalDateTime recordDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 4: 创建 MindMeditationSession entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("mind_meditation_sessions")
    public class MindMeditationSession {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long userId;
    private String meditationType;
    private Integer durationMinutes;
    private Integer completionRate;
    private String audioUrl;
    private String note;
    private LocalDateTime sessionDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 5: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 3: 创建 Action 域 Entity

Files:

  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionActivity.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionActivityRegistration.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionArticle.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionArticleCategory.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionElderCarePlan.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionElderCareLog.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionPracticeTask.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionCommunityPost.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/entity/ActionCommunityComment.java

  • [ ] Step 1: 创建 ActionActivity entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.math.BigDecimal;
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_activities")
    public class ActionActivity {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long creatorId;
    private String title;
    private String description;
    private String coverUrl;
    private String activityType;
    private String location;
    private BigDecimal latitude;
    private BigDecimal longitude;
    private LocalDateTime startTime;
    private LocalDateTime endTime;
    private Integer maxParticipants;
    private Integer currentParticipants;
    private BigDecimal fee;
    private String status;
    private Integer pointsReward;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 2: 创建 ActionActivityRegistration entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_activity_registrations")
    public class ActionActivityRegistration {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long activityId;
    private Long userId;
    private Long familyId;
    private String status;
    private String note;
    private LocalDateTime registeredAt;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 3: 创建 ActionArticle entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_articles")
    public class ActionArticle {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long authorId;
    private Long categoryId;
    private String title;
    private String summary;
    private String content;
    private String coverUrl;
    private String articleType;
    private Integer viewCount;
    private Integer likeCount;
    private String status;
    private LocalDateTime publishedAt;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 4: 创建 ActionArticleCategory entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_article_categories")
    public class ActionArticleCategory {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String name;
    private String icon;
    private String description;
    private Integer sortOrder;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 5: 创建 ActionElderCarePlan entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_elder_care_plans")
    public class ActionElderCarePlan {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long creatorId;
    private String elderName;
    private Integer elderAge;
    private String healthCondition;
    private String careLevel;
    private String careItems;
    private String emergencyContact;
    private String emergencyPhone;
    private String status;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 6: 创建 ActionElderCareLog entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_elder_care_logs")
    public class ActionElderCareLog {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long planId;
    private Long familyId;
    private Long caregiverId;
    private String careItem;
    private String careDetail;
    private String careResult;
    private LocalDateTime careDate;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 7: 创建 ActionPracticeTask entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_practice_tasks")
    public class ActionPracticeTask {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long familyId;
    private Long creatorId;
    private Long assigneeId;
    private String title;
    private String description;
    private String practiceType;
    private Integer pointsReward;
    private String status;
    private LocalDateTime deadline;
    private LocalDateTime completedAt;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 8: 创建 ActionCommunityPost entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_community_posts")
    public class ActionCommunityPost {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long userId;
    private Long familyId;
    private String title;
    private String content;
    private String imageUrl;
    private String postType;
    private Integer viewCount;
    private Integer likeCount;
    private Integer commentCount;
    private String status;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 9: 创建 ActionCommunityComment entity

    package com.etotem.cfc.entity;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.time.LocalDateTime;
    
    @Data
    @TableName("action_community_comments")
    public class ActionCommunityComment {
    @TableId(type = IdType.AUTO)
    private Long id;
    private Long postId;
    private Long userId;
    private Long parentId;
    private String content;
    private Integer likeCount;
    private LocalDateTime createdAt;
    private LocalDateTime updatedAt;
    }
    
  • [ ] Step 10: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 4: 创建所有 Mapper

Files:

  • Create: 17 mapper files (每个entity对应一个Mapper)

  • [ ] Step 1: 创建 Body 域 Mapper

    // cfc-backend/src/main/java/com/etotem/cfc/mapper/BodyHealthMetricMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.BodyHealthMetric;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface BodyHealthMetricMapper extends BaseMapper<BodyHealthMetric> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/BodyNutritionLogMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.BodyNutritionLog;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface BodyNutritionLogMapper extends BaseMapper<BodyNutritionLog> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/BodyAntiAgingAssessmentMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.BodyAntiAgingAssessment;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface BodyAntiAgingAssessmentMapper extends BaseMapper<BodyAntiAgingAssessment> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/BodyExerciseLogMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.BodyExerciseLog;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface BodyExerciseLogMapper extends BaseMapper<BodyExerciseLog> {}
    
  • [ ] Step 2: 创建 Mind 域 Mapper

    // cfc-backend/src/main/java/com/etotem/cfc/mapper/MindMoodLogMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.MindMoodLog;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface MindMoodLogMapper extends BaseMapper<MindMoodLog> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/MindPsychologicalAssessmentMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.MindPsychologicalAssessment;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface MindPsychologicalAssessmentMapper extends BaseMapper<MindPsychologicalAssessment> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/MindFamilyAtmosphereIndexMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.MindFamilyAtmosphereIndex;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface MindFamilyAtmosphereIndexMapper extends BaseMapper<MindFamilyAtmosphereIndex> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/MindMeditationSessionMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.MindMeditationSession;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface MindMeditationSessionMapper extends BaseMapper<MindMeditationSession> {}
    
  • [ ] Step 3: 创建 Action 域 Mapper

    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionActivityMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionActivity;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionActivityMapper extends BaseMapper<ActionActivity> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionActivityRegistrationMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionActivityRegistration;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionActivityRegistrationMapper extends BaseMapper<ActionActivityRegistration> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionArticleMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionArticle;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionArticleMapper extends BaseMapper<ActionArticle> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionArticleCategoryMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionArticleCategory;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionArticleCategoryMapper extends BaseMapper<ActionArticleCategory> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionElderCarePlanMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionElderCarePlan;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionElderCarePlanMapper extends BaseMapper<ActionElderCarePlan> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionElderCareLogMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionElderCareLog;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionElderCareLogMapper extends BaseMapper<ActionElderCareLog> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionPracticeTaskMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionPracticeTask;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionPracticeTaskMapper extends BaseMapper<ActionPracticeTask> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionCommunityPostMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionCommunityPost;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionCommunityPostMapper extends BaseMapper<ActionCommunityPost> {}
    
    // cfc-backend/src/main/java/com/etotem/cfc/mapper/ActionCommunityCommentMapper.java
    package com.etotem.cfc.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.etotem.cfc.entity.ActionCommunityComment;
    import org.apache.ibatis.annotations.Mapper;
    
    @Mapper
    public interface ActionCommunityCommentMapper extends BaseMapper<ActionCommunityComment> {}
    
  • [ ] Step 4: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 5: DatabaseInitializer 增加新表

Files:

  • Modify: cfc-backend/src/main/java/com/etotem/cfc/config/DatabaseInitializer.java

  • [ ] Step 1: 在 DatabaseInitializer 中添加 17 张新表的 CREATE TABLE 语句

在 DatabaseInitializer 的 init 方法中(现有建表语句之后),添加以下新表创建逻辑。遵循现有模式:CREATE TABLE IF NOT EXISTS + 基本字段 + created_at/updated_at

// === Body 域表 ===
createTableIfNotExists("body_health_metrics", "CREATE TABLE IF NOT EXISTS `body_health_metrics` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`weight` DECIMAL(5,1) DEFAULT NULL COMMENT '体重kg'," +
    "`blood_pressure_systolic` INT DEFAULT NULL COMMENT '收缩压'," +
    "`blood_pressure_diastolic` INT DEFAULT NULL COMMENT '舒张压'," +
    "`heart_rate` INT DEFAULT NULL COMMENT '心率'," +
    "`sleep_hours` INT DEFAULT NULL COMMENT '睡眠时长(小时)'," +
    "`sleep_quality` INT DEFAULT NULL COMMENT '睡眠质量1-5'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`record_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='健康指标记录'");

createTableIfNotExists("body_nutrition_logs", "CREATE TABLE IF NOT EXISTS `body_nutrition_logs` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`water_ml` INT DEFAULT NULL COMMENT '饮水量ml'," +
    "`calorie_intake` INT DEFAULT NULL COMMENT '热量摄入kcal'," +
    "`protein_g` DECIMAL(5,1) DEFAULT NULL COMMENT '蛋白质g'," +
    "`vitamin_c` DECIMAL(5,1) DEFAULT NULL COMMENT '维生素C mg'," +
    "`vitamin_d` DECIMAL(5,1) DEFAULT NULL COMMENT '维生素D ug'," +
    "`meal_type` VARCHAR(20) DEFAULT NULL COMMENT '餐次(breakfast/lunch/dinner/snack)'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`record_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='水营养记录'");

createTableIfNotExists("body_anti_aging_assessments", "CREATE TABLE IF NOT EXISTS `body_anti_aging_assessments` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`guide_id` BIGINT DEFAULT NULL COMMENT '规划师ID'," +
    "`assessment_type` VARCHAR(50) DEFAULT NULL COMMENT '评估类型'," +
    "`cellular_age` INT DEFAULT NULL COMMENT '细胞年龄'," +
    "`biological_age` INT DEFAULT NULL COMMENT '生物学年龄'," +
    "`result_summary` VARCHAR(500) DEFAULT NULL," +
    "`result_detail` TEXT DEFAULT NULL," +
    "`report_url` VARCHAR(500) DEFAULT NULL," +
    "`status` VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/completed'," +
    "`assessment_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='抗衰评估'");

createTableIfNotExists("body_exercise_logs", "CREATE TABLE IF NOT EXISTS `body_exercise_logs` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`exercise_type` VARCHAR(50) NOT NULL COMMENT '运动类型'," +
    "`duration_minutes` INT DEFAULT NULL COMMENT '时长(分钟)'," +
    "`steps` INT DEFAULT NULL COMMENT '步数'," +
    "`calories_burned` INT DEFAULT NULL COMMENT '消耗卡路里'," +
    "`intensity` INT DEFAULT NULL COMMENT '强度1-5'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`exercise_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='运动记录'");

// === Mind 域表 ===
createTableIfNotExists("mind_mood_logs", "CREATE TABLE IF NOT EXISTS `mind_mood_logs` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`mood_score` INT NOT NULL COMMENT '心情分数1-10'," +
    "`mood_label` VARCHAR(20) DEFAULT NULL COMMENT '心情标签(happy/sad/anxious/calm/angry)'," +
    "`tags` VARCHAR(200) DEFAULT NULL COMMENT '自定义标签,分隔'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`weather` VARCHAR(20) DEFAULT NULL COMMENT '天气'," +
    "`record_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='情绪日志'");

createTableIfNotExists("mind_psychological_assessments", "CREATE TABLE IF NOT EXISTS `mind_psychological_assessments` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`guide_id` BIGINT DEFAULT NULL COMMENT '规划师ID'," +
    "`assessment_type` VARCHAR(50) NOT NULL COMMENT '评估类型(SCL90/SDS/SAS等)'," +
    "`total_score` INT DEFAULT NULL," +
    "`result_level` VARCHAR(20) DEFAULT NULL COMMENT 'normal/mild/moderate/severe'," +
    "`result_summary` VARCHAR(500) DEFAULT NULL," +
    "`result_detail` TEXT DEFAULT NULL," +
    "`report_url` VARCHAR(500) DEFAULT NULL," +
    "`status` VARCHAR(20) DEFAULT 'pending'," +
    "`assessment_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='心理测评'");

createTableIfNotExists("mind_family_atmosphere_indices", "CREATE TABLE IF NOT EXISTS `mind_family_atmosphere_indices` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`communication_score` INT DEFAULT NULL COMMENT '沟通评分1-100'," +
    "`emotional_score` INT DEFAULT NULL COMMENT '情感评分1-100'," +
    "`support_score` INT DEFAULT NULL COMMENT '支持评分1-100'," +
    "`conflict_score` INT DEFAULT NULL COMMENT '冲突评分1-100(越高越好=冲突处理越好)'," +
    "`overall_score` INT DEFAULT NULL COMMENT '综合评分1-100'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`record_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='家庭氛围指数'");

createTableIfNotExists("mind_meditation_sessions", "CREATE TABLE IF NOT EXISTS `mind_meditation_sessions` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`meditation_type` VARCHAR(30) DEFAULT NULL COMMENT '冥想类型(breathing/body-scan/guided)'," +
    "`duration_minutes` INT NOT NULL COMMENT '时长(分钟)'," +
    "`completion_rate` INT DEFAULT NULL COMMENT '完成率0-100'," +
    "`audio_url` VARCHAR(500) DEFAULT NULL COMMENT '引导音频URL'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`session_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='冥想记录'");

// === Action 域表 ===
createTableIfNotExists("action_activities", "CREATE TABLE IF NOT EXISTS `action_activities` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`creator_id` BIGINT NOT NULL COMMENT '创建人ID'," +
    "`title` VARCHAR(200) NOT NULL," +
    "`description` TEXT DEFAULT NULL," +
    "`cover_url` VARCHAR(500) DEFAULT NULL COMMENT '封面图'," +
    "`activity_type` VARCHAR(30) DEFAULT NULL COMMENT 'online/offline/hybrid'," +
    "`location` VARCHAR(200) DEFAULT NULL COMMENT '地点'," +
    "`latitude` DECIMAL(10,7) DEFAULT NULL," +
    "`longitude` DECIMAL(10,7) DEFAULT NULL," +
    "`start_time` DATETIME NOT NULL," +
    "`end_time` DATETIME NOT NULL," +
    "`max_participants` INT DEFAULT NULL COMMENT '最大参与人数'," +
    "`current_participants` INT DEFAULT 0," +
    "`fee` DECIMAL(10,2) DEFAULT 0 COMMENT '费用'," +
    "`status` VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/published/cancelled/ended'," +
    "`points_reward` INT DEFAULT 0 COMMENT '参与奖励积分'," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动'");

createTableIfNotExists("action_activity_registrations", "CREATE TABLE IF NOT EXISTS `action_activity_registrations` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`activity_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`family_id` BIGINT NOT NULL," +
    "`status` VARCHAR(20) DEFAULT 'registered' COMMENT 'registered/cancelled/attended'," +
    "`note` VARCHAR(500) DEFAULT NULL," +
    "`registered_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='活动报名'");

createTableIfNotExists("action_articles", "CREATE TABLE IF NOT EXISTS `action_articles` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`author_id` BIGINT NOT NULL," +
    "`category_id` BIGINT DEFAULT NULL," +
    "`title` VARCHAR(200) NOT NULL," +
    "`summary` VARCHAR(500) DEFAULT NULL," +
    "`content` LONGTEXT DEFAULT NULL," +
    "`cover_url` VARCHAR(500) DEFAULT NULL," +
    "`article_type` VARCHAR(30) DEFAULT 'article' COMMENT 'article/video/infographic'," +
    "`view_count` INT DEFAULT 0," +
    "`like_count` INT DEFAULT 0," +
    "`status` VARCHAR(20) DEFAULT 'draft' COMMENT 'draft/published/archived'," +
    "`published_at` DATETIME DEFAULT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章/科普'");

createTableIfNotExists("action_article_categories", "CREATE TABLE IF NOT EXISTS `action_article_categories` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`name` VARCHAR(50) NOT NULL," +
    "`icon` VARCHAR(50) DEFAULT NULL," +
    "`description` VARCHAR(200) DEFAULT NULL," +
    "`sort_order` INT DEFAULT 0," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文章分类'");

createTableIfNotExists("action_elder_care_plans", "CREATE TABLE IF NOT EXISTS `action_elder_care_plans` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`creator_id` BIGINT NOT NULL," +
    "`elder_name` VARCHAR(50) NOT NULL COMMENT '长者姓名'," +
    "`elder_age` INT DEFAULT NULL," +
    "`health_condition` VARCHAR(500) DEFAULT NULL COMMENT '健康状况'," +
    "`care_level` VARCHAR(20) DEFAULT NULL COMMENT 'care-level-1/2/3'," +
    "`care_items` TEXT DEFAULT NULL COMMENT '关怀项目JSON'," +
    "`emergency_contact` VARCHAR(50) DEFAULT NULL COMMENT '紧急联系人'," +
    "`emergency_phone` VARCHAR(20) DEFAULT NULL COMMENT '紧急联系电话'," +
    "`status` VARCHAR(20) DEFAULT 'active' COMMENT 'active/completed/archived'," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长者关怀计划'");

createTableIfNotExists("action_elder_care_logs", "CREATE TABLE IF NOT EXISTS `action_elder_care_logs` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`plan_id` BIGINT NOT NULL," +
    "`family_id` BIGINT NOT NULL," +
    "`caregiver_id` BIGINT NOT NULL COMMENT '照护人ID'," +
    "`care_item` VARCHAR(100) NOT NULL COMMENT '关怀项目'," +
    "`care_detail` TEXT DEFAULT NULL COMMENT '照护详情'," +
    "`care_result` VARCHAR(200) DEFAULT NULL COMMENT '照护结果'," +
    "`care_date` DATETIME NOT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='长者关怀日志'");

createTableIfNotExists("action_practice_tasks", "CREATE TABLE IF NOT EXISTS `action_practice_tasks` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`family_id` BIGINT NOT NULL," +
    "`creator_id` BIGINT NOT NULL," +
    "`assignee_id` BIGINT DEFAULT NULL COMMENT '执行人ID'," +
    "`title` VARCHAR(200) NOT NULL," +
    "`description` VARCHAR(500) DEFAULT NULL," +
    "`practice_type` VARCHAR(30) DEFAULT NULL COMMENT 'relationship/parenting/elder-care/community'," +
    "`points_reward` INT DEFAULT 0 COMMENT '完成积分奖励'," +
    "`status` VARCHAR(20) DEFAULT 'pending' COMMENT 'pending/completed/cancelled'," +
    "`deadline` DATETIME DEFAULT NULL," +
    "`completed_at` DATETIME DEFAULT NULL," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='践行任务'");

createTableIfNotExists("action_community_posts", "CREATE TABLE IF NOT EXISTS `action_community_posts` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`user_id` BIGINT NOT NULL," +
    "`family_id` BIGINT DEFAULT NULL," +
    "`title` VARCHAR(200) NOT NULL," +
    "`content` TEXT NOT NULL," +
    "`image_url` VARCHAR(500) DEFAULT NULL COMMENT '图片URL,分隔多张'," +
    "`post_type` VARCHAR(30) DEFAULT 'sharing' COMMENT 'sharing/question/experience'," +
    "`view_count` INT DEFAULT 0," +
    "`like_count` INT DEFAULT 0," +
    "`comment_count` INT DEFAULT 0," +
    "`status` VARCHAR(20) DEFAULT 'published' COMMENT 'published/hidden/deleted'," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='社区帖子'");

createTableIfNotExists("action_community_comments", "CREATE TABLE IF NOT EXISTS `action_community_comments` (" +
    "`id` BIGINT PRIMARY KEY AUTO_INCREMENT," +
    "`post_id` BIGINT NOT NULL," +
    "`user_id` BIGINT NOT NULL," +
    "`parent_id` BIGINT DEFAULT NULL COMMENT '父评论ID(回复)'," +
    "`content` VARCHAR(500) NOT NULL," +
    "`like_count` INT DEFAULT 0," +
    "`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP," +
    "`updated_at` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP" +
    ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='社区评论'");
  • Step 2: PointsLog 表增加 domain 字段

在 DatabaseInitializer 的现有 points_log 表创建之后,添加 ALTER 语句:

// PointsLog 扩展:增加 domain 字段支持四域积分来源
try {
    jdbcTemplate.execute("ALTER TABLE points_log ADD COLUMN `domain` VARCHAR(20) DEFAULT 'wisdom' COMMENT '积分来源域(body/mind/wisdom/action)'");
} catch (Exception e) {
    // 字段已存在,忽略
    log.info("points_log.domain already exists");
}
  • Step 3: 启动验证

Run: cd cfc-backend && mvn spring-boot:run (确认启动不报错,新表自动创建)

Expected: Application starts successfully, logs show new tables created


Task 6: 创建 Service 骨架

Files:

  • Create: 13 service files (每个域核心service)

  • [ ] Step 1: 创建 Body 域 Service

    // cfc-backend/src/main/java/com/etotem/cfc/service/BodyHealthService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.BodyHealthMetric;
    import com.etotem.cfc.mapper.BodyHealthMetricMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class BodyHealthService {
    
    @Resource
    private BodyHealthMetricMapper bodyHealthMetricMapper;
    
    public BodyHealthMetric createRecord(BodyHealthMetric record) {
        bodyHealthMetricMapper.insert(record);
        return record;
    }
    
    public List<BodyHealthMetric> getRecords(Long userId, Integer limit) {
        return bodyHealthMetricMapper.selectList(
            new LambdaQueryWrapper<BodyHealthMetric>()
                .eq(BodyHealthMetric::getUserId, userId)
                .orderByDesc(BodyHealthMetric::getRecordDate)
                .last("LIMIT " + limit)
        );
    }
    
    public BodyHealthMetric getLatestRecord(Long userId) {
        return bodyHealthMetricMapper.selectOne(
            new LambdaQueryWrapper<BodyHealthMetric>()
                .eq(BodyHealthMetric::getUserId, userId)
                .orderByDesc(BodyHealthMetric::getRecordDate)
                .last("LIMIT 1")
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/BodyNutritionService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.BodyNutritionLog;
    import com.etotem.cfc.mapper.BodyNutritionLogMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class BodyNutritionService {
    
    @Resource
    private BodyNutritionLogMapper bodyNutritionLogMapper;
    
    public BodyNutritionLog createLog(BodyNutritionLog log) {
        bodyNutritionLogMapper.insert(log);
        return log;
    }
    
    public List<BodyNutritionLog> getLogs(Long userId, Integer limit) {
        return bodyNutritionLogMapper.selectList(
            new LambdaQueryWrapper<BodyNutritionLog>()
                .eq(BodyNutritionLog::getUserId, userId)
                .orderByDesc(BodyNutritionLog::getRecordDate)
                .last("LIMIT " + limit)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/BodyAntiAgingService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.BodyAntiAgingAssessment;
    import com.etotem.cfc.mapper.BodyAntiAgingAssessmentMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class BodyAntiAgingService {
    
    @Resource
    private BodyAntiAgingAssessmentMapper bodyAntiAgingAssessmentMapper;
    
    public BodyAntiAgingAssessment createAssessment(BodyAntiAgingAssessment assessment) {
        bodyAntiAgingAssessmentMapper.insert(assessment);
        return assessment;
    }
    
    public List<BodyAntiAgingAssessment> getAssessments(Long userId) {
        return bodyAntiAgingAssessmentMapper.selectList(
            new LambdaQueryWrapper<BodyAntiAgingAssessment>()
                .eq(BodyAntiAgingAssessment::getUserId, userId)
                .orderByDesc(BodyAntiAgingAssessment::getAssessmentDate)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/BodyExerciseService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.BodyExerciseLog;
    import com.etotem.cfc.mapper.BodyExerciseLogMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class BodyExerciseService {
    
    @Resource
    private BodyExerciseLogMapper bodyExerciseLogMapper;
    
    public BodyExerciseLog createLog(BodyExerciseLog log) {
        bodyExerciseLogMapper.insert(log);
        return log;
    }
    
    public List<BodyExerciseLog> getLogs(Long userId, Integer limit) {
        return bodyExerciseLogMapper.selectList(
            new LambdaQueryWrapper<BodyExerciseLog>()
                .eq(BodyExerciseLog::getUserId, userId)
                .orderByDesc(BodyExerciseLog::getExerciseDate)
                .last("LIMIT " + limit)
        );
    }
    }
    
  • [ ] Step 2: 创建 Mind 域 Service

    // cfc-backend/src/main/java/com/etotem/cfc/service/MindMoodService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.MindMoodLog;
    import com.etotem.cfc.mapper.MindMoodLogMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class MindMoodService {
    
    @Resource
    private MindMoodLogMapper mindMoodLogMapper;
    
    public MindMoodLog createLog(MindMoodLog log) {
        mindMoodLogMapper.insert(log);
        return log;
    }
    
    public List<MindMoodLog> getLogs(Long userId, Integer limit) {
        return mindMoodLogMapper.selectList(
            new LambdaQueryWrapper<MindMoodLog>()
                .eq(MindMoodLog::getUserId, userId)
                .orderByDesc(MindMoodLog::getRecordDate)
                .last("LIMIT " + limit)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/MindAssessmentService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.MindPsychologicalAssessment;
    import com.etotem.cfc.mapper.MindPsychologicalAssessmentMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class MindAssessmentService {
    
    @Resource
    private MindPsychologicalAssessmentMapper mindPsychologicalAssessmentMapper;
    
    public MindPsychologicalAssessment createAssessment(MindPsychologicalAssessment assessment) {
        mindPsychologicalAssessmentMapper.insert(assessment);
        return assessment;
    }
    
    public List<MindPsychologicalAssessment> getAssessments(Long userId) {
        return mindPsychologicalAssessmentMapper.selectList(
            new LambdaQueryWrapper<MindPsychologicalAssessment>()
                .eq(MindPsychologicalAssessment::getUserId, userId)
                .orderByDesc(MindPsychologicalAssessment::getAssessmentDate)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/MindFamilyAtmosphereService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.MindFamilyAtmosphereIndex;
    import com.etotem.cfc.mapper.MindFamilyAtmosphereIndexMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class MindFamilyAtmosphereService {
    
    @Resource
    private MindFamilyAtmosphereIndexMapper mindFamilyAtmosphereIndexMapper;
    
    public MindFamilyAtmosphereIndex createIndex(MindFamilyAtmosphereIndex index) {
        mindFamilyAtmosphereIndexMapper.insert(index);
        return index;
    }
    
    public List<MindFamilyAtmosphereIndex> getIndices(Long familyId) {
        return mindFamilyAtmosphereIndexMapper.selectList(
            new LambdaQueryWrapper<MindFamilyAtmosphereIndex>()
                .eq(MindFamilyAtmosphereIndex::getFamilyId, familyId)
                .orderByDesc(MindFamilyAtmosphereIndex::getRecordDate)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/MindMeditationService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.MindMeditationSession;
    import com.etotem.cfc.mapper.MindMeditationSessionMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class MindMeditationService {
    
    @Resource
    private MindMeditationSessionMapper mindMeditationSessionMapper;
    
    public MindMeditationSession createSession(MindMeditationSession session) {
        mindMeditationSessionMapper.insert(session);
        return session;
    }
    
    public List<MindMeditationSession> getSessions(Long userId, Integer limit) {
        return mindMeditationSessionMapper.selectList(
            new LambdaQueryWrapper<MindMeditationSession>()
                .eq(MindMeditationSession::getUserId, userId)
                .orderByDesc(MindMeditationSession::getSessionDate)
                .last("LIMIT " + limit)
        );
    }
    }
    
  • [ ] Step 3: 创建 Action 域 Service

    // cfc-backend/src/main/java/com/etotem/cfc/service/ActionActivityService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.ActionActivity;
    import com.etotem.cfc.entity.ActionActivityRegistration;
    import com.etotem.cfc.mapper.ActionActivityMapper;
    import com.etotem.cfc.mapper.ActionActivityRegistrationMapper;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class ActionActivityService {
    
    @Resource
    private ActionActivityMapper actionActivityMapper;
    
    @Resource
    private ActionActivityRegistrationMapper actionActivityRegistrationMapper;
    
    public ActionActivity createActivity(ActionActivity activity) {
        actionActivityMapper.insert(activity);
        return activity;
    }
    
    public List<ActionActivity> getPublishedActivities(Integer limit) {
        return actionActivityMapper.selectList(
            new LambdaQueryWrapper<ActionActivity>()
                .eq(ActionActivity::getStatus, "published")
                .orderByDesc(ActionActivity::getStartTime)
                .last("LIMIT " + limit)
        );
    }
    
    public ActionActivity getById(Long id) {
        return actionActivityMapper.selectById(id);
    }
    
    @Transactional
    public ActionActivityRegistration register(Long activityId, Long userId, Long familyId) {
        ActionActivityRegistration registration = new ActionActivityRegistration();
        registration.setActivityId(activityId);
        registration.setUserId(userId);
        registration.setFamilyId(familyId);
        registration.setStatus("registered");
        actionActivityRegistrationMapper.insert(registration);
        // Increment current_participants
        ActionActivity activity = actionActivityMapper.selectById(activityId);
        if (activity != null) {
            activity.setCurrentParticipants(activity.getCurrentParticipants() + 1);
            actionActivityMapper.updateById(activity);
        }
        return registration;
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/ActionArticleService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.ActionArticle;
    import com.etotem.cfc.mapper.ActionArticleMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class ActionArticleService {
    
    @Resource
    private ActionArticleMapper actionArticleMapper;
    
    public ActionArticle createArticle(ActionArticle article) {
        actionArticleMapper.insert(article);
        return article;
    }
    
    public List<ActionArticle> getPublishedArticles(Long categoryId, Integer limit) {
        LambdaQueryWrapper<ActionArticle> wrapper = new LambdaQueryWrapper<ActionArticle>()
            .eq(ActionArticle::getStatus, "published")
            .orderByDesc(ActionArticle::getPublishedAt)
            .last("LIMIT " + limit);
        if (categoryId != null) {
            wrapper.eq(ActionArticle::getCategoryId, categoryId);
        }
        return actionArticleMapper.selectList(wrapper);
    }
    
    public ActionArticle getById(Long id) {
        return actionArticleMapper.selectById(id);
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/ActionElderCareService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.ActionElderCarePlan;
    import com.etotem.cfc.entity.ActionElderCareLog;
    import com.etotem.cfc.mapper.ActionElderCarePlanMapper;
    import com.etotem.cfc.mapper.ActionElderCareLogMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class ActionElderCareService {
    
    @Resource
    private ActionElderCarePlanMapper actionElderCarePlanMapper;
    
    @Resource
    private ActionElderCareLogMapper actionElderCareLogMapper;
    
    public ActionElderCarePlan createPlan(ActionElderCarePlan plan) {
        actionElderCarePlanMapper.insert(plan);
        return plan;
    }
    
    public List<ActionElderCarePlan> getPlans(Long familyId) {
        return actionElderCarePlanMapper.selectList(
            new LambdaQueryWrapper<ActionElderCarePlan>()
                .eq(ActionElderCarePlan::getFamilyId, familyId)
                .orderByDesc(ActionElderCarePlan::getCreatedAt)
        );
    }
    
    public ActionElderCareLog createCareLog(ActionElderCareLog log) {
        actionElderCareLogMapper.insert(log);
        return log;
    }
    
    public List<ActionElderCareLog> getCareLogs(Long planId) {
        return actionElderCareLogMapper.selectList(
            new LambdaQueryWrapper<ActionElderCareLog>()
                .eq(ActionElderCareLog::getPlanId, planId)
                .orderByDesc(ActionElderCareLog::getCareDate)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/ActionPracticeService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.ActionPracticeTask;
    import com.etotem.cfc.mapper.ActionPracticeTaskMapper;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class ActionPracticeService {
    
    @Resource
    private ActionPracticeTaskMapper actionPracticeTaskMapper;
    
    public ActionPracticeTask createTask(ActionPracticeTask task) {
        actionPracticeTaskMapper.insert(task);
        return task;
    }
    
    public List<ActionPracticeTask> getTasks(Long familyId) {
        return actionPracticeTaskMapper.selectList(
            new LambdaQueryWrapper<ActionPracticeTask>()
                .eq(ActionPracticeTask::getFamilyId, familyId)
                .orderByDesc(ActionPracticeTask::getCreatedAt)
        );
    }
    }
    
    // cfc-backend/src/main/java/com/etotem/cfc/service/ActionCommunityService.java
    package com.etotem.cfc.service;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.etotem.cfc.entity.ActionCommunityPost;
    import com.etotem.cfc.entity.ActionCommunityComment;
    import com.etotem.cfc.mapper.ActionCommunityPostMapper;
    import com.etotem.cfc.mapper.ActionCommunityCommentMapper;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;
    
    import javax.annotation.Resource;
    import java.util.List;
    
    @Service
    public class ActionCommunityService {
    
    @Resource
    private ActionCommunityPostMapper actionCommunityPostMapper;
    
    @Resource
    private ActionCommunityCommentMapper actionCommunityCommentMapper;
    
    public ActionCommunityPost createPost(ActionCommunityPost post) {
        actionCommunityPostMapper.insert(post);
        return post;
    }
    
    public List<ActionCommunityPost> getPosts(Integer limit) {
        return actionCommunityPostMapper.selectList(
            new LambdaQueryWrapper<ActionCommunityPost>()
                .eq(ActionCommunityPost::getStatus, "published")
                .orderByDesc(ActionCommunityPost::getCreatedAt)
                .last("LIMIT " + limit)
        );
    }
    
    public ActionCommunityComment createComment(ActionCommunityComment comment) {
        actionCommunityCommentMapper.insert(comment);
        // Increment post comment count
        ActionCommunityPost post = actionCommunityPostMapper.selectById(comment.getPostId());
        if (post != null) {
            post.setCommentCount(post.getCommentCount() + 1);
            actionCommunityPostMapper.updateById(post);
        }
        return comment;
    }
    
    public List<ActionCommunityComment> getComments(Long postId) {
        return actionCommunityCommentMapper.selectList(
            new LambdaQueryWrapper<ActionCommunityComment>()
                .eq(ActionCommunityComment::getPostId, postId)
                .orderByAsc(ActionCommunityComment::getCreatedAt)
        );
    }
    }
    
  • [ ] Step 4: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 7: 创建后端 Controller 骨架

Files:

  • Create: cfc-backend/src/main/java/com/etotem/cfc/controller/body/BodyHealthController.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/controller/mind/MindMoodController.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/controller/action/ActionController.java
  • Create: cfc-backend/src/main/java/com/etotem/cfc/controller/action/ActionArticleController.java

  • [ ] Step 1: 创建 BodyHealthController

    package com.etotem.cfc.controller.body;
    
    import com.etotem.cfc.common.Result;
    import com.etotem.cfc.entity.BodyHealthMetric;
    import com.etotem.cfc.service.BodyHealthService;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/body/health")
    public class BodyHealthController {
    
    @Resource
    private BodyHealthService bodyHealthService;
    
    @PostMapping("/create")
    public Result<BodyHealthMetric> createRecord(@RequestBody BodyHealthMetric record) {
        return Result.success(bodyHealthService.createRecord(record));
    }
    
    @PostMapping("/list")
    public Result<List<BodyHealthMetric>> getRecords(@RequestBody Map<String, Object> params) {
        Long userId = Long.valueOf(params.get("userId").toString());
        Integer limit = params.containsKey("limit") ? Integer.valueOf(params.get("limit").toString()) : 30;
        return Result.success(bodyHealthService.getRecords(userId, limit));
    }
    
    @PostMapping("/latest")
    public Result<BodyHealthMetric> getLatestRecord(@RequestBody Map<String, Object> params) {
        Long userId = Long.valueOf(params.get("userId").toString());
        return Result.success(bodyHealthService.getLatestRecord(userId));
    }
    }
    
  • [ ] Step 2: 创建 MindMoodController

    package com.etotem.cfc.controller.mind;
    
    import com.etotem.cfc.common.Result;
    import com.etotem.cfc.entity.MindMoodLog;
    import com.etotem.cfc.service.MindMoodService;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/mind/mood")
    public class MindMoodController {
    
    @Resource
    private MindMoodService mindMoodService;
    
    @PostMapping("/create")
    public Result<MindMoodLog> createLog(@RequestBody MindMoodLog log) {
        return Result.success(mindMoodService.createLog(log));
    }
    
    @PostMapping("/list")
    public Result<List<MindMoodLog>> getLogs(@RequestBody Map<String, Object> params) {
        Long userId = Long.valueOf(params.get("userId").toString());
        Integer limit = params.containsKey("limit") ? Integer.valueOf(params.get("limit").toString()) : 30;
        return Result.success(mindMoodService.getLogs(userId, limit));
    }
    }
    
  • [ ] Step 3: 创建 ActionController (活动+社区)

    package com.etotem.cfc.controller.action;
    
    import com.etotem.cfc.common.Result;
    import com.etotem.cfc.entity.ActionActivity;
    import com.etotem.cfc.entity.ActionActivityRegistration;
    import com.etotem.cfc.service.ActionActivityService;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/action/activity")
    public class ActionController {
    
    @Resource
    private ActionActivityService actionActivityService;
    
    @PostMapping("/create")
    public Result<ActionActivity> createActivity(@RequestBody ActionActivity activity) {
        return Result.success(actionActivityService.createActivity(activity));
    }
    
    @PostMapping("/list")
    public Result<List<ActionActivity>> getActivities(@RequestBody Map<String, Object> params) {
        Integer limit = params.containsKey("limit") ? Integer.valueOf(params.get("limit").toString()) : 20;
        return Result.success(actionActivityService.getPublishedActivities(limit));
    }
    
    @PostMapping("/detail")
    public Result<ActionActivity> getDetail(@RequestBody Map<String, Object> params) {
        Long id = Long.valueOf(params.get("id").toString());
        return Result.success(actionActivityService.getById(id));
    }
    
    @PostMapping("/register")
    public Result<ActionActivityRegistration> register(@RequestBody Map<String, Object> params) {
        Long activityId = Long.valueOf(params.get("activityId").toString());
        Long userId = Long.valueOf(params.get("userId").toString());
        Long familyId = Long.valueOf(params.get("familyId").toString());
        return Result.success(actionActivityService.register(activityId, userId, familyId));
    }
    }
    
  • [ ] Step 4: 创建 ActionArticleController

    package com.etotem.cfc.controller.action;
    
    import com.etotem.cfc.common.Result;
    import com.etotem.cfc.entity.ActionArticle;
    import com.etotem.cfc.service.ActionArticleService;
    import org.springframework.web.bind.annotation.*;
    
    import javax.annotation.Resource;
    import java.util.List;
    import java.util.Map;
    
    @RestController
    @RequestMapping("/action/article")
    public class ActionArticleController {
    
    @Resource
    private ActionArticleService actionArticleService;
    
    @PostMapping("/create")
    public Result<ActionArticle> createArticle(@RequestBody ActionArticle article) {
        return Result.success(actionArticleService.createArticle(article));
    }
    
    @PostMapping("/list")
    public Result<List<ActionArticle>> getArticles(@RequestBody Map<String, Object> params) {
        Long categoryId = params.containsKey("categoryId") ? Long.valueOf(params.get("categoryId").toString()) : null;
        Integer limit = params.containsKey("limit") ? Integer.valueOf(params.get("limit").toString()) : 20;
        return Result.success(actionArticleService.getPublishedArticles(categoryId, limit));
    }
    
    @PostMapping("/detail")
    public Result<ActionArticle> getDetail(@RequestBody Map<String, Object> params) {
        Long id = Long.valueOf(params.get("id").toString());
        return Result.success(actionArticleService.getById(id));
    }
    }
    
  • [ ] Step 5: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 8: PointsLog Entity 增加 domain 字段

Files:

  • Modify: cfc-backend/src/main/java/com/etotem/cfc/entity/PointsLog.java

  • [ ] Step 1: 在 PointsLog.java 中增加 domain 字段

在现有 PointsLog 实体中添加:

private String domain; // 积分来源域: body/mind/wisdom/action,默认 "wisdom"
  • Step 2: 编译验证

Run: cd cfc-backend && mvn compile -q

Expected: BUILD SUCCESS


Task 9: 前端首页四象限重构

Files:

  • Modify: cfc-frontend/pages/index/index.vue
  • Modify: cfc-frontend/pages/index/parent-index.vue
  • Modify: cfc-frontend/pages/index/child-index.vue
  • Modify: cfc-frontend/pages.json
  • Modify: cfc-frontend/utils/api.js
  • Modify: cfc-frontend/store/index.js

  • [ ] Step 1: 修改 pages.json — 注册新页面路径 + 调整tabBar

pages 数组中新增(在现有页面之后):

{
  "path": "pages/body/index",
  "style": { "navigationBarTitleText": "身·细胞抗衰" }
},
{
  "path": "pages/body/health-metric",
  "style": { "navigationBarTitleText": "健康指标" }
},
{
  "path": "pages/body/nutrition-log",
  "style": { "navigationBarTitleText": "水营养追踪" }
},
{
  "path": "pages/body/anti-aging",
  "style": { "navigationBarTitleText": "抗衰评估" }
},
{
  "path": "pages/body/exercise-log",
  "style": { "navigationBarTitleText": "运动记录" }
},
{
  "path": "pages/mind/index",
  "style": { "navigationBarTitleText": "心·心理情绪" }
},
{
  "path": "pages/mind/mood-log",
  "style": { "navigationBarTitleText": "情绪日志" }
},
{
  "path": "pages/mind/assessment",
  "style": { "navigationBarTitleText": "心理测评" }
},
{
  "path": "pages/mind/family-atmosphere",
  "style": { "navigationBarTitleText": "家庭氛围" }
},
{
  "path": "pages/mind/meditation",
  "style": { "navigationBarTitleText": "冥想放松" }
},
{
  "path": "pages/action/index",
  "style": { "navigationBarTitleText": "行·关系践行" }
},
{
  "path": "pages/action/activity-list",
  "style": { "navigationBarTitleText": "活动列表" }
},
{
  "path": "pages/action/activity-detail",
  "style": { "navigationBarTitleText": "活动详情" }
},
{
  "path": "pages/action/article-list",
  "style": { "navigationBarTitleText": "文章科普" }
},
{
  "path": "pages/action/article-detail",
  "style": { "navigationBarTitleText": "文章详情" }
},
{
  "path": "pages/action/elder-care",
  "style": { "navigationBarTitleText": "长者关怀" }
},
{
  "path": "pages/action/community",
  "style": { "navigationBarTitleText": "社区互动" }
}

修改 tabBar(保留4-tab,调整"任务"和"心愿单"为域入口):

"tabBar": {
  "color": "#999999",
  "selectedColor": "#4A90D9",
  "backgroundColor": "#ffffff",
  "borderStyle": "black",
  "list": [
    {
      "pagePath": "pages/index/index",
      "text": "首页",
      "iconPath": "static/tab-home.png",
      "selectedIconPath": "static/tab-home-active.png"
    },
    {
      "pagePath": "pages/tasks/tasks",
      "text": "智·成长",
      "iconPath": "static/tab-wisdom.png",
      "selectedIconPath": "static/tab-wisdom-active.png"
    },
    {
      "pagePath": "pages/rewards/rewards",
      "text": "行·实践",
      "iconPath": "static/tab-action.png",
      "selectedIconPath": "static/tab-action-active.png"
    },
    {
      "pagePath": "pages/profile/profile",
      "text": "我的",
      "iconPath": "static/tab-me.png",
      "selectedIconPath": "static/tab-me-active.png"
    }
  ]
}
  • Step 2: 修改 index.vue — 增加四象限入口组件

parent-index.vuechild-index.vue 的模板中,在现有内容上方添加四象限矩阵区域:

<!-- 四象限入口 -->
<view class="domain-matrix">
  <view class="domain-title">溪福俱乐部 · 身心智行</view>
  <view class="domain-grid">
    <view class="domain-item domain-body" @click="navigateToDomain('body')">
      <view class="domain-icon">🧬</view>
      <view class="domain-name">身 · 细胞抗衰</view>
      <view class="domain-desc">健康指标·运动·抗衰</view>
    </view>
    <view class="domain-item domain-mind" @click="navigateToDomain('mind')">
      <view class="domain-icon">🧠</view>
      <view class="domain-name">心 · 心理情绪</view>
      <view class="domain-desc">情绪·测评·冥想</view>
    </view>
    <view class="domain-item domain-wisdom" @click="navigateToDomain('wisdom')">
      <view class="domain-icon">📚</view>
      <view class="domain-name">智 · 教育成长</view>
      <view class="domain-desc">任务·积分·测评</view>
    </view>
    <view class="domain-item domain-action" @click="navigateToDomain('action')">
      <view class="domain-icon">🤝</view>
      <view class="domain-name">行 · 关系践行</view>
      <view class="domain-desc">活动·科普·关怀</view>
    </view>
  </view>
</view>

parent-index.vuechild-index.vue 的 methods 中添加:

navigateToDomain(domain) {
  const routes = {
    body: '/pages/body/index',
    mind: '/pages/mind/index',
    wisdom: '/pages/tasks/tasks',
    action: '/pages/action/index'
  };
  const url = routes[domain];
  if (domain === 'wisdom') {
    uni.switchTab({ url });
  } else {
    uni.navigateTo({ url });
  }
}

在 style 中添加:

.domain-matrix {
  padding: 20rpx 30rpx;
  background: linear-gradient(135deg, #f5f7fa 0%, #e8ecf1 100%);
  border-radius: 20rpx;
  margin: 20rpx;
}
.domain-title {
  font-size: 32rpx;
  font-weight: bold;
  color: #333;
  text-align: center;
  margin-bottom: 20rpx;
}
.domain-grid {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
}
.domain-item {
  width: 48%;
  padding: 30rpx 20rpx;
  border-radius: 16rpx;
  margin-bottom: 16rpx;
  text-align: center;
  box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
}
.domain-body {
  background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%);
}
.domain-mind {
  background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%);
}
.domain-wisdom {
  background: linear-gradient(135deg, #fccb90 0%, #d57eeb 100%);
}
.domain-action {
  background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.domain-icon {
  font-size: 60rpx;
  margin-bottom: 10rpx;
}
.domain-name {
  font-size: 28rpx;
  font-weight: bold;
  color: #fff;
  margin-bottom: 6rpx;
}
.domain-desc {
  font-size: 22rpx;
  color: rgba(255, 255, 255, 0.85);
}
  • Step 3: 创建各域首页占位页面

创建以下空壳页面(每个域首页只含标题和功能列表入口):

pages/body/index.vue:

<template>
  <view class="domain-page">
    <view class="domain-header domain-header-body">
      <view class="domain-header-icon">🧬</view>
      <view class="domain-header-title">身 · 细胞抗衰</view>
    </view>
    <view class="feature-list">
      <view class="feature-item" @click="navigateTo('/pages/body/health-metric')">
        <text class="feature-name">健康指标记录</text>
        <text class="feature-arrow">›</text>
      </view>
      <view class="feature-item" @click="navigateTo('/pages/body/nutrition-log')">
        <text class="feature-name">水营养追踪</text>
        <text class="feature-arrow">›</text>
      </view>
      <view class="feature-item" @click="navigateTo('/pages/body/anti-aging')">
        <text class="feature-name">抗衰评估</text>
        <text class="feature-arrow">›</text>
      </view>
      <view class="feature-item" @click="navigateTo('/pages/body/exercise-log')">
        <text class="feature-name">运动记录</text>
        <text class="feature-arrow">›</text>
      </view>
    </view>
  </view>
</template>

<script>
export default {
  methods: {
    navigateTo(url) {
      uni.navigateTo({ url });
    }
  }
}
</script>

<style scoped>
.domain-page { padding: 0; }
.domain-header { padding: 40rpx; text-align: center; color: #fff; }
.domain-header-body { background: linear-gradient(135deg, #43e97b 0%, #38f9d7 100%); }
.domain-header-icon { font-size: 80rpx; }
.domain-header-title { font-size: 36rpx; font-weight: bold; margin-top: 10rpx; }
.feature-list { padding: 20rpx 30rpx; }
.feature-item { display: flex; justify-content: space-between; align-items: center; padding: 30rpx; background: #fff; border-radius: 12rpx; margin-bottom: 16rpx; box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.04); }
.feature-name { font-size: 30rpx; color: #333; }
.feature-arrow { font-size: 36rpx; color: #ccc; }
</style>

pages/mind/index.vue: 同上结构,domain-header-mind 样式为 background: linear-gradient(135deg, #a18cd1 0%, #fbc2eb 100%),功能列表为情绪日志/心理测评/家庭氛围/冥想放松。

pages/action/index.vue: 同上结构,domain-header-action 样式为 background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%),功能列表为活动列表/文章科普/长者关怀/社区互动。

  • Step 4: 创建各域子页面占位文件

为每个域的功能页面创建最小占位 vue 文件(仅含标题文本),确保 pages.json 注册的路径都有对应文件。

每个占位页面结构统一:

<template>
  <view class="page-container">
    <view class="placeholder">
      <text class="placeholder-text">【功能名称】 — 即将上线</text>
    </view>
  </view>
</template>
<script>
export default {}
</script>
<style scoped>
.page-container { display: flex; justify-content: center; align-items: center; height: 100vh; background: #f5f7fa; }
.placeholder { text-align: center; }
.placeholder-text { font-size: 28rpx; color: #999; }
</style>

需要创建的占位文件:

  • pages/body/health-metric.vue → "健康指标记录"
  • pages/body/nutrition-log.vue → "水营养追踪"
  • pages/body/anti-aging.vue → "抗衰评估"
  • pages/body/exercise-log.vue → "运动记录"
  • pages/mind/mood-log.vue → "情绪日志"
  • pages/mind/assessment.vue → "心理测评"
  • pages/mind/family-atmosphere.vue → "家庭氛围"
  • pages/mind/meditation.vue → "冥想放松"
  • pages/action/activity-list.vue → "活动列表"
  • pages/action/activity-detail.vue → "活动详情"
  • pages/action/article-list.vue → "文章科普"
  • pages/action/article-detail.vue → "文章详情"
  • pages/action/elder-care.vue → "长者关怀"
  • pages/action/community.vue → "社区互动"

  • [ ] Step 5: 修改 api.js — 增加域 API 封装

cfc-frontend/utils/api.js 末尾添加:

// === Body 域 API ===
export const createBodyHealthMetric = (data) => request('/body/health/create', data);
export const getBodyHealthMetrics = (data) => request('/body/health/list', data);
export const getLatestBodyHealthMetric = (data) => request('/body/health/latest', data);
export const createBodyNutritionLog = (data) => request('/body/nutrition/create', data);
export const getBodyNutritionLogs = (data) => request('/body/nutrition/list', data);
export const createBodyAntiAgingAssessment = (data) => request('/body/anti-aging/create', data);
export const getBodyAntiAgingAssessments = (data) => request('/body/anti-aging/list', data);
export const createBodyExerciseLog = (data) => request('/body/exercise/create', data);
export const getBodyExerciseLogs = (data) => request('/body/exercise/list', data);

// === Mind 域 API ===
export const createMindMoodLog = (data) => request('/mind/mood/create', data);
export const getMindMoodLogs = (data) => request('/mind/mood/list', data);
export const createMindAssessment = (data) => request('/mind/assessment/create', data);
export const getMindAssessments = (data) => request('/mind/assessment/list', data);
export const createMindFamilyAtmosphere = (data) => request('/mind/atmosphere/create', data);
export const getMindFamilyAtmosphere = (data) => request('/mind/atmosphere/list', data);
export const createMindMeditation = (data) => request('/mind/meditation/create', data);
export const getMindMeditations = (data) => request('/mind/meditation/list', data);

// === Action 域 API ===
export const createActionActivity = (data) => request('/action/activity/create', data);
export const getActionActivities = (data) => request('/action/activity/list', data);
export const getActionActivityDetail = (data) => request('/action/activity/detail', data);
export const registerActionActivity = (data) => request('/action/activity/register', data);
export const createActionArticle = (data) => request('/action/article/create', data);
export const getActionArticles = (data) => request('/action/article/list', data);
export const getActionArticleDetail = (data) => request('/action/article/detail', data);
export const createActionElderCarePlan = (data) => request('/action/elder-care/create', data);
export const getActionElderCarePlans = (data) => request('/action/elder-care/list', data);
export const createActionPracticeTask = (data) => request('/action/practice/create', data);
export const getActionPracticeTasks = (data) => request('/action/practice/list', data);
export const createActionCommunityPost = (data) => request('/action/community/create-post', data);
export const getActionCommunityPosts = (data) => request('/action/community/posts', data);
export const createActionCommunityComment = (data) => request('/action/community/create-comment', data);
export const getActionCommunityComments = (data) => request('/action/community/comments', data);
  • Step 6: 修改 store/index.js — 增加 domain 状态

在 Vuex state 中添加:

currentDomain: null, // 'body' | 'mind' | 'wisdom' | 'action' — 当前活跃域

在 mutations 中添加:

setCurrentDomain(state, domain) {
  state.currentDomain = domain;
}
  • [ ] Step 7: 提交 Phase 1 框架代码

    git add -A
    git commit -m "feat: 溪福俱乐部四域框架搭建 - 17个新entity/mapper/service - 四象限首页 - 域导航骨架"
    

Phase 2: 行 (Action) 域功能实现

目标: 完善行域所有功能页面(活动、文章、长者关怀、社区、践行任务),建立域内完整业务闭环。

注: Phase 2-4 每个域的具体功能实现计划将在 Phase 1 完成后细化。每个域需要:

  1. 完善后端 Controller + Service 业务逻辑(CRUD + 业务规则)
  2. 实现前端功能页面(替换占位页面)
  3. 接入积分系统(域内行为可获积分)
  4. 管理端页面(cfc-web 内容管理)

行域功能清单

功能 后端Controller 前端页面 优先级
活动发布/列表/报名/详情 ActionController activity-list, activity-detail P0
文章发布/列表/详情 ActionArticleController article-list, article-detail P0
长者关怀计划/日志 ActionElderCareController(新增) elder-care P1
社区帖子/评论 ActionCommunityController(新增) community P1
践行任务CRUD ActionPracticeController(新增) - (首页卡片) P1

Phase 3: 身 (Body) 域功能实现

目标: 完善身域所有功能页面(健康指标、水营养、抗衰评估、运动记录),建立健康管理闭环。

身域功能清单

功能 后端Controller 前端页面 优先级
健康指标CRUD + 趋势图 BodyHealthController health-metric P0
水营养追踪 + 统计 BodyNutritionController(新增) nutrition-log P0
抗衰评估 + 报告 BodyAntiAgingController(新增) anti-aging P1
运动记录 + 步数 BodyExerciseController(新增) exercise-log P1

Phase 4: 心 (Mind) 域功能实现

目标: 完善心域所有功能页面(情绪日志、心理测评、家庭氛围、冥想),建立心理健康管理闭环。

心域功能清单

功能 后端Controller 前端页面 优先级
情绪日志CRUD + 趋势 MindMoodController mood-log P0
心理测评问卷+报告 MindAssessmentController(新增) assessment P0
家庭氛围评估 MindFamilyAtmosphereController(新增) family-atmosphere P1
冥想计时+引导音频 MindMeditationController(新增) meditation P1

Phase 5: Lilishop 电商集成

目标: 通过 REST API 集成 Lilishop 电商功能,实现商品浏览、购物车、下单结算。

电商集成清单

功能 后端实现 前端页面 优先级
LilishopApiClient LilishopApiClient.java (RestTemplate) - P0
商品浏览/搜索 CommerceController → Lilishop buyer-api product-list, product-detail P0
购物车 CommerceController → Lilishop buyer-api cart P0
下单/支付 CommerceController → Lilishop buyer-api checkout P1
订单管理 CommerceController → Lilishop buyer-api order-list P1
商品管理(管理端) CommerceAdminController → Lilishop seller-api cfc-web 商品管理页 P2

Lilishop 配置

application.yml 中添加:

lilishop:
  buyer-api: http://localhost:8888
  seller-api: http://localhost:8889
  manager-api: http://localhost:8887
  service-account:
    username: cfc_service
    password: ${LILISHOP_SERVICE_PASSWORD}
  token-cache-minutes: 50

Phase 6: 管理端 (cfc-web) 扩展

目标: 在 Web 管理端新增四域内容管理页面。

管理端新增页面

路由 页面 功能
/body/health BodyHealth.vue 健康指标数据管理
/body/anti-aging BodyAntiAging.vue 抗衰评估管理
/mind/assessment MindAssessment.vue 心理测评管理
/mind/atmosphere MindAtmosphere.vue 家庭氛围数据
/action/activity ActionActivity.vue 活动发布管理
/action/article ActionArticle.vue 文章内容管理
/action/community ActionCommunity.vue 社区内容审核
/commerce/products CommerceProducts.vue 商品管理(Lilishop)
/commerce/orders CommerceOrders.vue 订单管理(Lilishop)

自检清单

1. Spec 覆盖度

  • 身域: 健康指标、水营养、抗衰评估、运动 → Task 1, 6, 7, Phase 3
  • 心域: 情绪日志、心理测评、家庭氛围、冥想 → Task 2, 6, 7, Phase 4
  • 智域: 现有功能保留 → Task 9 (首页入口), 不改动现有代码
  • 行域: 活动、文章、长者关怀、社区、践行 → Task 3, 6, 7, Phase 2
  • 首页四象限 → Task 9
  • Lilishop集成 → Phase 5
  • 管理端 → Phase 6
  • 跨域积分 → Task 5 (PointsLog.domain), Task 8

2. 占位符扫描

  • 无 TBD/TODO/待定 — Phase 2-5 的具体实现将在各Phase开始时细化
  • 所有 Phase 1 步骤包含完整代码

3. 类型一致性

  • Entity字段名 ↔ Mapper ↔ Service ↔ Controller 参数名一致
  • 前端 api.js 路径 ↔ 后端 @RequestMapping 路径一致
  • pages.json 路径 ↔ 实际文件路径一致