版本:v1.1(✅ 已确认) 日期:2026-08-08 状态:✅ 设计已确认(2026-08-08 用户拍板:C3 只扣沟通,其余按建议默认) 范围:家庭挑战(
family_challenge)参与机制重构:任意成员发起、可选成员、全体同意后开始、方向性关系评分。圈子挑战(circle_challenge)不在范围。 前置基线:2026-08-06-family-challenge-flow-design.md(现状断点清单) 输入确认:brainstorming 6 轮澄清 + 3 轮追问(作用对象/时机/方向性语义)
现状一句话(来自基线文档):挑战只能由家长手动创建,创建即 active、默认全员参与、无角色检查、无打卡 UI、无奖励发放。
本次要解决的核心问题:
| # | 问题 | 现状 |
|---|---|---|
| 1 | 挑战只能家长发起 | 孩子/其他成员无发起入口 |
| 2 | 创建即开始,无成员确认环节 | 无"同意"概念,强制全员参与 |
| 3 | 无法选择参与成员 | 固定全员 |
| 4 | 参与行为无任何激励/反馈 | 发起/同意/拒绝不产生分数变化 |
| 5 | 关系分数无方向性 | family_members 只有成员级分数,A对B=B对A |
目标:
| # | 规则 | 结论 |
|---|---|---|
| 1 | 发起人 | 任何家庭成员(家长/孩子/规划师)均可发起 |
| 2 | 参与成员选择 | 创建时可选全员或部分成员 |
| 3 | 发起人 | 自动同意,必参与,自动计入参与成员 |
| 4 | 拒绝 | 拒绝者剔除出挑战,其余参与成员同意后开始 |
| 5 | 同意/拒绝入口 | 首页挑战卡片直接操作(不用进管理页) |
| 6 | 结算口径 | 只统计参与成员(agreed,排除 rejected) |
| 7 | 打卡 | 仅参与成员可打卡;pending 成员不可打卡 |
| 8 | 管理页 | 放开门控,所有家庭成员可进入创建 |
| 9 | 待同意状态 | 不限时(pending 可长期挂起) |
| 10 | 评分时机 | 动作发生时立即结算 |
| 11 | 评分数值 | 默认 ±1 分,后台可配置 |
| 12 | 方向性 | 信任/亲近/沟通是有方向的成对分数:A对B ≠ B对A |
| 13 | 发起评分 | 能量+、沟通+、亲近+(不加信任) |
| 14 | 同意评分 | 能量+、沟通+、亲近+、信任+(四项全加) |
| 15 | 拒绝评分 | 能量-;发起人→拒绝人 信任/亲近/沟通全 -;拒绝人→发起人 仅沟通 +;已同意成员→拒绝人 仅沟通 -(见 §4.3) |
family_challenge_participant(参与成员表)family_challenge_participant
├── id BIGINT AUTO_INCREMENT PK
├── challenge_id BIGINT NOT NULL -- 挑战ID
├── member_id BIGINT NOT NULL -- 成员ID
├── status VARCHAR(20) NOT NULL -- pending / agreed / rejected
├── agreed_at DATETIME NULL -- 同意时间(rejected 可复用记录拒绝时间)
├── created_at DATETIME DEFAULT NOW
├── updated_at DATETIME DEFAULT NOW ON UPDATE NOW
├── UNIQUE KEY uk_challenge_member (challenge_id, member_id)
└── INDEX idx_member (member_id)
用途:替代"创建即全员参与"的隐式模型,显式记录每个成员在挑战中的参与状态。
family_relationship_scores(方向性关系分数表)family_relationship_scores
├── id BIGINT AUTO_INCREMENT PK
├── family_id BIGINT NOT NULL
├── from_member_id BIGINT NOT NULL -- 分数持有方(A对B的分,记在 A→B 行)
├── to_member_id BIGINT NOT NULL -- 分数对象方
├── trust_score DECIMAL(5,2) DEFAULT 0 -- A对B的信任
├── intimacy_score DECIMAL(5,2) DEFAULT 0 -- A对B的亲近
├── communication_score DECIMAL(5,2) DEFAULT 0 -- A对B的沟通
├── created_at DATETIME DEFAULT NOW
├── updated_at DATETIME DEFAULT NOW ON UPDATE NOW
├── UNIQUE KEY uk_from_to (from_member_id, to_member_id)
└── INDEX idx_family (family_id)
为什么要新表:
family_members 的三列是成员级、无方向(问卷提交时被 updateMemberScores 压平)family_relationships 是成对表但只存辈分/关系标签,无分数列,且语义是"族谱"family_members 的对应成员级分数作为初始基线;基线缺失则默认 60.00family_challenge 表变更| 变更 | 说明 |
|---|---|
| 状态机 | active / completed / cancelled 之外新增 pending(待同意) |
| 兼容 | 存量 active 挑战无 participant 行 → 迁移时为其补全 participant 行(全员 agreed),不破坏旧数据 |
方案:新建 family_challenge_score_config 简单配置表 + cfc-web 管理页(复用 AdminEnergyConfigController 风格):
family_challenge_score_config
├── id BIGINT AUTO_INCREMENT PK
├── action VARCHAR(20) NOT NULL -- initiate / agree / reject
├── score_type VARCHAR(20) NOT NULL -- energy / trust / intimacy / communication
├── value INT NOT NULL DEFAULT 0 -- 增减值(可正可负,默认 ±1)
├── enabled TINYINT DEFAULT 1
├── updated_at DATETIME
└── UNIQUE KEY uk_action_type (action, score_type)
默认值(按已确认规则 13-15):
| action | energy | trust | intimacy | communication |
|---|---|---|---|---|
| initiate | +1 | 0 | +1 | +1 |
| agree | +1 | +1 | +1 | +1 |
| reject | -1 | -1 | -1 | +1 |
说明:reject 行的通信方向由代码固定(拒绝人→发起人 +、发起人→拒绝人 -;已同意成员→拒绝人 仅沟通 -1,见 §4.3 C3),
value只控制增减幅度。能量非方向属性,作用于个人。
family_relationship_scores,行 (A→B) 表示"A 对 B 的分数"。动作执行者(actor)永远是被加/被减的主体——加分/减分落点在 actor 的行上。EnergyService.awardEnergy / deductEnergyByCode,维度 code action)。用户原话:"同意只是加同意人相对其他成员的,不影响其他成员对同意人的"
成员 X 同意挑战时:
| 落点 | trust | intimacy | communication | energy |
|---|---|---|---|---|
| X→其他每个参与成员 | +1 | +1 | +1 | — |
| X 本人(能量) | — | — | — | +1 |
| 其他成员→X(反向) | 0 | 0 | 0 | — |
即:只有 X 的"出向"行加分,反向行不动。
用户原话:"拒绝人对发起人的沟通加,发起人对拒绝人的沟通减" 用户拍板(2026-08-08):C1 ✅ -1、C2 ✅ 不变、C3 ✅ 只扣沟通、C4 ✅ I+A 每人 -1
成员 X 拒绝、发起人为 I、已同意成员集合为 A:
| 落点 | trust | intimacy | communication | energy |
|---|---|---|---|---|
| I→X | -1 | -1 | -1 | — |
| X→I | 0 | 0 | +1 | — |
| A 中每个成员→X | 0 | 0 | -1(只扣沟通) | — |
| I / A 能量 | — | — | — | -1(I+A 每人 -1) |
Q1 结论:"发起/同意时所有参与成员一起加"。方向性语义下,发起人与同意人的模式完全平行(发起人出向加分)。
发起人 I 创建挑战(参与成员集合 P,含 I):
| 落点 | trust | intimacy | communication | energy |
|---|---|---|---|---|
| I→P 中每个其他成员 | 0(不加信任) | +1 | +1 | — |
| I 本人(能量) | — | — | — | +1(仅发起人) |
| P 中其他成员→I(反向) | 0 | 0 | 0 | — |
全部
@PostMapping(项目规范)。前缀沿用/api/health/challenge/。
POST /api/health/challenge/create
{
"title": "全家运动周",
"type": "sports",
"goalMode": "aggregate",
"goalValue": 100,
"rewardPoints": 50,
"startDate": "2026-08-10",
"endDate": "2026-08-17",
"participantMemberIds": [1001, 1002, 1003] // 新增:参与成员,含发起人自身
}
participantMemberIds 必须包含发起人且均属本家庭family_challenge(status=pending)→ 写 participant 行(发起人 agreed,其余 pending)→ 立即结算发起评分(§4.4)→ 发起人能量 +1POST /api/health/challenge/respond
{
"challengeId": 123,
"action": "agree" // 或 "reject"
}
pending;挑战必须处于 pendingagreed、agreed_at=now → 立即结算同意评分(§4.2)→ 若除 rejected 外的参与成员全部 agreed → 挑战 status→activerejected → 立即结算拒绝评分(§4.3)→ 若除发起人外无剩余参与成员 → 挑战自动 cancelled(待确认,见 §6 Q6)POST /api/health/challenge/list
myStatus: pending/agreed/rejected),前端据此渲染同意/拒绝按钮pending 状态挑战:显示"等待成员确认(X/Y)"进度文案POST /api/health/challenge/progress
agreed;pending/rejected 返回"仅参与成员可打卡"settleChallenge():参与成员集合 = status=agreed 的成员(排除 rejected)POST /api/admin/challenge-score-config/list / update(cfc-web 新增管理页,风格参照 EnergyBehaviorConfig.vue)
| # | 问题 | 结论 |
|---|---|---|
| C1 | 拒绝时 I→X 的 trust/intimacy 是否也 -1 | ✅ -1(与 communication 一致) |
| C2 | 拒绝时 X→I 的 trust/intimacy 是否不变 | ✅ 不变(只加沟通) |
| C3 | 拒绝时已同意成员 A→X 方向是否也扣 | ✅ 只扣沟通 -1(trust/intimacy 不扣) |
| C4 | 拒绝时能量 -1 落点 | ✅ I+A 每人 -1 |
| D1 | 发起时 I→其他成员 是否 +1(intimacy/communication) | ✅ +1(平行同意模式) |
| D2 | 发起时其他成员→I 反向是否不动 | ✅ 不动 |
| D3 | 发起时能量 +1 是仅发起人还是全员 | ✅ 仅发起人("发起人加能量") |
| Q6 | 除发起人外全员拒绝时挑战如何处置 | ✅ 自动 cancelled |
| 文件 | 改动 |
|---|---|
pages/health/challenge-manage.vue |
①移除角色门控(L173-174);②创建表单新增"参与成员多选"(默认全选);③展示 pending 状态 |
components/FamilyChallengeCard.vue |
①pending 状态渲染:"等待确认(已同意 x/y)";②同意/拒绝按钮(仅我的状态=pending 时显示);③rejected 成员卡片显示"已拒绝"徽标 |
pages/index-home/index.vue |
已加"管理 →"入口(已完成 7bdf6aa8) |
utils/api.js |
新增 respondChallenge(challengeId, action);list 响应解析 myStatus |
| 文件 | 改动 |
|---|---|
entity/FamilyChallengeParticipant.java |
新建实体 |
entity/FamilyRelationshipScore.java |
新建实体(方向分数) |
entity/FamilyChallengeScoreConfig.java |
新建实体(评分配置) |
mapper/ 对应 3 个 Mapper |
新建 |
service/FamilyChallengeService.java |
createChallenge 改 pending+participant 落库;新增 respondChallenge();打卡/结算校验接入 participant |
service/ChallengeScoreService.java |
新建:方向分数读取/初始化/增减 + 能量发放/扣减(读 config 表),供发起/同意/拒绝调用 |
controller/FamilyChallengeController.java |
新增 respond 端点;create 校验 participant 列表;list 返回 myStatus |
controller/admin/AdminChallengeScoreConfigController.java |
新建:配置 CRUD |
config/DatabaseInitializer.java |
迁移:3 张新表 + family_challenge.status 兼容说明 |
resources/schema.sql |
同步 3 张新表 DDL |
cfc-web/src/views/admin/ChallengeScoreConfig.vue |
新建管理页 |