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
This commit is contained in:
MingNian
2026-06-02 11:11:29 +08:00
commit 14d7c30d3d
144 changed files with 11436 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
using Health.Domain.Enums;
namespace Health.Domain.Entities;
/// <summary>
/// 问诊会话
/// </summary>
public sealed class Consultation
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public Guid DoctorId { get; set; }
public ConsultationStatus Status { get; set; }
public int Month { get; set; } // 所属月份,用于配额计算
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? ClosedAt { get; set; }
public User User { get; set; } = null!;
public Doctor Doctor { get; set; } = null!;
public ICollection<ConsultationMessage> Messages { get; set; } = [];
}
/// <summary>
/// 问诊消息
/// </summary>
public sealed class ConsultationMessage
{
public Guid Id { get; set; }
public Guid ConsultationId { get; set; }
public ConsultationSenderType SenderType { get; set; }
public string? SenderName { get; set; }
public string Content { get; set; } = string.Empty;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public Consultation Consultation { get; set; } = null!;
}
/// <summary>
/// 医生
/// </summary>
public sealed class Doctor
{
public Guid Id { get; set; }
public string Name { get; set; } = string.Empty;
public string? Title { get; set; } // 主任医师/副主任医师
public string? Department { get; set; } // 心血管内科/营养科
public string? AvatarUrl { get; set; }
public string? Introduction { get; set; }
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public ICollection<Consultation> Consultations { get; set; } = [];
}

View File

@@ -0,0 +1,39 @@
using Health.Domain.Enums;
using System.ComponentModel.DataAnnotations.Schema;
namespace Health.Domain.Entities;
/// <summary>
/// AI 对话会话
/// </summary>
public sealed class Conversation
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public AgentType AgentType { get; set; }
public string? Title { get; set; }
public string? Summary { get; set; } // 侧滑抽屉显示用摘要
public int MessageCount { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
public ICollection<ConversationMessage> Messages { get; set; } = [];
}
/// <summary>
/// AI 对话消息
/// </summary>
public sealed class ConversationMessage
{
public Guid Id { get; set; }
public Guid ConversationId { get; set; }
public MessageRole Role { get; set; }
public string Content { get; set; } = string.Empty;
public string? Intent { get; set; } // health_record / diet / medication / exercise / report / chat
[Column(TypeName = "jsonb")]
public string? MetadataJson { get; set; } // 结构化数据(录入数值、食物列表、卡片数据等)
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public Conversation Conversation { get; set; } = null!;
}

View File

@@ -0,0 +1,39 @@
using Health.Domain.Enums;
namespace Health.Domain.Entities;
/// <summary>
/// 饮食记录
/// </summary>
public sealed class DietRecord
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public MealType MealType { get; set; }
public int? TotalCalories { get; set; }
public int? HealthScore { get; set; } // 1-5 星
public DateOnly RecordedAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
public ICollection<DietFoodItem> FoodItems { get; set; } = [];
}
/// <summary>
/// 饮食记录中的食物条目
/// </summary>
public sealed class DietFoodItem
{
public Guid Id { get; set; }
public Guid DietRecordId { get; set; }
public string Name { get; set; } = string.Empty;
public string? Portion { get; set; }
public int? Calories { get; set; }
public decimal? ProteinGrams { get; set; }
public decimal? CarbsGrams { get; set; }
public decimal? FatGrams { get; set; }
public string? Warning { get; set; }
public int SortOrder { get; set; }
public DietRecord DietRecord { get; set; } = null!;
}

View File

@@ -0,0 +1,33 @@
namespace Health.Domain.Entities;
/// <summary>
/// 运动计划(按周)
/// </summary>
public sealed class ExercisePlan
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public DateOnly WeekStartDate { get; set; } // 本周一
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
public ICollection<ExercisePlanItem> Items { get; set; } = [];
}
/// <summary>
/// 运动计划每日条目
/// </summary>
public sealed class ExercisePlanItem
{
public Guid Id { get; set; }
public Guid PlanId { get; set; }
public int DayOfWeek { get; set; } // 0=周一, 6=周日
public string ExerciseType { get; set; } = string.Empty; // 散步/慢跑/游泳
public int DurationMinutes { get; set; }
public bool IsCompleted { get; set; }
public DateTime? CompletedAt { get; set; }
public bool IsRestDay { get; set; }
public ExercisePlan Plan { get; set; } = null!;
}

