# 用户使用流程图 — 圈子与联系人 > **端口说明**: 📱 小程序 | 🖥 管理后台 | 📋 规划师端 | ⚙️ 系统自动 > > **分层说明**: 🏠 页面 → 🔗 API → ⚙️ Service → 📦 数据实体 ```mermaid flowchart TD %% ============ 颜色定义 ============ classDef page fill:#e3f2fd,stroke:#1565c0,stroke-width:2px classDef api fill:#fff3e0,stroke:#f57c00,stroke-width:1px classDef service fill:#e8f5e9,stroke:#388e3c,stroke-width:1px classDef data fill:#f3e5f5,stroke:#7b1fa2,stroke-width:1px classDef external fill:#fce4ec,stroke:#d32f2f,stroke-width:1px,stroke-dasharray:3 2 classDef actor fill:#e1f5fe,stroke:#0288d1,stroke-width:2px,stroke-dasharray:5 3 %% ============ 角色 ============ ROLE_PARENT(("👤 家长")):::actor ROLE_CHILD(("👶 孩子")):::actor %% ================================================================ %% 阶段一:社交圈子管理 %% ================================================================ subgraph 阶段一[阶段一:社交圈子管理] direction TB P1["🏠 小程序:圈子列表页"]:::page P1 -->|"选择操作"| P1_choice{"查看 / 发现 / 创建"}:::page P1_choice -->|"查看我的圈子"| A1["🔗 POST /api/circle/my-circles"]:::api A1 -->|"memberId, memberType"| S1["⚙️ CircleService.getMyCircles()"]:::service S1 -->|"查询成员关系后关联圈子信息"| D1["📦 social_circle_member + social_circle"]:::data P1_choice -->|"发现推荐圈子"| P1a["🏠 小程序:发现圈子页"]:::page P1a -->|"childId"| A2["🔗 POST /api/circle/discover"]:::api A2 -->|"childId"| S2["⚙️ CircleService.discoverCircles()"]:::service S2 -->|"调用匹配引擎, 过滤已加入"| S2a["⚙️ CircleMatchService.discover()"]:::service S2a -->|"活动/测评/健康报告等匹配源"| D1a["📦 匹配结果(内存中)"]:::data P1_choice -->|"创建圈子"| P1b["🏠 小程序:创建圈子表单"]:::page P1b -->|"name, type, matchSource, creatorId, creatorType"| A3["🔗 POST /api/circle/create"]:::api A3 -->|"圈子基本信息"| S3["⚙️ CircleService.createCircle()"]:::service S3 -->|"写入圈子记录, 创建者自动加入"| D1b["📦 social_circle + social_circle_member"]:::data D1 -->|"circleId"| P1c["🏠 小程序:圈子详情页"]:::page P1c -->|"加入圈子"| A4["🔗 POST /api/circle/join"]:::api A4 -->|"circleId, memberId, memberType"| S4["⚙️ CircleService.joinCircle()"]:::service S4 -->|"写成员记录, 更新成员计数"| D1c["📦 social_circle_member + social_circle"]:::data P1c -->|"退出圈子"| A5["🔗 POST /api/circle/leave"]:::api A5 -->|"circleId, memberId, memberType"| S5["⚙️ CircleService.leaveCircle()"]:::service S5 -->|"删除成员记录, 更新成员计数"| D1 end %% ================================================================ %% 阶段二:健康圈子 %% ================================================================ subgraph 阶段二[阶段二:健康圈子] direction TB P2["🏠 小程序:健康圈子页"]:::page P2 -->|"选择操作"| P2_choice{"列表 / 创建 / 加入 / 退出 / 排行"}:::page P2_choice -->|"列表"| A6["🔗 POST /api/health/circle/list"]:::api A6 -->|"familyId"| S6["⚙️ HealthCircleService.getUserCircles()"]:::service S6 -->|"查询家庭所属的健康圈"| D2["📦 health_circle"]:::data P2_choice -->|"创建"| P2a["🏠 小程序:创建健康圈子表单"]:::page P2a -->|"name, description, familyId"| A7["🔗 POST /api/health/circle/create"]:::api A7 -->|"圈子名称, 描述, 所属家庭"| S7["⚙️ HealthCircleService.createCircle()"]:::service S7 -->|"写入健康圈记录, 生成邀请码"| D2a["📦 health_circle"]:::data P2_choice -->|"加入"| P2b["🏠 小程序:输入邀请码"]:::page P2b -->|"inviteCode, familyId"| A8["🔗 POST /api/health/circle/join"]:::api A8 -->|"邀请码, 家庭ID"| S8["⚙️ HealthCircleService.joinCircle()"]:::service S8 -->|"校验邀请码, 写入成员记录"| D2b["📦 health_circle_member"]:::data P2_choice -->|"退出"| P2c["🏠 小程序:确认退出"]:::page P2c -->|"circleId, familyId"| A9["🔗 POST /api/health/circle/leave"]:::api A9 -->|"圈ID, 家庭ID"| S9["⚙️ HealthCircleService.leaveCircle()"]:::service S9 -->|"标记成员退出"| D2b P2_choice -->|"排行"| P2d["🏠 小程序:健康圈排行榜"]:::page P2d -->|"circleId"| A10["🔗 POST /api/health/circle/ranking"]:::api A10 -->|"圈ID"| S10["⚙️ HealthCircleService.getCircleRanking()"]:::service S10 -->|"各家庭健康积分排行"| D2c["📦 排行数据(内存)"]:::data end %% ================================================================ %% 阶段三:挑战 %% ================================================================ subgraph 阶段三[阶段三:挑战管理] direction TB D2 -->|"circleId"| P3["🏠 小程序:圈子挑战页"]:::page P3 -->|"查看挑战列表"| A11["🔗 POST /api/health/circle/challenge/list"]:::api A11 -->|"circleId"| S11["⚙️ CircleChallengeService.getActiveChallenges()"]:::service S11 -->|"该圈子活跃的挑战"| D3["📦 circle_challenge"]:::data P3 -->|"查看挑战历史"| A12["🔗 POST /api/health/circle/challenge/history"]:::api A12 -->|"circleId"| S12["⚙️ CircleChallengeService.getChallengeHistory()"]:::service S12 -->|"该圈子已完成的挑战"| D3 P3a["🏠 小程序:家庭挑战页"]:::page P3a -->|"查看家庭挑战"| A13["🔗 POST /api/health/challenge/list"]:::api A13 -->|"familyId"| S13["⚙️ FamilyChallengeService.getActiveChallenges()"]:::service S13 -->|"该家庭活跃的挑战"| D3a["📦 family_challenge"]:::data P3a -->|"查看挑战历史"| A14["🔗 POST /api/health/challenge/history"]:::api A14 -->|"familyId"| S14["⚙️ FamilyChallengeService.getChallengeHistory()"]:::service S14 -->|"该家庭已完成的挑战"| D3a P3a -->|"更新挑战进度"| P3b["🏠 小程序:执行挑战任务"]:::page P3b -->|"challengeId, childId, delta"| A15["🔗 POST /api/health/challenge/progress"]:::api A15 -->|"挑战ID, 孩子ID, 进度增量"| S15["⚙️ FamilyChallengeService.updateProgress()"]:::service S15 -->|"更新进度, 自动完成检测"| D3a end %% ================================================================ %% 阶段四:联系人管理 %% ================================================================ subgraph 阶段四[阶段四:联系人管理] direction TB P4["🏠 小程序:联系人列表页"]:::page P4 -->|"选择操作"| P4_choice{"列表 / 创建 / 导入 / 详情"}:::page P4_choice -->|"查看列表"| A16["🔗 POST /api/contact/list"]:::api A16 -->|"userId(从token提取)"| S16["⚙️ ContactService.list()"]:::service S16 -->|"按亲密度降序排列"| D4["📦 contacts 表"]:::data P4_choice -->|"手动创建"| P4a["🏠 小程序:新建联系人表单"]:::page P4a -->|"name, phone, relationshipType, ..."| A17["🔗 POST /api/contact/create"]:::api A17 -->|"联系人基本信息"| S17["⚙️ ContactService.create()"]:::service S17 -->|"写入联系人, 默认亲密度30"| D4 P4_choice -->|"导入手机联系人"| P4b["🏠 小程序:手机联系人列表"]:::page P4b -->|"name, phone, extra信息"| A18["🔗 POST /api/contact/import-phone"]:::api A18 -->|"姓名, 手机号, 额外信息"| S18["⚙️ ContactService.importFromPhone()"]:::service S18 -->|"去重后写入, 来源标记phone"| D4 D4 -->|"contactId"| P4c["🏠 小程序:联系人详情页"]:::page P4c -->|"查看详情"| A19["🔗 POST /api/contact/detail"]:::api A19 -->|"id"| S19["⚙️ ContactService.detail()"]:::service S19 -->|"返回DTO(含生日倒计时等)"| D4 P4c -->|"编辑联系人"| P4d["🏠 小程序:编辑联系人表单"]:::page P4d -->|"id, name, phone, tags, ..."| A20["🔗 POST /api/contact/update"]:::api A20 -->|"更新字段"| S20["⚙️ ContactService.update()"]:::service S20 -->|"局部更新联系人"| D4 P4c -->|"删除联系人"| P4e["🏠 小程序:确认删除"]:::page P4e -->|"id"| A21["🔗 POST /api/contact/delete"]:::api A21 -->|"联系人ID"| S21["⚙️ ContactService.delete()"]:::service S21 -->|"物理删除联系人"| D4 P4c -->|"记录互动"| A22["🔗 POST /api/contact/interaction"]:::api A22 -->|"id"| S22["⚙️ ContactService.recordInteraction()"]:::service S22 -->|"互动+1, 更新亲密度, 更新时间"| D4 P4c -->|"邀请到家庭"| P4f["🏠 小程序:选择关系类型"]:::page P4f -->|"contactId, relationshipType, generationLevel"| A23["🔗 POST /api/contact/invite-to-family"]:::api A23 -->|"联系人ID, 关系类型, 辈分"| S23["⚙️ ContactService.inviteToFamily()"]:::service S23 -->|"标记邀请状态invited"| D4 end %% ================================================================ %% 角色关联 %% ================================================================ ROLE_CHILD -.- P1 ROLE_CHILD -.- P1a ROLE_CHILD -.- P3 ROLE_PARENT -.- P2 ROLE_PARENT -.- P3a ROLE_PARENT -.- P4 ``` ## 端点明细 ### 社交圈子 | 端点 | 说明 | 端口 | 请求数据 | 响应数据 | |------|------|------|---------|---------| | `POST /api/circle/my-circles` | 获取我的圈子列表 | 📱小程序 | `{memberId, memberType}` | `[{id, name, type, matchSource, memberCount}]` | | `POST /api/circle/discover` | 发现推荐圈子 | 📱小程序 | `{childId}` | `[{id, name, type, matchSource, memberCount}]` | | `POST /api/circle/join` | 加入圈子 | 📱小程序 | `{circleId, memberId, memberType}` | `{success}` | | `POST /api/circle/leave` | 退出圈子 | 📱小程序 | `{circleId, memberId, memberType}` | `{success}` | | `POST /api/circle/create` | 创建圈子 | 📱小程序 | `{name, type, matchSource, sourceId, creatorId, creatorType}` | `SocialCircle对象` | ### 健康圈子 | 端点 | 说明 | 端口 | 请求数据 | 响应数据 | |------|------|------|---------|---------| | `POST /api/health/circle/list` | 获取健康圈子列表 | 📱小程序 | `{familyId}` | `[HealthCircle对象]` | | `POST /api/health/circle/create` | 创建健康圈子 | 📱小程序 | `{name, description, familyId}` | `HealthCircle对象` | | `POST /api/health/circle/join` | 通过邀请码加入健康圈子 | 📱小程序 | `{inviteCode, familyId}` | `{success}` | | `POST /api/health/circle/leave` | 退出健康圈子 | 📱小程序 | `{circleId, familyId}` | `{success}` | | `POST /api/health/circle/ranking` | 健康圈子排行榜 | 📱小程序 | `{circleId}` | `{rankings: [...]}` | | `POST /api/health/family/ranking` | 家庭健康排行 | 📱小程序 | `{circleId}` | `{rankings: [...]}` | ### 挑战 | 端点 | 说明 | 端口 | 请求数据 | 响应数据 | |------|------|------|---------|---------| | `POST /api/health/circle/challenge/list` | 圈子挑战列表 | 📱小程序 | `{circleId}` | `[CircleChallenge对象]` | | `POST /api/health/circle/challenge/history` | 圈子挑战历史 | 📱小程序 | `{circleId}` | `[CircleChallenge对象]` | | `POST /api/health/challenge/list` | 家庭挑战列表 | 📱小程序 | `{familyId}` | `[FamilyChallenge对象]` | | `POST /api/health/challenge/history` | 家庭挑战历史 | 📱小程序 | `{familyId}` | `[FamilyChallenge对象]` | | `POST /api/health/challenge/progress` | 更新挑战进度 | 📱小程序 | `{challengeId, childId, delta}` | `{success}` | ### 联系人 | 端点 | 说明 | 端口 | 请求数据 | 响应数据 | |------|------|------|---------|---------| | `POST /api/contact/list` | 获取联系人列表 | 📱小程序 | Header: `Authorization` | `[ContactDTO对象]`(按亲密度降序) | | `POST /api/contact/create` | 创建联系人 | 📱小程序 | `{name, phone, relationshipType, ...}` | `ContactDTO对象` | | `POST /api/contact/update` | 更新联系人 | 📱小程序 | `{id, name, phone, tags, ...}` | `ContactDTO对象` | | `POST /api/contact/delete` | 删除联系人 | 📱小程序 | `{id}` | `{success}` | | `POST /api/contact/detail` | 获取联系人详情 | 📱小程序 | `{id}` | `ContactDTO对象`(含生日倒计时等) | | `POST /api/contact/import-phone` | 从手机导入联系人 | 📱小程序 | `{name, phone, extra}` | `ContactDTO对象` | | `POST /api/contact/interaction` | 记录互动 | 📱小程序 | `{id}` | `ContactDTO对象` | | `POST /api/contact/invite-to-family` | 邀请联系人加入家庭 | 📱小程序 | `{contactId, relationshipType, generationLevel}` | `{contactId, invitedStatus, relationshipType}` | ## 数据实体关系 ```mermaid erDiagram SocialCircle ||--o{ SocialCircleMember : "一个圈子多个成员" SocialCircle ||--o{ CircleChallenge : "一个圈子多个挑战" HealthCircle ||--o{ HealthCircleMember : "一个健康圈多个家庭" Family ||--o{ HealthCircleMember : "一个家庭加入多个健康圈" User ||--o{ Contact : "一个用户多个联系人" SocialCircle { Long id PK string name string type "topic/activity/hobby/ability/product/health/provider" string matchSource Long sourceId int memberCount } SocialCircleMember { Long id PK Long circleId FK Long memberId string memberType "parent/child" } HealthCircle { Long id PK string name string description Long creatorFamilyId string inviteCode int memberLimit string status "active/closed" } HealthCircleMember { Long id PK Long circleId FK Long familyId FK string role "member/admin" string status "active/inactive" } CircleChallenge { Long id PK Long circleId FK string challengeType "checkin/step/meditation" string title int durationDays string period "weekly/monthly" string status "active/completed/cancelled" } Contact { Long id PK Long userId FK string name string phone string relationshipType "family/friend/partner/colleague/other" int intimacyLevel string contactSource "phone/manual" Long familyMemberId "关联家庭成员ID" string invitedStatus "none/invited/accepted/declined" } ``` ## 完整性分析 | # | 维度 | 评估 | 说明 | |---|------|------|------| | 1 | **流程完整性** | ✅ 完整 | 覆盖社交圈子CRUD、健康圈子管理、圈子挑战、家庭挑战、联系人管理5个阶段,路径完整 | | 2 | **异常路径** | ⚠️ 部分覆盖 | 加入圈子时已加入则直接返回成功(幂等);联系人导入时重复手机号会返回错误;邀请联系人时已是家庭成员也会返回错误 | | 3 | **端点覆盖** | ✅ 完整 | 24个端点全部映射到流程图中,与CircleController、HealthCircleController、CircleChallengeController、FamilyChallengeController、ContactController的`@PostMapping`一致 | | 4 | **角色覆盖** | ✅ 完整 | 社交圈子面向孩子(child),健康圈子和联系人面向家长(parent),分工明确 | | 5 | **数据实体** | ✅ 完整 | social_circle、social_circle_member、health_circle、health_circle_member、circle_challenge、contacts共6张核心表均在图中映射 | | 6 | **一致性** | ✅ 与代码一致 | 所有端点路径与实际Controller中的`@PostMapping`匹配,边缘输出数据与代码逻辑一致 |