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"); }); } }