Files
AI-Health/docs/backend_architecture_evolution.md
MingNian 4d213b5a44 feat: 后端架构重构 — Endpoint→Service→Repository分层 + AI确认机制 + 异步任务持久化
- 核心业务拆分为 Endpoint → Application Service → Repository 三层
- AI写入操作必须用户确认后才写库(确认卡片机制)
- 报告/饮食/用药分析改为持久化任务队列(原子领取/重试/重启恢复)
- 运动计划修复: 连续真实日期替代周模板
- 用药提醒去重 + 通知Outbox预留
- 认证收拢到AuthService, 管理员收拢到AdminService
- AI会话加用户归属校验防串号
- 提示词调整为患者视角
- 开发假数据已关闭
- 21/21测试通过, 0警告0错误
2026-06-20 20:41:42 +08:00

284 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-06-18
## 1. 背景
当前项目已经具备 `Health.Domain``Health.Application``Health.Infrastructure``Health.WebApi` 的分层目录,但实际业务逻辑主要仍写在 `Health.WebApi/Endpoints` 中。多数接口直接注入 `AppDbContext`,在 Endpoint 内完成查询、权限判断、状态流转和 `SaveChanges`
这种方式适合早期快速验证但随着患者端健康管理、AI 分析、报告、饮食、用药、运动、医生端等业务增长,会带来几个问题:
- 业务规则分散在 Endpoint、AI Agent Handler、后台服务中。
- 同一个业务动作可能被普通接口和 AI 工具重复实现。
- 权限和资源归属校验容易遗漏。
- 异步任务目前存在 `Task.Run` 形式,不便于限流、重试和统一管理。
- Application 层没有真正承接业务用例,后续测试和维护成本会升高。
技术目标是按 DDD 思路逐步演进:让 Endpoint 成为接口适配层,业务流程进入 Application Service外部能力和数据访问由 Infrastructure 支撑,耗时任务通过生产者-消费者管道处理。
## 2. 当前业务边界
### 2.1 当前核心患者端闭环
以下业务都需要继续做扎实:
1. AI 健康管家聊天
2. 健康指标记录与趋势:血压、心率、血糖、血氧、体重
3. 报告上传与 AI 预解读
4. 饮食拍照分析与保存
5. 用药管理与打卡
6. 运动计划
### 2.2 医生端当前策略
医生端可以做代码整理和权限边界收拢,但不作为当前核心业务闭环:
- 医患实时聊天:暂时搁置,后续接入互联网医院后再完善。
- 医生审核报告:暂时不是当前真实流程,患者端报告以 AI 预解读为主;界面可显示“医生审核中/待审核”一类状态。
- 医生工作台:保留现有页面和基础接口,不主动扩大功能范围,重构时以不影响当前项目运行为目标。
### 2.3 AI 写入规则
统一业务规则:
- AI 纯查询、解释、建议可以直接回复。
- AI 只要要写入用户健康相关数据,必须先让用户确认。
- 需要确认的写入包括但不限于:健康指标、用药计划、运动计划、健康档案修改。
- 饮食记录当前在饮食分析结果页由用户主动保存,保留该方式。
## 3. 目标架构
目标不是创建一个巨大的全能 Service而是按业务模块拆分 Application Service
```text
Health.WebApi
Endpoints
Hubs
BackgroundServices
Health.Application
Auth
Users
HealthRecords
Medications
Diet
Reports
Exercise
Consultations
Doctors
Ai
Common
Health.Domain
Entities
Enums
DomainRules
Health.Infrastructure
Data
AI
Services
Storage
```
职责边界:
- `WebApi`:接收 HTTP/SignalR 请求,解析参数,返回响应。
- `Application`:承接业务用例、状态流转、权限校验、任务入队。
- `Domain`:保存核心实体、枚举和稳定业务规则。
- `Infrastructure`EF Core、AI 客户端、短信、文件存储、推送、未来互联网医院适配。
## 4. 数据库读写收拢方式
短期先不强制引入 Repository 抽象,避免过度设计。第一阶段采用:
```text
Endpoint -> Application Service -> AppDbContext
```
也就是说,数据库读写先统一进入对应业务 Service而不是继续散落在 Endpoint 中。
后续如果业务复杂度继续提升,再考虑:
```text
Application Service -> Repository / UnitOfWork -> AppDbContext
```
第一阶段优先收拢这些模块:
1. `ReportService`
2. `DietService`
3. `MedicationService`
4. `ExerciseService`
5. `HealthRecordService`
6. `ConsultationService`
## 5. 生产者-消费者管道
不是所有业务都需要管道。同步、快速、必须立即返回的操作仍走普通 Service。
适合管道的任务:
- 报告 AI 分析
- 饮食图片识别
- 处方图片识别
- 用药提醒推送
- 健康周报生成
- 后期互联网医院数据同步
当前阶段采用 .NET 内置 `Channel<T>` + `BackgroundService`
```text
业务 Service = 生产者
Channel<TJob> = 内存任务队列
BackgroundService = 消费者
AI/推送/外部服务 = 实际执行器
```
单服务器和当前用户规模下,内存队列足够作为第一阶段方案。后续如果出现多实例部署、任务不能丢、重试次数和死信队列等要求,再迁移到 Redis Stream、RabbitMQ 或 Hangfire。
## 6. 第一阶段落地范围
先从报告模块落地因为它同时具备上传、AI、异步、状态流转、失败重试等典型场景。
### 6.1 报告模块目标
从当前:
```text
ReportEndpoints -> AppDbContext + Task.Run + AI 调用
```
演进为:
```text
ReportEndpoints
-> ReportService
-> 保存文件
-> 创建报告
-> 标记状态
-> 入队 ReportAnalysisJob
ReportAnalysisWorker
-> 消费 ReportAnalysisQueue
-> 调用 ReportAnalysisService / AI Analyzer
-> 更新报告状态
```
### 6.2 报告模块要保持的业务行为
- 上传只支持报告图片。
- 上传成功后状态为 `Analyzing`
- AI 成功后进入 `PendingDoctor`,患者端展示 AI 预解读。
- AI 失败后进入 `AnalysisFailed`,不生成假摘要、假指标。
- 患者端可以查看原始报告图片。
- 医生审核不是当前核心闭环,状态可保留但不扩大功能。
## 7. 当前已落地进展
截至 2026-06-18第一轮服务化已经完成以下模块
1. `ReportService`报告上传、图片校验、报告创建、重新分析、删除、AI 分析状态流转进入服务层;报告分析通过 `ReportAnalysisQueue` + `ReportAnalysisWorker` 异步消费。
2. `HealthRecordService`健康指标列表、创建、更新、删除、最新值、趋势查询、异常判断进入服务层AI 记数据工具复用同一套创建逻辑。
3. `ExerciseService`运动计划创建、列表、详情、删除、打卡、AI 创建/查询/打卡进入服务层。
4. `MedicationService`用药列表、创建、更新、删除、今日服药确认、按剂量打卡、提醒查询、AI 创建/查询/确认进入服务层。
5. `DietService`:饮食记录列表、保存、删除、热量/评分更新进入服务层。
其中以下模块已经继续演进为更严格的 Application Service + Repository 结构:
```text
Endpoint
-> Health.Application.*Service
-> Health.Application.I*Repository
-> Health.Infrastructure.Ef*Repository
-> AppDbContext
```
已完成:
1. `HealthRecordService` + `IHealthRecordRepository` + `EfHealthRecordRepository`
2. `ExerciseService` + `IExerciseRepository` + `EfExerciseRepository`
3. `DietService` + `IDietRepository` + `EfDietRepository`
4. `MedicationService` + `IMedicationRepository` + `EfMedicationRepository`
5. `ReportService` + `IReportRepository` + `EfReportRepository` + `IReportFileStorage` + `LocalReportFileStorage`
报告模块中,`ReportAnalysisQueue``ReportAnalysisService` 仍属于 Infrastructure前者是内存队列适配后者需要调用 AI 客户端并通过 `IReportRepository` 更新报告状态,不再直接依赖 `AppDbContext`
AI 写入确认机制已完成第一阶段落地:
```text
AI 写入工具调用
-> 创建 10 分钟有效的 PendingAiWriteCommand
-> 前端展示确认卡片,此时不写数据库
-> 用户点击确认
-> POST /api/ai/confirm-write/{commandId}
-> 校验当前用户和一次性命令
-> 执行对应 Application Service 写入
```
- 健康指标、创建/确认用药、创建/打卡运动、AI 修改健康档案均进入待确认流程。
- 纯查询工具继续直接执行。
- 命令只能由所属用户执行一次;执行失败时会恢复命令供用户重试,过期或服务重启后自动失效。
- AI 待确认命令已迁移到数据库表 `AiWriteCommands`,领取命令、业务写入和完成状态在同一事务内执行,避免重复写入。
- 报告分析任务已迁移到数据库表 `ReportAnalysisTasks`,支持服务重启恢复、原子领取、失败重试和最终失败状态。
- 项目当前仍使用 `EnsureCreated` 管理原有表;新增 `DatabaseSchemaMigrator``__AppSchemaMigrations` 版本表,为已有本地数据库安全补充基础设施表。
患者端其他业务收拢进展:
1. `HealthArchiveService` + `IHealthArchiveRepository`健康档案页面、AI 查询和确认写入统一执行。
2. `AiConversationService` + `IAiConversationRepository`:会话创建、消息保存、历史列表和删除统一执行。
3. `PatientContextService`:统一组合健康档案、近期指标和当前用药。
4. `UserService` + `IUserRepository`:个人资料和账号注销统一执行;账号数据清理使用事务。
5. `CalendarService` + `ICalendarRepository`:聚合用药、运动、随访日历。
生产者/消费者管道现状:
- 报告分析:数据库持久化任务 + `ReportAnalysisWorker`
- 饮食图片识别:任务持久化到 `DietImageAnalysisTasks`,由 `DietImageAnalysisWorker` 原子领取、失败重试和恢复处理中断任务;前端接口协议保持不变。
- 用药提醒:`MedicationReminderService` 负责扫描生产任务,任务持久化到 `MedicationReminderTasks` 并按药品/日期/时间唯一去重;`MedicationReminderWorker` 消费后写入 `NotificationOutbox`。真正的手机推送需在选定推送服务后消费 Outbox目前不会把未发送通知标记为已推送。
- 报告、饮食和用药任务消费者均采用 1 到 5 秒的自适应空闲轮询,过期 Processing 任务每分钟恢复一次,避免每秒重复执行恢复更新。
- App 内用药提醒通过 `NotificationOutbox` + `/api/notifications/pending` 提供,患者端前台每 30 秒获取并展示,展示后回执去重;暂不接入系统级或厂商推送。
- `MaintenanceService` 每小时执行一次维护,自动删除超过 30 天的已完成/失败后台任务、通知 Outbox、过期 AI 写入命令和旧 AI 会话,不删除用户健康记录、饮食记录或用药计划。
- 登录、注册、验证码、Token 刷新和退出已收拢到 `IAuthService`;管理员医生管理和患者列表已收拢到 `IAdminService`Endpoint 不再直接读写数据库。开发环境 `send-sms` 仍返回 `devCode` 供 Flutter 自动填入,非开发环境不返回验证码。
- AI 工具查询、确认写入和事务边界已收拢到 `IAiToolExecutionService`AI Endpoint 不再直接持有 `AppDbContext`
- 运动计划已从 `WeekStartDate + DayOfWeek` 周模板改为 `StartDate + EndDate + ReminderTime`,每日条目使用唯一的 `ScheduledDate`。连续 7 天、10 天或更长计划按真实日期生成,首页今日任务、健康日历、医生端只读详情和 AI 创建均使用同一日期模型。
- App 内运动提醒按每日条目的 `ScheduledDate` 和计划 `ReminderTime` 生成到 `NotificationOutbox`;已打卡和休息日不会提醒,同一每日条目只生成一次。
当前仍保留在 Endpoint 或旧 Handler 中的业务,需要后续逐步收拢:
- `ConsultationService`:医生聊天暂不作为当前核心业务,但基础权限和数据读写仍可继续服务化。
- `DoctorService` / `AdminDoctorService`:医生信息、患者列表、报告查看等目前不作为真实互联网医院流程,后续按老板和互联网医院接入方案调整。
- `CalendarService`:健康日历目前聚合运动、用药、饮食等多模块数据,适合在核心模块稳定后单独收拢。
- `User/ProfileService`:个人信息、健康档案、账号清理等可继续整理,尤其是 AI 修改健康档案前的确认规则。
## 8. 后续推广顺序
1. 报告Application Service + 分析队列 + Worker
2. 饮食:图片识别任务队列,用户修正后保存
3. 用药:提醒扫描和推送任务解耦
4. 运动:计划创建、打卡规则收拢到 Service
5. 健康指标:记录、异常判断、趋势查询收拢到 Service
6. 问诊:互联网医院接入前只收拢基础接口,不扩大聊天功能
7. 医生端:保留基础接口,后续根据互联网医院和老板决策再完善审核/聊天工作流
## 9. 不做的事
当前阶段暂不做:
- 不一次性重构全项目。
- 不立即引入 RabbitMQ/Kafka 等重型组件。
- 不强制引入复杂 Repository 层。
- 不改变当前患者端主要交互。
- 不把医生聊天/医生审核作为当前核心业务流。
## 10. 验收标准
第一阶段完成后应满足:
- 报告 Endpoint 不再直接承载主要业务流程。
- 报告 AI 分析不再使用 `Task.Run`
- 报告分析任务通过队列进入后台 Worker。
- 报告状态流转集中在 Application Service。
- 上传、列表、详情、删除、查看原图、分析失败状态保持可用。
- 后端编译通过,前端报告页分析通过。