Spring Boot 2.7.18 后端服务,MyBatis-Plus ORM,JWT 认证。
| Task | Location | Notes |
|---|---|---|
| REST API 端点 | src/main/java/com/etotem/cfc/controller/ |
按功能分组: admin/, auth/, assessment/, family/, guide/, task/, reward/ |
| 业务逻辑 | src/main/java/com/etotem/cfc/service/ |
Service 命名: XxxService.java |
| 数据实体 | src/main/java/com/etotem/cfc/entity/ |
使用 MyBatis-Plus 注解 |
| MyBatis Mapper | src/main/java/com/etotem/cfc/mapper/ |
接口文件,与XML映射对应 |
| SQL 映射文件 | src/main/resources/mapper/ |
MyBatis XML 文件 |
| 配置类 | src/main/java/com/etotem/cfc/config/ |
JWT, Security, Swagger, DatabaseInitializer |
| DTO传输对象 | src/main/java/com/etotem/cfc/dto/ |
请求/响应数据结构 |
| 数据迁移 | src/main/java/com/etotem/cfc/service/DataMigrationService.java |
sfms → cfc |
| Symbol | Type | Location | Role |
|---|---|---|---|
AuthController |
Controller | controller/auth/AuthController.java |
微信登录、JWT 认证 |
TaskService |
Service | service/TaskService.java |
任务业务逻辑核心 |
DanAssessmentController |
Controller | controller/DanAssessmentController.java |
DAN测评全链路(订单/预约/支付/结果) |
AssessmentAppointmentController |
Controller | controller/assessment/AssessmentAppointmentController.java |
测评预约管理 |
DataMigrationService |
Service | service/DataMigrationService.java |
sfms → cfc 全量数据迁移 |
User |
Entity | entity/User.java |
用户实体 (parent/child/teacher) |
Task |
Entity | entity/Task.java |
任务实体 |
DanAssessmentResult |
Entity | entity/DanAssessmentResult.java |
DAN测评结果(含孩子快照字段) |
DatabaseInitializer |
Config | config/DatabaseInitializer.java |
数据库迁移和初始化 |
SfmsDataSourceConfig |
Config | config/SfmsDataSourceConfig.java |
sfms 多数据源配置 |
@TableName、@TableId(type = IdType.AUTO)Result<T> 返回,包含 code/message/dataAuthorization: Bearer {token}Controller 结尾,Service 以 Service 结尾@Resource 代替 @Autowired,字段名必须与类型默认 Bean Name 一致(如 PaymentService 的字段名必须为 paymentService)@PostMapping(POST 方法),禁止使用 @GetMapping/@PutMapping/@DeleteMappingdocs/superpowers/api/API_REFERENCE.md,确认:
docs/superpowers/api/API_REFERENCE.md 同步记录废弃接口处理: 标记 @Deprecated + 返回 Result.error(410, "该接口已废弃,请使用 xxx"),待确认无调用后清理
grep -rn '@GetMapping\|@PostMapping\|@PutMapping\|@DeleteMapping' src/main/java/com/etotem/cfc/controller/ | grep -oP '@\w+Mapping\("\K[^"]*' | sort -u
Bean名冲突检查: 新增 Controller/Service 时,确保类名不与其他包中的类重名(Spring 默认 Bean Name 为类名首字母小写)
身份标识: userId=登录账号(users.id)、memberId/familyMemberId/currentMemberId=家庭成员(family_members.id)、childId=遗留表(children.id);账号自身/交易/权限判断用 userId,成员/孩子数据(任务/健康/成长/关系/五维能量)用 memberId;JwtInterceptor 按 userId+familyId 自动反查 currentMemberId(始终是登录账号自己),切换视角不改变 currentMemberId,靠前端显式传 memberId 参数覆盖;新功能禁止新增 childId 引用(详见 docs/architecture/userid-vs-memberid-usage-guide.md)
family_members.userId 非空且关联 User 有 openid(VO 层 memberType=login)family_members.userId 为空或关联 User 无 openid(VO 层 memberType=local),由同家庭能登录用户代管其数据(任务/健康/DOA 等)children 表已删除(数据由 DatabaseInitializer 迁移322 迁移至 family_members 后 DROP),相关实体/Mapper 已清理application.yml@ts-ignore、as any 绕过类型检查@Resource(name="xxx") 与字段名不一致的注入方式,保持字段名与类型默认 Bean Name 一致@GetMapping/@PutMapping/@DeleteMapping — 统一使用 @PostMappingnew XxxMapper() — Mapper 必须通过 @Resource 注入awk '!seen[$0]++' 对 Java 源文件去重 — 会删除合法的重复行(如多个方法的 } 闭合括号)role 字段分流,家长端可切孩子端,切换过来的孩子端可退出切换,即时出需要密码。直接登录的孩子端不能切换到家长 端PointsServicespring.datasource.* + sfms 源库 sfms.datasource.*(只读)当出现 Unknown column 'xxx' in 'field list' 等字段不存在错误,或需要进行表结构变更时,按以下流程处理:
错误日志定位 → 查找实体类 → 检查 schema.sql → 添加迁移 → 同步 schema.sql → 编译验证
后端日志中出现类似以下错误:
Unknown column 'xxx' in 'field list'
Table 'xxx' doesn't exist
从错误信息中提取:表名、列名/对象名,并从异常堆栈找到触发点(哪个 Service/Controller)。
entity/ 目录找到对应实体类,确认字段已定义(@TableField 或成员变量)src/main/resources/schema.sql 中找到该表的 CREATE TABLE 语句,确认列是否已包含| 情况 | 修复方式 |
|---|---|
| schema.sql 建表语句有该列,但生产库没有(旧表) | 只需在 DatabaseInitializer.runMigrations() 添加 ALTER TABLE |
| schema.sql 没有该列,实体也没有 | 在实体添加字段 + schema.sql 建表语句补列 + DatabaseInitializer 加迁移 |
| 全新表 | schema.sql 添加 CREATE TABLE + DatabaseInitializer 的 runMigrations() 加迁移 |
| 仅调整约束/类型/默认值 | DatabaseInitializer.runMigrations() 加 MODIFY COLUMN |
DatabaseInitializer.java 的 runMigrations() 方法是所有 DDL 迁移的唯一入口。
ensureColumn(table, column, definition) 是已有的辅助方法(第 4099 行),自动处理重复列异常:
// 迁移N: xxx表添加yyy字段
ensureColumn("table_name", "column_name", "VARCHAR(50) COMMENT '字段说明'");
ensureColumn 不可用时(如需要 MODIFY / 多个列在同一个 try 块),使用标准模式:
// 迁移N: xxx表添加yyy字段(原因描述/需求编号)
try {
jdbcTemplate.execute("ALTER TABLE xxx ADD COLUMN yyy VARCHAR(50) COMMENT '字段说明'");
log.info("已添加yyy列到xxx表");
} catch (Exception e) {
// 列已存在,忽略错误
}
// 迁移N: 创建 xxx 表(功能描述)
try {
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS xxx (" +
"id BIGINT AUTO_INCREMENT PRIMARY KEY, " +
"...字段定义..." +
"INDEX idx_xxx (column)" +
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表注释'");
log.info("已创建xxx表");
} catch (Exception e) {
// 表已存在,忽略错误
}
try {
jdbcTemplate.execute("ALTER TABLE xxx ADD INDEX idx_xxx (column)");
log.info("已添加idx_xxx索引到xxx表");
} catch (Exception e) {
// 索引已存在,忽略错误
}
try {
jdbcTemplate.execute("ALTER TABLE xxx MODIFY COLUMN yyy VARCHAR(100) COMMENT '新注释'");
log.info("已修改xxx.yyy字段");
} catch (Exception e) {
// 修改失败,忽略错误
}
schema.sql 中保持该表最新的完整定义:
schema.sql 对应表的 CREATE TABLE 语句中也加上该列定义(保持建表即包含所有列)schema.sql 追加该表的 CREATE TABLE IF NOT EXISTS 语句CREATE TABLE IF NOT EXISTS 创建的表:也追加到 schema.sql(保持 schema.sql 为完整定义快照)mvn clean compile # 编译验证通过即可
迁移代码是幂等的(try-catch 忽略已存在的列/表/索引),无需运行时验证。
迁移N,在文件末尾搜索 // 迁移 确认最新编号)// 迁移N: 表名 操作说明(原因/需求编号)log.info(),失败统一用空 catch 或 // 忽略ensureColumn() 添加列,它自动处理重复列异常;原始 try-catch 模式用于索引/表/非标准操作mvn clean compile # 编译验证(唯一验证方式)
mvn spring-boot:run # 启动开发服务器 (localhost:9082)
mvn test # 运行测试 (目前只有1个测试类)
cfc-langgraph Python 服务(FastAPI + LangGraph + ChromaDB RAG,端口 9000,部署到 ai.etotem.com.cn)。AiGateway 调用(熔断 + 超时 + Fallback),禁止绕过 AiGateway 直接对接第三方 LLM。app/graphs/ 或 src/ 模块)。application.yml 的 langgraph.base-url(报告解析)/ python.base-url(通用 AI)。cd cfc-langgraph && pip install -r requirements.txt && uvicorn src.app:app --port 9000
## 工作流程
1. 修改代码后执行 `mvn clean compile` 验证编译通过
2. 编译通过即视为验证完成,不做运行时/启动验证
## NOTES
- 数据库:MySQL 8.0,**测试库**地址 `192.168.16.251:3306`(库名可能为 `zxyj`,参见根 AGENTS.md;该地址只是测试环境,**不是生产库**,生产连接信息需向运维确认)
- 后端端口:9082,JWT 密钥在 `application.yml`
- 街道数据:使用静态种子数据,街道匹配有回退机制
- API冲突已修复:检查所有Controller确保无重复路径映射
- sfms 源库连接信息在 `application.yml` 的 `sfms.datasource.*`
- 数据迁移 POST `/api/migration/run`
## 后端接口规范
- 后台/api的接口规范都用post方式
## 家庭成员列表接口规范
### 统一接口:`POST /api/family/member/list`
**唯一入口**,所有获取家庭成员信息的需求必须调用此接口。
**替代(已废弃,请勿新增调用):**
- `POST /api/family/user/family-members` — 返回 Map 格式,信息分散在 parents + children 两块
- `POST /api/family/user/members/visible` — 只返回 `show_to_family=1` 的部分
- `POST /api/family/user/children/list` — 只返回孩子,不包含家长
- `POST /api/user/children/list` — 同上,重复路由
**请求:**
json // 可选的过滤参数 { "visibleOnly": true, // 仅返回 show_to_family=1 的成员(默认false) "includeFamily": true // 是否包含家庭信息(familyId/name/inviteCode,默认false) }
**返回:** `Result<List<FamilyMemberVO>>`
**`FamilyMemberVO` 字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | Long | family_members.id |
| `source` | String | `family_member` 或 `parent` |
| `userId` | Long | 关联的用户ID |
| `nickname` | String | 昵称 |
| `phone` | String | 手机号 |
| `avatar` | String | 头像 |
| `gender` | String | male/female |
| `age` | Integer | 年龄(由 birthday 实时计算) |
| `effectiveRole` | String | parent/child/teacher |
| `roleLabel` | String | 关系标签(如"爸爸""妈妈""爷爷""奶奶") |
| `generation` | Integer | 辈分值 |
| `isSpouse` | Boolean | 是否配偶 |
| `isAdmin` | Boolean | 是否家庭管理员 |
| `isSelf` | Boolean | 是否当前用户自己 |
| `totalPoints` | Integer | 总积分 |
| `showToFamily` | Integer | 对家庭成员可见性(1=可见, 0=不可见) |
| `trustScore` | BigDecimal | 信任度 0-100 |
| `intimacyScore` | BigDecimal | 亲密度 0-100 |
| `communicationScore` | BigDecimal | 沟通质量 0-100 |
| `memberType` | String | 成员类型(parent/child) |
**后端实现:**
- Controller: `FamilyMembersController.listMembers()` → `FamilyMemberService.listMembers()`
- 传入 `visibleOnly=true` 时,调用 `FamilyMemberService.listMembers()` 后按 `show_to_family` 过滤
- 不传 `familyMemberId` 参数,根据 JWT 的 userId 自动获取用户的 `familyId`
**前端调用:**
js import { getFamilyMemberList } from '@/utils/api.js' // 已封装为:request('/api/family/member/list', 'POST', { visibleOnly: true }) ```
新增成员相关接口:
| 功能 | 路径 | 说明 |
|------|------|------|
| 添加成员 | POST /api/family/member/add | 创建新家庭成员 |
| 更新成员 | POST /api/family/member/update | 修改成员信息 |
| 踢出成员 | POST /api/family/member/kick | 删除成员 |
| 切换视角 | POST /api/family/member/switch | 切换到指定成员视图 |
| 检查编辑 | POST /api/family/member/editable | 检查成员是否可编辑 |
| 变更日志 | POST /api/family/member/logs | 成员变更历史 |