elder-yudao-pro/docs/健康业务模块与身份模型.md

183 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 健康业务模块与身份模型
更新日期:2026-10-05。实施仓库:`elder-yudao-pro`,分支:`axr`。
## 1. 本次业务决定
以下规则来自用户本次确认,后续开发应沿用:
- 老人、志愿者、家属使用 **手机号+密码** 登录,短信验证后续接入。
- **每个账号只有一种业务身份**,不能同时是老人、志愿者和家属。后台管理员属于另一套账号体系。
- 志愿者需要管理员审核,通过且启用后才能参与志愿服务。
- 第一版由管理员创建账号与身份档案,后续再开放本人注册。
- 一位老人可关联多位家属,一位家属可关联多位老人。
- 家属关联需老人本人确认;管理员也可以核实后确认,必须填写核实理由。
- 家属只在关联已确认且双方身份有效时,才能读取关联老人的健康信息。
- 暂不删除已有源码、模块和目录;身份使用停用,关联使用解除,保留历史。
管理员核实方式、注册时的手机号验证流程,后续实现对应入口时再细化。目前没有公共密码注册接口。
## 2. 账号与身份的边界
```mermaid
flowchart LR
System[system_users 后台管理员] --> Admin[管理端权限和审核]
Member[member_user 手机号账号] --> Identity[health_identity 单一业务身份]
Identity --> Elder[老人 health_elderly_profile]
Identity --> Volunteer[志愿者 health_volunteer_profile]
Identity --> Family[家属身份]
Family --> Relation[health_family_relation 待确认或已确认]
Relation --> Elder
```
`system` 的角色、菜单权限与 `health` 的业务身份用途不同。前台选择“老人”或“志愿者”不能获得该身份;后端按登录的 `memberId` 查询可信身份。
框架区分 `/admin-api` 的后台 Token 与 `/app-api` 的会员 Token。健康 APP Controller 额外使用 `@PreAuthorize("isAuthenticated()")`,因为本版本芋道的 APP 路径默认允许匿名进入。
| 名称 | 含义 | 使用位置 |
| --- | --- | --- |
| `adminUserId` | `system_users.id` | 审核管理员、管理端权限 |
| `memberId` | `member_user.id` | 手机号登录账号 |
| `identityId` | `health_identity.id` | 老人、志愿者或家属的业务身份 |
| `elderlyProfileId` | `health_elderly_profile.id` | 老人健康档案,后续评估与预约的主体 |
| `volunteerProfileId` | `health_volunteer_profile.id` | 志愿者服务资料,后续派单关联 |
| `relationId` | `health_family_relation.id` | 单条家属与老人关联 |
不同表的 ID 可以恰好相同,不能靠数值相同推导关联。创建账号、身份和对应档案在一个数据库事务内完成;账号密码使用芋道 BCrypt,响应不包含密码。
## 3. 模块与表
根 Maven 和 `yudao-server` 已启用 `member`、`health`。健康业务只有一个模块,按芋道惯例分为 `api/biz`,不引入新的微服务或通用工作流。
```
yudao-module-health/
yudao-module-health-api/ 模块间接口、DTO、身份枚举、错误码
yudao-module-health-biz/ Controller、Service、DO、Mapper、测试
yudao-ui-admin-vue3/src/
api/health/ 健康管理请求封装
views/health/identity/ 身份、基础档案与志愿者审核
views/health/family-relation/ 家属关联确认与解除
mysql_script/health/ 独立增量 SQL
```
所有健康表继承 `TenantBaseDO`,含租户、创建/更新和逻辑删除字段;使用框架租户拦截器,关联查询与建档校验均在当前租户内完成。
| 表 | 当前字段与约束 |
| --- | --- |
| `member_user` | 手机号、BCrypt 密码、账号状态;新建表添加 `(tenant_id,mobile)` 唯一索引 |
| `health_identity` | 账号、单一身份、姓名、性别、生日、联系方式、地址、启用状态、审核结果;`(tenant_id,member_id)` 唯一 |
| `health_elderly_profile` | 紧急联系人、健康摘要、病史、用药、特殊需求;每个老人身份一份基础档案 |
| `health_volunteer_profile` | 技能、服务区域、可服务时间说明、服务经验;每个志愿者身份一份服务档案 |
| `health_family_relation` | 家属身份、老人档案、亲属关系、确认/解除状态、处理账号类型与编号、时间、理由;同一配对唯一 |
当前不采集身份证号。生日存储为 `date`,年龄后续按生日计算。性别沿用芋道:`1=男、2=女、3=未知`;旧系统 `0=男、1=女` 必须显式转换。
手机号用于登录;`contactPhone` 是档案联系方式,不会因编辑档案而更改登录手机号。已有会员账号通过 `memberId` 显式绑定,建档不会重置其密码。身份类型和绑定账号创建后不可变更,避免后续服务记录主体发生变化。
账号停用和身份停用均会使业务身份校验失败。模板会员模块仍保留积分、等级等源码与空表,本次管理页面只展示健康相关功能。
## 4. 审核与关联状态
业务身份:`10=老人、20=志愿者、30=家属`。
启用状态:`0=启用、1=停用`。审核状态:`0=待审核、1=通过、2=驳回`。两者独立,启用一个待审志愿者不能使其获得服务权限。
- 管理员创建老人和家属身份时直接通过;创建志愿者时进入待审。
- 只有待审志愿者能审核;驳回必须填写理由。
- 驳回后可修改资料并重新提审;重复审核由条件更新拦截。
- 编辑档案不会修改启用或审核状态。
家属关联:`0=待确认、1=已确认、2=已拒绝、3=已解除`。
- 新增关联仅创建申请,不能自动获得读取权限。
- 本人端申请的家属身份取自登录账号,忽略请求中伪造的 `familyIdentityId`。
- 老人只能处理自己的关联;管理员核实确认或拒绝时必须填写理由。
- 老人、家属可解除本人的待确认/已确认关联;管理员也可解除,均需填写理由。
- 管理员可将已拒绝/已解除关联重新申请,此时必须重新确认才恢复授权。
- 处理字段保留最近一次审核/解除痕迹,尚不是完整的每次变更事件流水。
- 当前确认代表该家属可读取关联老人的基础健康档案;第一阶段不提供家属代预约。
## 5. 接口与权限
管理端前缀 `/admin-api`:
| 路径 | 功能 | 权限 |
| --- | --- | --- |
| `POST /health/identity/create` | 创建账号与身份档案,或绑定已有会员账号 | `health:identity:create` |
| `PUT /health/identity/update` | 编辑基本资料及对应档案 | `health:identity:update` |
| `GET /health/identity/get`、`/page` | 详情与分页筛选 | `health:identity:query` |
| `PUT /health/identity/audit`、`/resubmit` | 志愿者审核与重新提审 | `health:identity:audit` |
| `PUT /health/identity/update-status` | 身份启用与停用 | `health:identity:update-status` |
| `GET /health/family-relation/page` | 查看关联列表 | `health:family-relation:query` |
| `POST /health/family-relation/create` | 提交关联申请 | `health:family-relation:create` |
| `PUT /health/family-relation/review`、`/reopen` | 核实处理与重新申请 | `health:family-relation:review` |
| `PUT /health/family-relation/revoke` | 解除关联 | `health:family-relation:revoke` |
新增关联页面按姓名搜索老人和家属,同时需要 `health:identity:query`。普通后台角色应通过系统角色管理授予所需菜单与按钮权限;超级管理员默认拥有全部菜单。
会员端前缀 `/app-api`:
| 路径 | 当前行为 |
| --- | --- |
| `POST /member/auth/login` | 复用芋道的手机号与密码登录、Token 颁发 |
| `GET /health/identity/me` | 读取当前会员的单一身份与对应档案;可查看本人志愿者待审结果 |
| `GET /health/identity/family-relations` | 本人相关关联列表 |
| `POST /health/identity/family-relation/request` | 家属提交关联申请 |
| `PUT /health/identity/family-relation/review` | 老人本人确认或拒绝 |
| `PUT /health/identity/family-relation/revoke` | 老人或家属解除本人关联 |
| `GET /health/identity/family-elderly-profile` | 已确认家属按老人档案编号读取健康档案 |
本次提供 APP 后端接口,没有改造旧移动端。请求需正确的 Token 和 `tenant-id`;家属和老人账号类型同为 MEMBER,具体业务权限仍须健康服务校验。
其他模块调用 `HealthIdentityApi`:
- `getIdentityByMemberId` 仅查询身份,不表示身份有效。
- `requireActiveIdentity(memberId, identityType)` 检查账号存在/启用、业务身份类型、启用状态与审核结果。后续预约与接单使用此方法。
- `requireConfirmedFamilyRelation(familyMemberId, elderlyProfileId)` 检查双方有效及已确认关联。后续家属访问评估、运动与告警记录使用此方法。
- 本次没有提供志愿者读取任意老人健康档案的入口;后续必须结合已分配工单校验访问范围。
## 6. 数据库与 IDEA 运行
按顺序执行:
1. `V000__member_schema.sql`:从模板提取的 11 张会员表的纯 DDL,去掉 DROP、示例账号、演示数据及自增起点。
2. `V001__health_identity.sql`:新增 4 张健康业务表与唯一索引。
3. `V002__health_menu.sql`:新增“健康服务 → 身份与档案、家属关联”及按钮权限。
均使用存在性判断,不清空数据。`CREATE TABLE IF NOT EXISTS` 不会升级已有旧表结构;若已有 member_user 缺少约定唯一索引,初始化脚本会停止并提示核实,不能直接覆盖已有表。
在项目根目录执行:
```powershell
powershell -ExecutionPolicy Bypass -File .\deploy\local\initialize-health.ps1
```
脚本读取已有的私有 `secrets.psd1`,仅允许操作 `elder_yudao_*` 本地独立数据库。不会导入旧老年系统数据,不会初始化摄像头、短信或外部呼叫服务。
IDEA 点击 Maven 的 Reload All Maven Projects,再重新运行已有的后端和前端启动配置。前端 `.env` 已增加 `health`;模板会员商城页面未加入产品菜单。
管理端重新登录后进入“健康服务”,可以新建老人、志愿者和家属档案,审批志愿者、申请和核实家属关联。
## 7. 旧系统迁移注意事项与下一步
旧数据库 `sys_user.user_id` 是登录编号,`elderly_info.elderly_id` 是档案编号;一些旧家属代码把 `elderly_id` 当作登录编号。不能直接沿用旧字段名解释。正式导入时需要 legacy ID 映射,明确旧登录 ID → 新 memberId、旧档案 ID → 新 elderlyProfileId、旧采集记录 ID → 新 assessmentId。
旧健康采集和评估允许多次记录。当前 `health_elderly_profile` 是基础档案,不能把所有历史评估覆盖到这一行。下一阶段应建立独立评估记录、保存时间及原始指标,再逐项迁移已核实的评分逻辑。
后续既定顺序:健康采集与评估 → 轻量运动/场地/设备 → 自由预约与活动报名 → 志愿者匹配/派单/按月拒单理由 → 提醒;LLM/智能体和固定场地摄像头另行接入。摄像头跌倒检测、最近 72 小时视频保存、自动呼叫 120 都尚未实现,老人无回应的判定仍待讨论。
新增能力继续放在 health 的独立包与表中,预约使用老人档案编号,派单使用志愿者身份/服务档案编号,健康历史数据单独保存。不要将手机号、密码或管理员角色塞进健康评估表。
参考:[迁移上下文](芋道框架迁移上下文.md)、[扩展需求草案](老年健康系统扩展需求与设计草案.md)、[项目目录说明](项目目录说明.md)。扩展草案是历史需求记录,具体已实现内容以本文和代码为准。
## 8. 验证记录
- Maven 后端打包通过,`HealthIdentityServiceTest` 的 9 项测试全部通过:加密账号/档案创建、事务回滚、单身份约束、重复手机号不重置密码、志愿者审核与停用、家属确认与解除、伪造身份拦截及多对多、租户隔离、匿名 APP 调用拦截。
- Vue 管理端 desktop 生产构建通过,新增文件 ESLint 检查通过。
- 前端原始全量 `vue-tsc` 因既有类型声明路径配置报 TS2688;使用临时检查配置补充核实后,新增 health 页面/API 没有类型错误,但模板其他页面/组件仍有既有错误,不能宣称全仓库类型检查通过。
- 本地 `elder_yudao_local` 增量初始化完成,共 76 张表(原 61+member 11+health 4);脚本重复执行验证通过,健康账号与关联表保持空数据。
- 临时后端在 `48081`、Redis DB 14 验证后已关闭;已有 `48080` 后端和 `5173` 前端进程未重启。新代码需要由用户在 IDEA 重新运行。
- 实际 HTTP 验证通过:管理员登录、身份与关联分页、新增两个健康菜单、匿名 APP 拒绝(业务码 403)、后台 Token 调用 APP 拒绝(403)、错误手机号拒绝(400)、匿名后台接口拒绝(401)。未创建演示会员账号。
未迁移旧数据,未改造移动端页面,未实现历次健康评估、预约派单或设备/智能体接入;这些仍是后续阶段。