Files
AI-Health/健康管家-技术设计文档-v2.md
MingNian 14d7c30d3d Initial commit: 健康管家 AI 健康陪伴助手
- Backend: .NET 10 Minimal API + EF Core + PostgreSQL
- Frontend: Flutter + Riverpod + GoRouter + Dio
- AI: DeepSeek LLM + Qwen VLM (OpenAI-compatible)
- Auth: SMS + JWT (access/refresh tokens)
- Features: AI chat, health tracking, medication management, diet analysis, exercise plans, doctor consultations, report analysis
2026-06-02 11:11:29 +08:00

1255 lines
41 KiB
Markdown
Raw Permalink 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.

# 健康管家 — 技术设计文档 V2
> 基于需求规格文档 V2技术栈确定为 .NET 10 + Flutter + PostgreSQL。
---
## 一、整体架构
```
Flutter AppiOS + Android
├── HTTPS REST业务 CRUD
├── SSEAI 对话流式输出)
└── HTTPS 轮询医患消息15s间隔
.NET 10 ASP.NET Core Minimal API
├── HttpClient → DeepSeekLLM 对话 + Tool Calling
├── HttpClient → 千问 VL食物图片识别
├── EF Core → PostgreSQL主数据库
├── MinIO SDK → MinIO文件/图片存储)
└── 极光推送 SDK → 极光推送Android/iOS 通知)
```
- 后端:.NET 10C# 124 层 Clean Architecture
- 前端FlutterDartRiverpod
- AI 调用:纯 HttpClient 直连各模型厂商 APIOpenAI 兼容协议),不使用任何模型厂商 SDK
- 不允许前端直接调 AI 模型 API全部经 .NET 后端中转
---
## 二、后端架构
### 2.1 目录结构
```
HealthManager/
├── src/
│ ├── Health.Domain/ # 领域层
│ │ ├── Entities/ # 实体User, HealthRecord, Medication, DietRecord 等
│ │ ├── Enums/ # 枚举HealthMetricType, MealType, ReportStatus 等
│ │ └── Interfaces/ # 仓储接口
│ │
│ ├── Health.Application/ # 应用层
│ │ ├── DTOs/ # 数据传输对象
│ │ ├── Services/ # 业务服务
│ │ │ ├── AuthService.cs # 认证
│ │ │ ├── HealthService.cs # 健康数据
│ │ │ ├── DietService.cs # 饮食记录
│ │ │ ├── MedicationService.cs # 用药管理
│ │ │ ├── ReportService.cs # 报告管理
│ │ │ ├── ConsultationService.cs # 问诊
│ │ │ ├── ExerciseService.cs # 运动计划
│ │ │ └── UserService.cs # 用户/健康档案
│ │ └── Interfaces/ # 服务接口
│ │
│ ├── Health.Infrastructure/ # 基础设施层
│ │ ├── Data/
│ │ │ ├── AppDbContext.cs # EF Core 上下文
│ │ │ └── Migrations/ # 数据库迁移
│ │ ├── Services/
│ │ │ ├── JwtProvider.cs # JWT 生成与验证
│ │ │ ├── SmsService.cs # 短信发送
│ │ │ ├── PushService.cs # 极光推送
│ │ │ └── MinioStorageService.cs # 文件存储
│ │ └── AI/ # AI 服务模块
│ │ ├── OpenAiCompatibleClient.cs # OpenAI 兼容 HTTP 客户端(统一调 DeepSeek/千问)
│ │ ├── AiChatService.cs # AI 对话编排(意图识别 + Tool Calling
│ │ ├── AgentHandlers/ # 7 个 Agent 的处理器
│ │ │ ├── DefaultAgentHandler.cs # 默认对话
│ │ │ ├── ConsultationAgentHandler.cs # AI 问诊
│ │ │ ├── HealthDataAgentHandler.cs # 记数据
│ │ │ ├── DietAgentHandler.cs # 拍饮食
│ │ │ ├── MedicationAgentHandler.cs # 药管家
│ │ │ ├── ReportAgentHandler.cs # 看报告
│ │ │ └── ExerciseAgentHandler.cs # 运动计划
│ │ └── PromptManager.cs # System Prompt 模板管理
│ │
│ └── Health.WebApi/ # 接口层
│ ├── Program.cs # 启动配置
│ ├── Endpoints/ # Minimal API 端点
│ │ ├── AuthEndpoints.cs
│ │ ├── HealthEndpoints.cs
│ │ ├── DietEndpoints.cs
│ │ ├── MedicationEndpoints.cs
│ │ ├── ReportEndpoints.cs
│ │ ├── ConsultationEndpoints.cs
│ │ ├── ExerciseEndpoints.cs
│ │ ├── UserEndpoints.cs
│ │ ├── AiChatEndpoints.cs # AI 对话 SSE
│ │ └── FileEndpoints.cs
│ ├── Middleware/ # 中间件
│ │ └── ExceptionMiddleware.cs
│ └── BackgroundServices/ # 后台服务
│ └── MedicationReminderService.cs # 用药提醒定时扫描
```
### 2.2 API 响应格式
所有 API 返回统一结构:
```json
// 成功
{ "code": 0, "data": { ... }, "message": null }
// 业务错误
{ "code": 40001, "data": null, "message": "验证码错误" }
// 系统异常
{ "code": 50000, "data": null, "message": "服务器内部错误" }
```
- `code = 0`:成功
- `code = 4xxxx`:客户端错误
- `code = 5xxxx`:服务端错误
---
### 2.3 7 个 Agent 设计
每个 Agent 本质上是:一个 SSE 端点 + 独立的 System Prompt + 独立的 Tool Set。
#### Agent 架构模式
```
Flutter 端 → GET /api/ai/{agent_type}/chat?message=xxx
AgentHandler.Process(message, userId, sessionId)
├── 1. 加载上下文System Prompt + 患者数据 + 对话历史)
├── 2. 调用 DeepSeekstream: true
├── 3. Tool Calling 循环(如果 LLM 返回 tool_calls
│ ├── 执行 Tool
│ ├── 结果追加到消息历史
│ └── 再次调用 LLM
├── 4. 流式输出回复SSE data: 逐 token
└── 5. 保存对话记录到 PostgreSQL
```
#### 7 个 Agent 对照表
| Agent | Path | System Prompt 定位 | Tool Set |
|-------|------|-------------------|----------|
| 默认对话 | `/api/ai/default/chat` | 通用健康管家 | query_health_records, check_abnormal |
| AI 问诊 | `/api/ai/consultation/chat` | 医生助手,多轮追问 | query_health_records, check_archive, request_doctor |
| 记数据 | `/api/ai/health/chat` | 健康数据录入助手 | record_health_data, query_health_records |
| 拍饮食 | `/api/ai/diet/chat` | 营养分析专家 | analyze_food_imageVLM, estimate_food_text, check_archive |
| 药管家 | `/api/ai/medication/chat` | 用药管理专家 | manage_medicationcreate/query/confirm, check_archive |
| 看报告 | `/api/ai/report/chat` | 报告解读专家 | analyze_reportVLM, query_health_records |
| 运动计划 | `/api/ai/exercise/chat` | 运动康复教练 | manage_exercise_plancreate/query/checkin |
### 2.4 Tool Calling 设计
| Tool | 功能 | 参数 |
|------|------|------|
| `record_health_data` | 录入健康数据 | type, systolic?, diastolic?, heart_rate?, glucose?, spo2?, weight?, recorded_at |
| `query_health_records` | 查询近期健康数据 | type?, days? |
| `check_abnormal` | 检查是否有异常指标 | — |
| `check_archive` | 查询患者档案 | — |
| `analyze_food_image` | VLM 食物识别 | image_url |
| `estimate_food_text` | 文字描述估算食物 | text |
| `manage_medication` | 用药管理 | action(create/query/confirm/update), name?, dosage?, time? |
| `manage_exercise_plan` | 运动计划 | action(create/query/checkin), exercises?, day? |
| `analyze_report` | 报告 AI 预解读 | image_url |
| `request_doctor` | 转医生 | reason, urgency_level |
### 2.5 AI 服务模块
#### OpenAiCompatibleClient
统一的 OpenAI 兼容协议 HTTP 客户端,支持:
- Chat Completions含 streaming
- Vision图片理解
- Tool CallingFunction Calling
- JSON Mode
- 多 BaseUrl 切换DeepSeek / 千问 VL
- 自动重试
```csharp
// 调用示例
var client = new OpenAiCompatibleClient(new()
{
BaseUrl = "https://api.deepseek.com/v1",
ApiKey = config["DEEPSEEK_API_KEY"],
Model = "deepseek-chat"
});
await foreach (var token in client.ChatStreamAsync(messages, tools))
{
// SSE 输出给前端
}
```
#### AiChatService
每个 Agent 对话的核心编排逻辑:
```csharp
public async IAsyncEnumerable<string> ProcessAgentChat(
AgentType agentType,
string message,
string userId,
string? conversationId)
{
// 1. 加载 System Prompt
var systemPrompt = _promptManager.GetSystemPrompt(agentType);
// 2. 注入患者上下文
var patientContext = await LoadPatientContext(userId);
// 3. 加载对话历史
var history = await LoadConversationHistory(conversationId, userId);
// 4. 构建消息
var messages = BuildMessages(systemPrompt, patientContext, history, message);
// 5. Tool Calling 循环
var tools = _toolRegistry.GetTools(agentType);
var maxIterations = 5;
for (int i = 0; i < maxIterations; i++)
{
var response = await _aiClient.ChatAsync(messages, tools, stream: true);
if (response.FinishReason == "stop")
{
// 流式输出文本
await foreach (var token in response.Stream)
yield return token;
break;
}
else if (response.FinishReason == "tool_calls")
{
// 执行 Tool
foreach (var toolCall in response.ToolCalls)
{
var result = await _toolExecutor.Execute(toolCall);
messages.Add(new { role = "tool", content = result, tool_call_id = toolCall.Id });
}
// 继续循环
}
}
// 6. 保存对话记录
await SaveConversation(userId, conversationId, messages);
}
```
#### PromptManager
System Prompt 模板管理:
```
默认对话:
你是一个心脏术后康复患者的私人 AI 健康管家。
你的名字叫"阿福",语气温暖专业。
每次回复末尾,如果有需要提醒的事项,简短提醒一句。
AI 问诊:
你是一个心血管内科医生助手,负责对患者进行多轮问诊。
规则:
1. 每次只问一个问题,不要一次问多个
2. 给出快捷选项让患者点击
3. 遇到胸痛/呼吸困难/心悸 → 建议立即就医
4. 血压持续>160/100 或心率异常 → 建议转医生
5. 问诊结束后给出初步分析
记数据:
你是一个健康数据录入助手。
规则:
1. 解析用户口中的指标和数值
2. 指标模糊时追问确认
3. 时间模糊时取当前时间并告知用户
4. 异常值时附带提醒
5. 录入后展示确认卡片
拍饮食:
你是一个营养分析专家。
规则:
1. 等待VLM识别结果后再做分析
2. 结合患者档案判断"能不能吃"
3. 给出单项警告和整体建议
4. 追问餐次归类
...
(其余 Agent 同理)
```
### 2.6 配置管理
所有敏感配置放环境变量(`.env` / appsettings
```
# LLM
DEEPSEEK_API_KEY=sk-xxxxx
DEEPSEEK_BASE_URL=https://api.deepseek.com/v1
DEEPSEEK_MODEL=deepseek-chat
# VLM
QWEN_API_KEY=sk-xxxxx
QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
QWEN_VISION_MODEL=qwen-vl-max
# 基础设施
JWT_SECRET=xxxxx
DB_CONNECTION=Host=localhost;Database=health_manager;...
MINIO_ENDPOINT=localhost:9000
MINIO_ACCESS_KEY=xxxxx
MINIO_SECRET_KEY=xxxxx
# 推送
JPUSH_APP_KEY=xxxxx
JPUSH_MASTER_SECRET=xxxxx
# 短信
SMS_PROVIDER=xxx
SMS_API_KEY=xxxxx
```
---
## 三、数据库设计
### 3.1 核心表
| 表名 | 说明 |
|------|------|
| `users` | 用户表 |
| `health_records` | 健康记录(血压/心率/血糖/血氧/体重) |
| `medications` | 用药计划 |
| `medication_logs` | 用药打卡记录 |
| `diet_records` | 饮食记录 |
| `diet_food_items` | 饮食记录中的食物条目 |
| `exercise_plans` | 运动计划(按周) |
| `exercise_plan_items` | 运动计划每日条目 |
| `reports` | 检查报告 |
| `conversations` | AI 对话会话 |
| `conversation_messages` | AI 对话消息 |
| `consultations` | 问诊会话 |
| `consultation_messages` | 问诊消息 |
| `follow_ups` | 复查/随访计划 |
| `health_archives` | 健康档案 |
| `refresh_tokens` | 刷新令牌 |
| `verification_codes` | 验证码 |
| `notification_preferences` | 通知偏好 |
| `device_tokens` | 设备推送 token |
| `devices` | 绑定设备(预留) |
### 3.2 关键表结构
#### users
```sql
id UUID PRIMARY KEY
phone VARCHAR(20) UNIQUE NOT NULL
name VARCHAR(50)
gender VARCHAR(10)
birth_date DATE
avatar_url VARCHAR(500)
created_at TIMESTAMP
updated_at TIMESTAMP
```
#### health_records
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
metric_type VARCHAR(20) NOT NULL -- blood_pressure / heart_rate / glucose / spo2 / weight
systolic INTEGER -- 血压专用
diastolic INTEGER -- 血压专用
value DECIMAL -- 通用数值(心率/血糖/血氧/体重)
unit VARCHAR(20)
source VARCHAR(20) NOT NULL -- ai_entry / device_sync / manual
is_abnormal BOOLEAN
recorded_at TIMESTAMP
created_at TIMESTAMP
```
#### medications
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
name VARCHAR(100) NOT NULL
dosage VARCHAR(50)
frequency VARCHAR(50) -- daily / twice_daily / weekly 等
time_of_day TIME[] -- PostgreSQL 数组,如 {08:00,20:00}
start_date DATE
end_date DATE
is_active BOOLEAN
source VARCHAR(20) -- prescription / ai_entry / manual
created_at TIMESTAMP
updated_at TIMESTAMP
```
#### diet_records
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
meal_type VARCHAR(10) -- breakfast / lunch / dinner / snack
total_calories INTEGER
health_score INTEGER -- 1-5
recorded_at DATE
created_at TIMESTAMP
```
#### diet_food_items
```sql
id UUID PRIMARY KEY
diet_record_id UUID REFERENCES diet_records(id)
name VARCHAR(100)
portion VARCHAR(50)
calories INTEGER
protein_g DECIMAL
carbs_g DECIMAL
fat_g DECIMAL
warning TEXT
sort_order INTEGER
```
#### health_archives
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id) UNIQUE
diagnosis TEXT -- 主要诊断
surgery_type VARCHAR(100) -- 手术类型
surgery_date DATE -- 手术日期
allergies TEXT[] -- 过敏信息
diet_restrictions TEXT[] -- 饮食限制
chronic_diseases TEXT[] -- 慢病史
family_history TEXT -- 家族病史
updated_at TIMESTAMP
```
#### medication_logs
```sql
id UUID PRIMARY KEY
medication_id UUID REFERENCES medications(id)
user_id UUID REFERENCES users(id)
status VARCHAR(20) NOT NULL -- taken / missed / skipped
scheduled_time TIME NOT NULL
confirmed_at TIMESTAMP
created_at TIMESTAMP
```
#### exercise_plans
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
week_start_date DATE NOT NULL -- 本周一
created_at TIMESTAMP
updated_at TIMESTAMP
```
#### exercise_plan_items
```sql
id UUID PRIMARY KEY
plan_id UUID REFERENCES exercise_plans(id)
day_of_week INTEGER NOT NULL -- 0=周一, 6=周日
exercise_type VARCHAR(100) NOT NULL -- 散步/慢跑/游泳 等
duration_minutes INTEGER NOT NULL
is_completed BOOLEAN DEFAULT FALSE
completed_at TIMESTAMP
is_rest_day BOOLEAN DEFAULT FALSE -- 休息日
```
#### reports
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
file_url VARCHAR(500) NOT NULL
file_type VARCHAR(20) -- image / pdf
report_type VARCHAR(50) -- blood_test / ecg / ultrasound / other
ai_summary TEXT -- AI 预解读结果JSON
ai_indicators JSONB -- 提取的指标 [{name, value, unit, range, status}]
status VARCHAR(20) NOT NULL -- pending_doctor / doctor_reviewed
doctor_comment TEXT -- 医生审核意见
doctor_name VARCHAR(50) -- 审核医生
reviewed_at TIMESTAMP
created_at TIMESTAMP
```
#### conversations
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
agent_type VARCHAR(20) NOT NULL -- default / consultation / health / diet 等
title VARCHAR(200)
summary VARCHAR(500) -- 侧滑抽屉显示用的摘要
message_count INTEGER DEFAULT 0
created_at TIMESTAMP
updated_at TIMESTAMP
```
#### conversation_messages
```sql
id UUID PRIMARY KEY
conversation_id UUID REFERENCES conversations(id)
role VARCHAR(10) NOT NULL -- user / assistant / tool
content TEXT NOT NULL
intent VARCHAR(30) -- health_record / diet / medication / exercise / report / chat
metadata_json JSONB -- 结构化数据(录入数值、食物列表、卡片数据等)
created_at TIMESTAMP
```
#### consultations
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
doctor_id UUID REFERENCES doctors(id)
status VARCHAR(20) NOT NULL -- ai_talking / waiting_doctor / doctor_replied / closed
month INTEGER NOT NULL -- 所属月份,用于配额计算
created_at TIMESTAMP
closed_at TIMESTAMP
```
#### consultation_messages
```sql
id UUID PRIMARY KEY
consultation_id UUID REFERENCES consultations(id)
sender_type VARCHAR(10) NOT NULL -- user / doctor / ai
sender_name VARCHAR(50)
content TEXT NOT NULL
created_at TIMESTAMP
```
#### doctors
```sql
id UUID PRIMARY KEY
name VARCHAR(50) NOT NULL
title VARCHAR(50) -- 主任医师/副主任医师
department VARCHAR(50) -- 心血管内科/营养科 等
avatar_url VARCHAR(500)
introduction TEXT
is_active BOOLEAN DEFAULT TRUE
created_at TIMESTAMP
```
#### follow_ups
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
title VARCHAR(200) NOT NULL
doctor_name VARCHAR(50)
department VARCHAR(50)
scheduled_at TIMESTAMP NOT NULL
notes TEXT
status VARCHAR(20) NOT NULL -- upcoming / completed / cancelled
created_at TIMESTAMP
```
#### refresh_tokens
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
token VARCHAR(500) UNIQUE NOT NULL
expires_at TIMESTAMP NOT NULL
is_revoked BOOLEAN DEFAULT FALSE
created_at TIMESTAMP
```
#### verification_codes
```sql
id UUID PRIMARY KEY
phone VARCHAR(20) NOT NULL
code VARCHAR(6) NOT NULL
expires_at TIMESTAMP NOT NULL
is_used BOOLEAN DEFAULT FALSE
created_at TIMESTAMP
```
#### notification_preferences
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id) UNIQUE
medication_reminder BOOLEAN DEFAULT TRUE
followup_reminder BOOLEAN DEFAULT TRUE
doctor_reply BOOLEAN DEFAULT TRUE
abnormal_alert BOOLEAN DEFAULT TRUE
updated_at TIMESTAMP
```
#### device_tokens
```sql
id UUID PRIMARY KEY
user_id UUID REFERENCES users(id)
platform VARCHAR(10) NOT NULL -- ios / android
push_token VARCHAR(500) NOT NULL
is_active BOOLEAN DEFAULT TRUE
created_at TIMESTAMP
updated_at TIMESTAMP
```
### 3.3 索引设计
```sql
-- 健康记录:按用户+时间查询高频
CREATE INDEX idx_health_records_user_time ON health_records(user_id, recorded_at DESC);
CREATE INDEX idx_health_records_user_type ON health_records(user_id, metric_type);
-- 用药:按时段扫描提醒
CREATE INDEX idx_medications_active_time ON medications(user_id, is_active);
CREATE INDEX idx_medication_logs_medication ON medication_logs(medication_id, created_at DESC);
-- 对话:按用户+时间
CREATE INDEX idx_conversations_user_time ON conversations(user_id, updated_at DESC);
CREATE INDEX idx_conversation_messages_conv ON conversation_messages(conversation_id, created_at);
-- 问诊:轮询新消息
CREATE INDEX idx_consultation_messages_consult ON consultation_messages(consultation_id, created_at DESC);
-- 饮食:按日期查询
CREATE INDEX idx_diet_records_user_date ON diet_records(user_id, recorded_at DESC);
-- 验证码:清理过期
CREATE INDEX idx_verification_codes_phone ON verification_codes(phone, created_at DESC);
```
---
## 四、API 设计
### 4.1 认证 API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/auth/send-sms` | 发送短信验证码 `{phone}`(开发阶段直接返回成功,不做真实发送) |
| POST | `/api/auth/login` | 验证码登录 `{phone, smsCode}`开发阶段任意6位数字通过 |
| POST | `/api/auth/refresh` | 刷新 token `{refreshToken}``{accessToken, refreshToken}` |
| POST | `/api/auth/logout` | 登出 `{refreshToken}` |
**Token 策略:**
- `access_token`30 分钟,前端内存持有
- `refresh_token`30 天flutter_secure_storage 存储
- 前端收到 401 → Dio 拦截器自动用 refresh_token 换新 access_token → 重试原请求
- 刷新时同时下发新的 refresh_token续期 30 天),旧 refresh_token 立即失效
- 连续 30 天未使用 App 导致 refresh_token 过期 → 需重新登录
- 一次登录长期有效,除非主动退出或 30 天未使用
### 4.2 AI 对话 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/ai/{agent_type}/chat` | SSE 流式对话 |
| GET | `/api/ai/conversations` | 获取对话列表 |
| GET | `/api/ai/conversations/{id}` | 获取对话历史 |
| DELETE | `/api/ai/conversations/{id}` | 删除对话 |
**查询参数:** `message`, `conversationId?`(不传则新建对话)
**agent_type**`default` / `consultation` / `health` / `diet` / `medication` / `report` / `exercise`
**SSE 事件格式:**
```
data: {"action":"notice","message":"正在分析..."} // Tool Calling 前先发提示
data: {"action":"notice","message":"正在记录血压数据..."} // 每次调 Tool 前都发
data: {"action":"answer","data":"你"}
data: {"action":"answer","data":"的"}
data: {"action":"answer","data":"血压"}
data: {"action":"answer","data":"是"}
...
data: {"action":"tool_result","tool":"record_health_data","data":{...}}
data: {"action":"answer","data":"已记录"}
...
data: {"action":"conversation_id","data":"abc123"}
data: [DONE]
```
每次 Tool Calling 循环调 Tool 前,先发一条 notice 事件,避免用户对着空白等待。
### 4.3 VLM 食物识别 API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/ai/analyze-food-image` | 上传食物照片 |
**请求:** `multipart/form-data`,字段 `images`(支持多张)
**响应:**
```json
{
"foods": [
{
"name": "米饭", "portion": "约1碗", "calories": 174,
"proteinGrams": 3.9, "carbsGrams": 38.9, "fatGrams": 0.3
}
],
"totalCalories": 644,
"warnings": ["红烧肉脂肪偏高"],
"score": 3
}
```
### 4.4 健康数据 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/health-records` | 查询 `?type=&days=` |
| POST | `/api/health-records` | 新增AI Tool 或手动) |
| PUT | `/api/health-records/{id}` | 修改 |
| GET | `/api/health-records/latest` | 获取各指标最新值 |
| GET | `/api/health-records/trend` | 趋势数据 `?type=&period=7/30/90` |
### 4.5 用药管理 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/medications` | 获取用药列表 |
| POST | `/api/medications` | 添加用药 |
| PUT | `/api/medications/{id}` | 修改用药 |
| DELETE | `/api/medications/{id}` | 删除用药 |
| POST | `/api/medications/{id}/confirm` | 确认服药打卡 |
### 4.6 饮食记录 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/diet-records` | 查询 `?date=&meal_type=` |
| POST | `/api/diet-records` | 新增 |
| PUT | `/api/diet-records/{id}` | 修改 |
| DELETE | `/api/diet-records/{id}` | 删除 |
### 4.7 运动计划 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/exercise-plans/current` | 获取本周计划 |
| POST | `/api/exercise-plans` | 创建计划 |
| PUT | `/api/exercise-plans/{id}` | 修改计划 |
| POST | `/api/exercise-plans/items/{id}/checkin` | 打卡某日运动 |
### 4.8 报告管理 API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/reports/upload` | 上传报告文件 |
| GET | `/api/reports` | 报告列表 |
| GET | `/api/reports/{id}` | 报告详情(含 AI 解读) |
| PUT | `/api/reports/{id}/doctor-review` | 医生审核(医生端) |
### 4.9 问诊 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/doctors` | 可咨询的医生列表 |
| POST | `/api/consultations` | 创建问诊 |
| GET | `/api/consultations` | 问诊列表 |
| GET | `/api/consultations/{id}` | 问诊详情(含消息) |
| POST | `/api/consultations/{id}/messages` | 发送消息 |
| GET | `/api/consultations/{id}/messages?after={msgId}` | 轮询新消息 |
| GET | `/api/user/consultation-quota` | 剩余问诊次数 |
### 4.10 用户与档案 API
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | `/api/user/profile` | 获取个人信息 |
| PUT | `/api/user/profile` | 修改资料 |
| GET | `/api/user/health-archive` | 获取健康档案 |
| PUT | `/api/user/health-archive` | 更新健康档案 |
| DELETE | `/api/user/account` | 注销账号 |
### 4.11 文件上传 API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/files/upload` | 上传文件(食物图/报告/处方) |
| GET | `/api/files/{id}` | 获取文件 |
| DELETE | `/api/files/{id}` | 删除文件 |
### 4.12 推送与通知 API
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | `/api/notifications/register-device` | 注册设备推送 token |
| GET | `/api/notifications/preferences` | 获取通知偏好 |
| PUT | `/api/notifications/preferences` | 更新通知偏好 |
---
## 五、前端架构Flutter
### 5.1 目录结构
参考 YYZ 项目的组织方式:
```
lib/
├── main.dart # 入口
├── app.dart # MaterialApp + 主题 + 路由
├── core/ # 全局基础设施
│ ├── api_client.dart # Dio 封装token 注入、401 自动刷新、统一报错
│ ├── app_theme.dart # 薰衣草紫主题
│ ├── app_router.dart # GoRouter 配置
│ └── secure_storage.dart # Token 安全存储
├── models/ # 数据模型freezed
│ ├── user.dart
│ ├── health_record.dart
│ ├── food_record.dart
│ ├── medication.dart
│ ├── exercise_plan.dart
│ ├── consultation.dart
│ ├── report.dart
│ └── ai_message.dart
├── providers/ # Riverpod 状态管理
│ ├── auth_provider.dart # 认证状态
│ ├── chat_provider.dart # 对话状态(当前 Agent + 消息列表)
│ ├── health_data_provider.dart # 健康数据
│ ├── medication_provider.dart # 用药
│ ├── diet_provider.dart # 饮食
│ ├── exercise_provider.dart # 运动计划
│ ├── consultation_provider.dart # 问诊
│ └── report_provider.dart # 报告
├── services/ # 业务服务层
│ ├── auth_service.dart # 认证
│ ├── ai_service.dart # AI 对话SSE 流式)
│ ├── health_service.dart # 健康数据 CRUD
│ ├── diet_service.dart # 饮食 CRUD
│ ├── medication_service.dart # 用药 CRUD
│ ├── exercise_service.dart # 运动计划
│ ├── consultation_service.dart # 问诊
│ ├── report_service.dart # 报告
│ └── user_service.dart # 用户/档案
├── pages/ # 页面
│ ├── auth/
│ │ └── login_page.dart # 登录
│ │
│ ├── home/
│ │ ├── home_page.dart # 首页骨架(抽屉 + 卡片 + 对话流 + 输入框)
│ │ └── widgets/ # 首页专属组件
│ │ ├── task_card_area.dart # 任务卡片区
│ │ ├── agent_panel.dart # Agent 功能面板
│ │ ├── chat_bubble.dart # 聊天气泡
│ │ ├── data_confirm_card.dart# 数据确认卡片
│ │ ├── diet_result_card.dart # 饮食分析卡片
│ │ ├── food_edit_sheet.dart # 食物编辑面板
│ │ ├── medication_card.dart # 用药确认卡片
│ │ ├── report_card.dart # 报告解读卡片
│ │ └── health_drawer.dart # 侧滑抽屉
│ │
│ ├── profile/
│ │ ├── profile_page.dart # 个人中心
│ │ ├── edit_profile_page.dart # 编辑资料
│ │ └── health_archive_page.dart # 健康档案
│ │
│ ├── chart/
│ │ └── trend_page.dart # 趋势图表
│ │
│ ├── calendar/
│ │ └── health_calendar_page.dart # 健康日历
│ │
│ ├── medication/
│ │ ├── medication_list_page.dart # 用药列表
│ │ └── medication_edit_page.dart # 编辑用药
│ │
│ ├── report/
│ │ ├── report_list_page.dart # 报告列表
│ │ └── report_detail_page.dart # 报告详情
│ │
│ ├── consultation/
│ │ ├── doctor_list_page.dart # 医生列表
│ │ └── doctor_chat_page.dart # 问诊对话
│ │
│ ├── exercise/
│ │ └── exercise_plan_page.dart # 运动计划管理
│ │
│ ├── diet/
│ │ └── diet_record_list_page.dart# 饮食记录列表
│ │
│ ├── followup/
│ │ └── followup_list_page.dart # 复查列表
│ │
│ └── settings/
│ ├── settings_page.dart # 设置
│ ├── notification_prefs_page.dart
│ └── static_text_page.dart # 隐私/协议/关于(纯文本)
├── widgets/ # 通用组件
│ ├── app_button.dart # 主/次/文字按钮
│ ├── app_card.dart # 卡片容器
│ ├── app_input.dart # 输入框
│ ├── capsule_button.dart # 胶囊按钮
│ ├── loading_widget.dart # 加载动画
│ └── empty_state.dart # 空状态
└── utils/
├── sse_handler.dart # SSE 流处理
├── markdown_utils.dart # Markdown 渲染
└── format.dart # 日期/数值格式化
```
### 5.2 状态管理
使用 RiverpodNotifier + NotifierProvider参考 YYZ 模式:
```dart
// 认证
final authProvider = NotifierProvider<AuthNotifier, AuthState>(AuthNotifier.new);
// 聊天(核心)
final chatProvider = NotifierProvider<ChatNotifier, ChatState>(ChatNotifier.new);
// ChatState: 当前 agent_type、消息列表、conversationId、是否流式中
// 各业务域
final healthDataProvider = NotifierProvider<HealthDataNotifier, HealthDataState>(...);
final medicationProvider = NotifierProvider<MedicationNotifier, MedicationState>(...);
...
```
### 5.3 HTTP 请求层
```dart
// Dio 封装
class ApiClient {
late final Dio _dio;
// 拦截器 1自动带 Authorization: Bearer {accessToken}
// 拦截器 2收到 401 → 自动调 /api/auth/refresh → 重试
// 拦截器 3统一错误处理
}
```
所有 Service 层返回类型安全的模型对象,页面不直接处理 JSON。
### 5.4 路由设计
| 路由 | 页面 | 说明 |
|------|------|------|
| `/login` | 登录页 | 手机号+验证码登录 |
| `/home` | 首页 | 对话流 + 任务卡片区 + Agent 面板 + 输入框 |
| `/trend/:type` | 趋势图表 | 单指标折线图7/30/90天切换 |
| `/calendar` | 健康日历 | 月视图,标记用药/运动/复查 |
| `/medications` | 用药列表 | 当前用药计划列表 |
| `/medications/:id/edit` | 编辑用药 | 修改药品名/剂量/时间 |
| `/reports` | 报告列表 | 按时间倒序 |
| `/reports/:id` | 报告详情 | AI解读 + 医生审核 + 原始图片 |
| `/doctors` | 医生列表 | 可选医生3-4名 |
| `/consultation/:id` | 问诊对话 | 患者与医生文字聊天 |
| `/exercise-plan` | 运动计划 | 本周每天的运动安排+打卡 |
| `/diet-records` | 饮食记录列表 | 按日期查看历史饮食 |
| `/profile` | 个人中心 | 头像+诊断+菜单入口 |
| `/profile/edit` | 编辑资料 | 姓名/性别/出生日期 |
| `/health-archive` | 健康档案 | 完整健康信息6大类 |
| `/followups` | 复查列表 | 即将到来/已完成 |
| `/settings` | 设置 | 隐私/通知/字体/协议/关于/退出 |
| `/settings/notifications` | 通知偏好 | 四类推送独立开关 |
| `/page/:type` | 静态文本页 | 隐私政策/服务协议/关于 |
### 5.5 首页组件交互
- **任务卡片区**`AnimatedContainer` + `GestureDetector` 折叠/展开
- **对话流**`ListView.builder` + `reverse: true`
- **Agent 面板**:底部弹出 `AnimatedContainer`,面板内容随 agent_type 切换
- **输入框**`TextField` + `Row`,附件按钮 📎 + 系统键盘语音转文字
- **侧滑抽屉**`Scaffold` + `Drawer`
### 5.6 Agent 切换流程
```
用户点击胶囊 [记数据]
→ 胶囊高亮
→ chatProvider.setAgentType(AgentType.health)
→ 底部弹出记数据面板(手动录入入口)
→ AI 对话上下文切换到记数据
→ 用户输入 → POST /api/ai/health/chat → SSE 流式
用户再次点击 [记数据]
→ 胶囊恢复
→ 面板收起
→ chatProvider.setAgentType(AgentType.default)
```
### 5.7 消息类型 Widget
| 消息类型 | Widget | 说明 |
|---------|--------|------|
| 纯文本 | ChatBubble | AI 左白底紫左边框,用户右紫底白字 |
| 数据确认 | DataConfirmCard | "已记录:血压 135/85 ✅" + 可点击编辑 |
| 饮食分析 | DietResultCard | 食物列表 + 热量 + 评分 + 警告 + "能不能吃" |
| 用药确认 | MedicationConfirmCard | 药名 + 剂量 + [确认] [修改] |
| 报告解读 | ReportCard | 指标列表 + AI 分析 + "待医生确认" |
| 快捷选项 | QuickOptionsRow | 一行可点击按钮,如 [闷痛] [刺痛] |
### 5.8 SSE 流处理
参考 YYZ 的 `ChatStreamHandler` 模式:
```dart
class SseHandler {
// 订阅 Dio 流式响应
// 解析 SSE 事件answer / notice / tool_result / conversation_id / [DONE]
// 逐步追加 token 到当前 AI 消息
// 处理 tool_result 更新消息中的结构化数据
// onDone → 标记消息 status: 'done'
}
```
### 5.9 平台支持
- Android 最低7.0API 24
- iOS 最低15.0
- 不做暗黑模式
### 5.10 Flutter 依赖
```yaml
dependencies:
flutter:
sdk: flutter
# 状态管理
flutter_riverpod: ^3.2.0
# HTTP
dio: ^5.4.0
# 安全存储
flutter_secure_storage: ^9.2.0
# 路由
go_router: ^14.0.0
# 图表
fl_chart: ^0.68.0
# 本地存储
shared_preferences: ^2.2.0 # 轻量键值存储(字体大小等设置)
# 相机 & 图片
image_picker: ^1.0.0
file_picker: ^10.3.7
# 推送
jpush_flutter: ^4.0.0 # 极光推送
dev_dependencies:
flutter_test:
sdk: flutter
freezed: ^2.5.0
json_serializable: ^6.8.0
build_runner: ^2.4.0
```
---
## 六、定时任务
### 6.1 用药提醒扫描
.NET `BackgroundService` 每分钟扫描一次:
```csharp
public class MedicationReminderService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// 1. 查询reminder_enabled = true AND 服药时间 ≤ 当前时间 AND 今天未打卡
// 2. 对每条 → 查用户 device_token → 调用极光推送
// 3. 记录推送日志
// 4. 检查漏服(超时未打卡 → 再推 → 标记漏服)
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
```
### 6.2 漏服检测
- 服药时间已过 15 分钟未打卡 → 再推一次
- 服药时间已过 30 分钟未打卡 → 再推一次
- 服药时间已过 1 小时未打卡 → 标记漏服,停止提醒
### 6.3 其他定时任务
| 任务 | 频率 | 说明 |
|------|------|------|
| 复查提醒 | 每天 1 次 | 检查未来 3 天内的复查计划 → 推送 |
| 对话记录清理 | 每天 1 次 | 删除 30 天前的对话 |
| 验证码清理 | 每小时 1 次 | 删除过期验证码 |
---
## 七、文件存储
### 7.1 MinIO 存储桶
| 存储桶 | 内容 | 生命周期 |
|------|------|------|
| `food-images` | 食物照片 | 30 天 |
| `report-images` | 报告图片/PDF | 永久 |
| `avatars` | 用户头像 | 永久 |
### 7.2 上传流程
```
Flutter → Dio multipart → .NET API → 存入 MinIO → 返回文件 URL
→ VLM 食物识别时:文件 URL 传给千问 VL
```
### 7.3 图片上传规格
| 参数 | 食物照片 | 报告图片/PDF |
|------|---------|------------|
| 支持格式 | JPEG / PNG / HEIC | JPEG / PNG / HEIC / PDF |
| 最大文件 | 10MB | 10MB |
| 前端处理 | 原图上传,不压缩 | 原图上传,不压缩 |
---
## 八、推送通知
### 8.1 推送架构
```
.NET BackgroundService → 极光推送 SDK → 极光服务器 → APNs (iOS) / 极光通道 (Android)
```
- 设备注册时 Flutter 获取极光 registration ID → 上报后端存入 device_tokens 表
- 后端推送时查 device_tokens → 调用极光推送 API
### 8.2 推送类型
| 类型 | 触发 | iOS | Android |
|------|------|-----|---------|
| 用药提醒 | BackgroundService | 极光 → APNs | 极光通道 |
| 复查提醒 | BackgroundService | 同上 | 同上 |
| 医生回复 | 医生发消息时 | 同上 | 同上 |
| 异常警告 | 硬件上传异常时(后期) | 同上 | 同上 |
### 8.3 点击行为
| 通知类型 | 点击后跳转 |
|---------|----------|
| 用药提醒 | 首页 + 自动打开药管家 |
| 复查提醒 | 复查详情 |
| 医生回复 | 该问诊对话 |
| 异常警告 | 该指标趋势页 |
---
## 九、AI 模型配置
| 用途 | 模型 | API 地址 |
|------|------|------|
| LLM 对话 | DeepSeek `deepseek-chat` | `https://api.deepseek.com/v1` |
| VLM 食物识别 | 千问 `qwen-vl-max` | `https://dashscope.aliyuncs.com/compatible-mode/v1` |
| VLM 备用 | 火山引擎 `doubao-vision-pro` | `https://ark.cn-beijing.volces.com/api/v3` |
---
## 十、部署
### 10.1 本地开发
- `dotnet run` 启动后端
- PostgreSQL + MinIO 用 Docker 本地拉起
- Flutter `flutter run` 连模拟器/真机调试
### 10.2 后期上云
- .NET 10 Web APIDocker 单容器部署
- PostgreSQL → 阿里云 RDS
- MinIO → 阿里云 OSS
- 配置通过环境变量注入
---
## 十一、开发顺序
### 第一阶段:基础设施
| # | 任务 |
|---|------|
| 1 | .NET 后端项目搭建 + Clean Architecture 骨架 |
| 2 | PostgreSQL 数据库初始化 + EF Core 迁移 |
| 3 | JWT 认证系统 |
| 4 | Flutter 项目搭建 + 路由 + 主题 |
| 5 | Dio 封装 + 401 拦截器 |
| 6 | OpenAiCompatibleClient 实现 |
### 第二阶段:核心 AI
| # | 任务 |
|---|------|
| 7 | PromptManager + 7 个 Agent 的 System Prompt |
| 8 | Tool Calling 引擎 |
| 9 | 默认对话 Agent + SSE 流式 |
| 10 | 记数据 Agent |
| 11 | 药管家 Agent |
| 12 | 拍饮食 Agent含 VLM |
| 13 | AI 问诊 Agent |
| 14 | 看报告 Agent |
| 15 | 运动计划 Agent |
### 第三阶段:业务功能
| # | 任务 |
|---|------|
| 16 | 健康数据 CRUD API |
| 17 | 饮食记录 API |
| 18 | 用药管理 API + 服药提醒后台任务 |
| 19 | 运动计划 API |
| 20 | 报告上传 + AI 解读 API |
| 21 | 问诊 API医生列表 + 消息轮询) |
### 第四阶段:前端
| # | 任务 |
|---|------|
| 22 | 登录页 |
| 23 | 首页骨架(抽屉 + 卡片区 + 对话流 + 输入框) |
| 24 | 6 个 Agent 面板 UI |
| 25 | SSE 流处理 + 消息气泡 |
| 26 | 数据确认卡片 / 饮食分析卡片 / 用药确认卡片 |
| 27 | 侧滑抽屉(健康概览 + 历史对话) |
| 28 | 各功能页面(个人中心、健康档案、趋势图、报告等) |
### 第五阶段:联调 + 完善
| # | 任务 |
|---|------|
| 29 | 极光推送集成 |
| 30 | MinIO 文件存储集成 |
| 31 | 短信服务对接 |
| 32 | 异常处理完善 |
| 33 | 联调测试 |