feat: 后端架构重构 — Endpoint→Service→Repository分层 + AI确认机制 + 异步任务持久化

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

View File

@@ -0,0 +1,283 @@
# 后端架构演进方案
日期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。
- 上传、列表、详情、删除、查看原图、分析失败状态保持可用。
- 后端编译通过,前端报告页分析通过。