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

41 KiB
Raw Permalink Blame History

健康管家 — 技术设计文档 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 返回统一结构:

// 成功
{ "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
  • 自动重试
// 调用示例
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 对话的核心编排逻辑:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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 索引设计

-- 健康记录:按用户+时间查询高频
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_token30 分钟,前端内存持有
  • refresh_token30 天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_typedefault / 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(支持多张)

响应:

{
  "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 模式:

// 认证
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 请求层

// 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 模式:

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 依赖

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 每分钟扫描一次:

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 联调测试