View File

@@ -0,0 +1,23 @@
using Health.Domain.Enums;
namespace Health.Domain.Entities;
/// <summary>
/// 健康数据记录(血压/心率/血糖/血氧/体重)
/// </summary>
public sealed class HealthRecord
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public HealthMetricType MetricType { get; set; }
public int? Systolic { get; set; } // 血压收缩压
public int? Diastolic { get; set; } // 血压舒张压
public decimal? Value { get; set; } // 通用数值(心率/血糖/血氧/体重)
public string? Unit { get; set; } // 单位
public HealthRecordSource Source { get; set; }
public bool IsAbnormal { get; set; }
public DateTime RecordedAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}

View File

@@ -0,0 +1,41 @@
using Health.Domain.Enums;
namespace Health.Domain.Entities;
/// <summary>
/// 用药计划
/// </summary>
public sealed class Medication
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Name { get; set; } = string.Empty;
public string? Dosage { get; set; }
public MedicationFrequency Frequency { get; set; }
public List<TimeOnly> TimeOfDay { get; set; } = []; // PostgreSQL TIME[] 数组
public DateOnly? StartDate { get; set; }
public DateOnly? EndDate { get; set; }
public bool IsActive { get; set; } = true;
public MedicationSource Source { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
public ICollection<MedicationLog> Logs { get; set; } = [];
}
/// <summary>
/// 用药打卡记录
/// </summary>
public sealed class MedicationLog
{
public Guid Id { get; set; }
public Guid MedicationId { get; set; }
public Guid UserId { get; set; }
public MedicationLogStatus Status { get; set; }
public TimeOnly ScheduledTime { get; set; }
public DateTime? ConfirmedAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public Medication Medication { get; set; } = null!;
}

View File

@@ -0,0 +1,26 @@
using Health.Domain.Enums;
using System.ComponentModel.DataAnnotations.Schema;
namespace Health.Domain.Entities;
/// <summary>
/// 检查报告
/// </summary>
public sealed class Report
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string FileUrl { get; set; } = string.Empty;
public ReportFileType FileType { get; set; }
public ReportCategory Category { get; set; }
public string? AiSummary { get; set; } // AI 预解读结果
[Column(TypeName = "jsonb")]
public string? AiIndicators { get; set; } // JSONB: [{name, value, unit, range, status}]
public ReportStatus Status { get; set; }
public string? DoctorComment { get; set; } // 医生审核意见
public string? DoctorName { get; set; }
public DateTime? ReviewedAt { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}

View File

