using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
using Health.Infrastructure.Data.Records;
namespace Health.Infrastructure.Data;
///
/// 应用程序数据库上下文
///
public sealed class AppDbContext(DbContextOptions options) : DbContext(options)
{
// 核心业务表
public DbSet Users => Set();
public DbSet HealthRecords => Set();
public DbSet Medications => Set();
public DbSet MedicationLogs => Set();
public DbSet DietRecords => Set();
public DbSet DietFoodItems => Set();
public DbSet ExercisePlans => Set();
public DbSet ExercisePlanItems => Set();
public DbSet Reports => Set();
public DbSet Conversations => Set();
public DbSet ConversationMessages => Set();
public DbSet Consultations => Set();
public DbSet ConsultationMessages => Set();
public DbSet Doctors => Set();
public DbSet DoctorProfiles => Set();
public DbSet FollowUps => Set();
public DbSet HealthArchives => Set();
// 支撑表
public DbSet RefreshTokens => Set();
public DbSet VerificationCodes => Set();
public DbSet NotificationPreferences => Set();
public DbSet DeviceTokens => Set();
public DbSet AiWriteCommands => Set();
public DbSet ReportAnalysisTasks => Set();
public DbSet DietImageAnalysisTasks => Set();
public DbSet MedicationReminderTasks => Set();
public DbSet NotificationOutbox => Set();
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// ---- User ----
builder.Entity(e =>
{
e.HasIndex(u => u.Phone).IsUnique();
e.Property(u => u.Role).HasMaxLength(32).HasDefaultValue("User");
e.HasOne(u => u.Doctor).WithMany().HasForeignKey(u => u.DoctorId).IsRequired(false).OnDelete(DeleteBehavior.SetNull);
});
// ---- Doctor ----
builder.Entity(e =>
{
e.Property(d => d.Phone).HasMaxLength(20);
e.Property(d => d.ProfessionalDirection).HasMaxLength(256);
});
// ---- DoctorProfile ----
builder.Entity(e =>
{
e.HasIndex(d => d.UserId).IsUnique();
e.HasOne(d => d.User).WithOne(u => u.DoctorProfile).HasForeignKey(d => d.UserId);
e.HasOne(d => d.Doctor).WithMany().HasForeignKey(d => d.DoctorId).IsRequired(false).OnDelete(DeleteBehavior.SetNull);
});
// ---- HealthRecord ----
builder.Entity(e =>
{
e.HasIndex(r => new { r.UserId, r.RecordedAt }).IsDescending(false, true);
e.HasIndex(r => new { r.UserId, r.MetricType });
e.Property(r => r.MetricType).HasConversion();
e.Property(r => r.Source).HasConversion();
});
// ---- Medication ----
builder.Entity(e =>
{
e.HasIndex(m => new { m.UserId, m.IsActive });
e.Property(m => m.Frequency).HasConversion();
e.Property(m => m.Source).HasConversion();
e.Property(m => m.TimeOfDay).HasColumnType("time[]");
});
builder.Entity(e =>
{
e.HasIndex(l => new { l.MedicationId, l.CreatedAt }).IsDescending(false, true);
e.Property(l => l.Status).HasConversion();
});
// ---- Diet ----
builder.Entity(e =>
{
e.HasIndex(d => new { d.UserId, d.RecordedAt }).IsDescending(false, true);
e.Property(d => d.MealType).HasConversion();
});
// ---- ExercisePlan ----
builder.Entity(e =>
{
e.HasIndex(p => new { p.UserId, p.StartDate, p.EndDate });
});
builder.Entity(e =>
{
e.HasIndex(i => new { i.PlanId, i.ScheduledDate }).IsUnique();
});
// ---- Report ----
builder.Entity(e =>
{
e.Property(r => r.FileType).HasConversion();
e.Property(r => r.Category).HasConversion();
e.Property(r => r.Status).HasConversion();
});
// ---- Conversation ----
builder.Entity(e =>
{
e.HasIndex(c => new { c.UserId, c.UpdatedAt }).IsDescending(false, true);
e.Property(c => c.AgentType).HasConversion();
});
builder.Entity(e =>
{
e.HasIndex(m => new { m.ConversationId, m.CreatedAt });
e.Property(m => m.Role).HasConversion();
});
// ---- Consultation ----
builder.Entity(e =>
{
e.Property(c => c.Status).HasConversion();
});
builder.Entity(e =>
{
e.HasIndex(m => new { m.ConsultationId, m.CreatedAt }).IsDescending(false, true);
e.Property(m => m.SenderType).HasConversion();
});
// ---- VerificationCode ----
builder.Entity(e =>
{
e.HasIndex(v => new { v.Phone, v.CreatedAt }).IsDescending(false, true);
});
// ---- NotificationPreference ----
builder.Entity(e =>
{
e.HasIndex(n => n.UserId).IsUnique();
});
// ---- HealthArchive ----
builder.Entity(e =>
{
e.HasIndex(a => a.UserId).IsUnique();
});
builder.Entity(e =>
{
e.ToTable("AiWriteCommands");
e.HasIndex(x => new { x.UserId, x.Status, x.ExpiresAt });
e.Property(x => x.ToolName).HasMaxLength(64);
e.Property(x => x.Status).HasMaxLength(32);
e.Property(x => x.Arguments).HasColumnType("jsonb");
});
builder.Entity(e =>
{
e.ToTable("ReportAnalysisTasks");
e.HasIndex(x => new { x.Status, x.AvailableAt });
e.HasIndex(x => x.ReportId);
e.Property(x => x.Status).HasMaxLength(32);
});
builder.Entity(e =>
{
e.ToTable("DietImageAnalysisTasks");
e.HasIndex(x => new { x.Status, x.AvailableAt });
e.Property(x => x.FilePaths).HasColumnType("jsonb");
e.Property(x => x.Status).HasMaxLength(32);
});
builder.Entity(e =>
{
e.ToTable("MedicationReminderTasks");
e.HasIndex(x => new { x.Status, x.AvailableAt });
e.HasIndex(x => new { x.MedicationId, x.ScheduledDate, x.ScheduledTime }).IsUnique();
e.Property(x => x.Status).HasMaxLength(32);
});
builder.Entity(e =>
{
e.ToTable("NotificationOutbox");
e.HasIndex(x => x.SourceTaskId).IsUnique();
e.HasIndex(x => new { x.Status, x.AvailableAt });
e.Property(x => x.Type).HasMaxLength(64);
e.Property(x => x.Status).HasMaxLength(32);
e.Property(x => x.Payload).HasColumnType("jsonb");
});
}
}