feat: 引入 EF Core Migrations + 后台任务/校验修复 + 前端四态
后端 - 改用 EF Core Migrations(InitialCreate),Program.cs 用 MigrateAsync + AUTO_MIGRATE 开关 + 显式 MigrationsAssembly;移除 EnsureCreated、删除手写 DatabaseSchemaMigrator - 修复后台任务永久卡 Processing:三个队列对超时且达上限的任务原子标记 Failed - 修复报告重试失效:基础设施异常向上抛激活 RetryAsync,停机取消不计失败 - 修复 AI 用药天数 off-by-one(结束日为包含式) - AI 报告日志不再记录 VLM 健康正文 - 新增统一输入校验(ValidationException + 中间件映射 400):血压/心率/血糖/血氧/体重范围、药名/日期/服药时间去重、饮食热量与评分、运动时长上限 - 删除健康数据 AI 录入的影子直写路径,统一走 Service 校验 前端 - 新增 AppErrorState / AppFutureView,用药列表、服药打卡、运动计划、随访列表改为 加载/失败/空/数据 四态,失败可重试 瘦身 - 删除过时的 DataSeeder / DevDataSeeder 及调用 - 删除依赖实时服务的 ai_agent_tests 集成测试
This commit is contained in:
@@ -79,7 +79,9 @@ builder.Services.ConfigureHttpJsonOptions(options =>
|
||||
|
||||
// ---- 数据库 ----
|
||||
builder.Services.AddDbContext<AppDbContext>(options =>
|
||||
options.UseNpgsql(builder.Configuration.GetConnectionString("Default") ?? builder.Configuration["DB_CONNECTION"] ?? "Host=localhost;Database=health_manager;Username=postgres;Password=postgres"));
|
||||
options.UseNpgsql(
|
||||
builder.Configuration.GetConnectionString("Default") ?? builder.Configuration["DB_CONNECTION"] ?? "Host=localhost;Database=health_manager;Username=postgres;Password=postgres",
|
||||
npgsql => npgsql.MigrationsAssembly(typeof(AppDbContext).Assembly.FullName)));
|
||||
|
||||
// ---- JWT 认证 ----
|
||||
var jwtSecret = builder.Configuration["JWT_SECRET"];
|
||||
@@ -137,6 +139,8 @@ builder.Services.AddScoped<IMaintenanceService, MaintenanceService>();
|
||||
builder.Services.AddScoped<IMaintenanceRepository, EfMaintenanceRepository>();
|
||||
builder.Services.AddScoped<IInAppNotificationService, InAppNotificationService>();
|
||||
builder.Services.AddScoped<IInAppNotificationRepository, EfInAppNotificationRepository>();
|
||||
builder.Services.AddScoped<IUserNotificationProducer, EfUserNotificationProducer>();
|
||||
builder.Services.AddScoped<INotificationOutboxProcessor, EfNotificationOutboxProcessor>();
|
||||
builder.Services.AddScoped<IReportService, ReportService>();
|
||||
builder.Services.AddScoped<IReportRepository, EfReportRepository>();
|
||||
builder.Services.AddScoped<IReportFileStorage, LocalReportFileStorage>();
|
||||
@@ -144,7 +148,6 @@ builder.Services.AddScoped<IReportAnalysisService, ReportAnalysisService>();
|
||||
builder.Services.AddScoped<IReportAnalysisQueue, EfReportAnalysisQueue>();
|
||||
builder.Services.AddScoped<IUserService, UserService>();
|
||||
builder.Services.AddScoped<IUserRepository, EfUserRepository>();
|
||||
builder.Services.AddScoped<DatabaseSchemaMigrator>();
|
||||
|
||||
// ---- AI 客户端(使用 IHttpClientFactory 区分 LLM 和 VLM)----
|
||||
builder.Services.AddHttpClient<DeepSeekClient>(client =>
|
||||
@@ -167,6 +170,7 @@ builder.Services.AddHostedService<MedicationReminderWorker>();
|
||||
builder.Services.AddHostedService<CleanupService>();
|
||||
builder.Services.AddHostedService<ReportAnalysisWorker>();
|
||||
builder.Services.AddHostedService<DietImageAnalysisWorker>();
|
||||
builder.Services.AddHostedService<NotificationOutboxWorker>();
|
||||
|
||||
// ---- OpenAPI ----
|
||||
builder.Services.AddOpenApi();
|
||||
@@ -202,14 +206,12 @@ app.UseStaticFiles(new StaticFileOptions
|
||||
if (app.Environment.IsDevelopment())
|
||||
app.MapOpenApi();
|
||||
|
||||
// ---- 初始化数据库:创建空库,并用版本化补丁更新已有本地结构 ----
|
||||
// ---- 数据库迁移(EF Core Migrations,可用 AUTO_MIGRATE=false 禁用自动迁移)----
|
||||
using (var scope = app.Services.CreateScope())
|
||||
{
|
||||
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
|
||||
await db.Database.EnsureCreatedAsync();
|
||||
await scope.ServiceProvider.GetRequiredService<DatabaseSchemaMigrator>().ApplyAsync();
|
||||
await DataSeeder.SeedAsync(db);
|
||||
await DevDataSeeder.SeedIfEnabled(db, app.Configuration);
|
||||
if (app.Configuration.GetValue<bool>("AUTO_MIGRATE", true))
|
||||
await db.Database.MigrateAsync();
|
||||
}
|
||||
|
||||
// ---- 注册 API 端点 ----
|
||||
|
||||
Reference in New Issue
Block a user