@@ -0,0 +1,99 @@
using Health.Domain.Enums;
using System.ComponentModel.DataAnnotations.Schema;
namespace Health.Domain.Entities;
/// <summary>
/// 复查/随访计划
/// </summary>
public sealed class FollowUp
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Title { get; set; } = string.Empty;
public string? DoctorName { get; set; }
public string? Department { get; set; }
public DateTime ScheduledAt { get; set; }
public string? Notes { get; set; }
public FollowUpStatus Status { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}
/// <summary>
/// 健康档案
/// </summary>
public sealed class HealthArchive
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string? Diagnosis { get; set; } // 主要诊断
public string? SurgeryType { get; set; } // 手术类型
public DateOnly? SurgeryDate { get; set; } // 手术日期
public List<string> Allergies { get; set; } = []; // 过敏信息
public List<string> DietRestrictions { get; set; } = []; // 饮食限制
public List<string> ChronicDiseases { get; set; } = []; // 慢病史
public string? FamilyHistory { get; set; } // 家族病史
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}
/// <summary>
/// 刷新令牌
/// </summary>
public sealed class RefreshToken
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Token { get; set; } = string.Empty;
public DateTime ExpiresAt { get; set; }
public bool IsRevoked { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// 短信验证码
/// </summary>
public sealed class VerificationCode
{
public Guid Id { get; set; }
public string Phone { get; set; } = string.Empty;
public string Code { get; set; } = string.Empty;
public DateTime ExpiresAt { get; set; }
public bool IsUsed { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}
/// <summary>
/// 通知偏好
/// </summary>
public sealed class NotificationPreference
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public bool MedicationReminder { get; set; } = true;
public bool FollowUpReminder { get; set; } = true;
public bool DoctorReply { get; set; } = true;
public bool AbnormalAlert { get; set; } = true;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}
/// <summary>
/// 设备推送 token
/// </summary>
public sealed class DeviceToken
{
public Guid Id { get; set; }
public Guid UserId { get; set; }
public string Platform { get; set; } = string.Empty; // ios / android
public string PushToken { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
public User User { get; set; } = null!;
}

View File

@@ -0,0 +1,29 @@
namespace Health.Domain.Entities;
/// <summary>
/// 用户(患者)
/// </summary>
public sealed class User
{
public Guid Id { get; set; }
public string Phone { get; set; } = string.Empty;
public string? Name { get; set; }
public string? Gender { get; set; }
public DateOnly? BirthDate { get; set; }
public string? AvatarUrl { get; set; }
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
// 导航属性
public ICollection<HealthRecord> HealthRecords { get; set; } = [];
public ICollection<Medication> Medications { get; set; } = [];
public ICollection<DietRecord> DietRecords { get; set; } = [];
public ICollection<ExercisePlan> ExercisePlans { get; set; } = [];
public ICollection<Report> Reports { get; set; } = [];
public ICollection<Conversation> Conversations { get; set; } = [];
public ICollection<Consultation> Consultations { get; set; } = [];
public ICollection<FollowUp> FollowUps { get; set; } = [];
public ICollection<DeviceToken> DeviceTokens { get; set; } = [];
public HealthArchive? HealthArchive { get; set; }
public NotificationPreference? NotificationPreference { get; set; }
}

View File

@@ -0,0 +1,152 @@
namespace Health.Domain.Enums;
/// <summary>
/// 健康指标类型
/// </summary>
public enum HealthMetricType
{
BloodPressure, // 血压(收缩压+舒张压)
HeartRate, // 心率
Glucose, // 血糖
SpO2, // 血氧
Weight // 体重
}
/// <summary>
/// 数据录入来源
/// </summary>
public enum HealthRecordSource
{
AiEntry, // AI 对话录入
DeviceSync, // 设备自动同步
Manual // 手动录入
}
/// <summary>
/// 餐次类型
/// </summary>
public enum MealType
{
Breakfast, // 早餐
Lunch, // 午餐
Dinner, // 晚餐
Snack // 加餐
}
/// <summary>
/// 用药计划来源
/// </summary>
public enum MedicationSource
{
Prescription, // 处方
AiEntry, // AI 对话
Manual // 手动
}
/// <summary>
/// 服药打卡状态
/// </summary>
public enum MedicationLogStatus
{
Taken, // 已服用
Missed, // 漏服
Skipped // 跳过
}
/// <summary>
/// 用药频次
/// </summary>
public enum MedicationFrequency
{
Daily, // 每天一次
TwiceDaily, // 每天两次
ThreeTimesDaily, // 每天三次
Weekly, // 每周
AsNeeded // 必要时
}
/// <summary>
/// 报告状态
/// </summary>
public enum ReportStatus
{
PendingDoctor, // 待医生确认
DoctorReviewed // 医生已确认
}
/// <summary>
/// 报告文件类型
/// </summary>
public enum ReportFileType
{
Image, // 图片
Pdf // PDF
}
/// <summary>
/// 报告类别
/// </summary>
public enum ReportCategory
{
BloodTest, // 血常规
Biochemistry, // 生化全项
Ecg, // 心电图
Ultrasound, // 彩超
Discharge, // 出院小结
Other // 其他
}
/// <summary>
/// 问诊会话状态
/// </summary>
public enum ConsultationStatus
{
AiTalking, // AI 分身对话中
WaitingDoctor, // 等待医生
DoctorReplied, // 医生已回复
Closed // 已结束
}
/// <summary>
/// 问诊消息发送方类型
/// </summary>
public enum ConsultationSenderType
{
User, // 患者
Doctor, // 医生
Ai // AI 分身
}
/// <summary>
/// 复查随访状态
/// </summary>
public enum FollowUpStatus
{
Upcoming, // 即将到来
Completed, // 已完成
Cancelled // 已取消
}
/// <summary>
/// AI Agent 类型
/// </summary>
public enum AgentType
{
Default, // 默认对话
Consultation, // AI 问诊
Health, // 记数据
Diet, // 拍饮食
Medication, // 药管家
Report, // 看报告
Exercise // 运动计划
}
/// <summary>
/// 对话消息角色
/// </summary>
public enum MessageRole
{
User, // 用户
Assistant, // AI
Tool // 工具返回
}

View File

@@ -0,0 +1,9 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>