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:
53
backend/src/Health.Domain/Entities/Consultation.cs
Normal file
53
backend/src/Health.Domain/Entities/Consultation.cs
Normal 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; } = [];
|
||||
}
|
||||
39
backend/src/Health.Domain/Entities/Conversation.cs
Normal file
39
backend/src/Health.Domain/Entities/Conversation.cs
Normal 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!;
|
||||
}
|
||||
39
backend/src/Health.Domain/Entities/DietRecord.cs
Normal file
39
backend/src/Health.Domain/Entities/DietRecord.cs
Normal 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!;
|
||||
}
|
||||
33
backend/src/Health.Domain/Entities/ExercisePlan.cs
Normal file
33
backend/src/Health.Domain/Entities/ExercisePlan.cs
Normal 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!;
|
||||
}
|
||||
23
backend/src/Health.Domain/Entities/HealthRecord.cs
Normal file
23
backend/src/Health.Domain/Entities/HealthRecord.cs
Normal 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!;
|
||||
}
|
||||
41
backend/src/Health.Domain/Entities/Medication.cs
Normal file
41
backend/src/Health.Domain/Entities/Medication.cs
Normal 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!;
|
||||
}
|
||||
26
backend/src/Health.Domain/Entities/Report.cs
Normal file
26
backend/src/Health.Domain/Entities/Report.cs
Normal 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!;
|
||||
}
|
||||
99
backend/src/Health.Domain/Entities/SupportEntities.cs
Normal file
99
backend/src/Health.Domain/Entities/SupportEntities.cs
Normal 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!;
|
||||
}
|
||||
29
backend/src/Health.Domain/Entities/User.cs
Normal file
29
backend/src/Health.Domain/Entities/User.cs
Normal 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; }
|
||||
}
|
||||
152
backend/src/Health.Domain/Enums/HealthEnums.cs
Normal file
152
backend/src/Health.Domain/Enums/HealthEnums.cs
Normal 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 // 工具返回
|
||||
}
|
||||
9
backend/src/Health.Domain/Health.Domain.csproj
Normal file
9
backend/src/Health.Domain/Health.Domain.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user