using System.Text; using Health.Infrastructure.AI; using Health.Infrastructure.Data; using Health.Infrastructure.Services; using Health.WebApi.BackgroundServices; using Health.WebApi.Endpoints; using Health.WebApi.Middleware; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.EntityFrameworkCore; using Microsoft.IdentityModel.Tokens; // 加载 .env 文件(开发环境) var envPath = Path.Combine(Directory.GetCurrentDirectory(), "..", "..", ".env"); if (File.Exists(envPath)) { foreach (var line in File.ReadAllLines(envPath)) { var trimmed = line.Trim(); if (string.IsNullOrEmpty(trimmed) || trimmed.StartsWith('#')) continue; var eqIdx = trimmed.IndexOf('='); if (eqIdx <= 0) continue; var key = trimmed[..eqIdx].Trim(); var value = trimmed[(eqIdx + 1)..].Trim(); Environment.SetEnvironmentVariable(key, value); } } var builder = WebApplication.CreateBuilder(args); // ---- JSON 配置(枚举字符串、camelCase)---- builder.Services.ConfigureHttpJsonOptions(options => { options.SerializerOptions.Converters.Add(new System.Text.Json.Serialization.JsonStringEnumConverter()); options.SerializerOptions.PropertyNamingPolicy = System.Text.Json.JsonNamingPolicy.CamelCase; options.SerializerOptions.PropertyNameCaseInsensitive = true; }); // ---- 数据库 ---- builder.Services.AddDbContext(options => options.UseNpgsql(builder.Configuration.GetConnectionString("Default") ?? builder.Configuration["DB_CONNECTION"] ?? "Host=localhost;Database=health_manager;Username=postgres;Password=postgres")); // ---- JWT 认证 ---- var jwtSecret = builder.Configuration["JWT_SECRET"]; if (string.IsNullOrEmpty(jwtSecret) && !builder.Environment.IsDevelopment()) throw new InvalidOperationException("JWT_SECRET 环境变量未配置,生产环境必须设置"); jwtSecret ??= "dev-secret-key-change-in-production-min-32-chars!!"; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["JWT_ISSUER"] ?? "health-manager", ValidAudience = builder.Configuration["JWT_AUDIENCE"] ?? "health-manager-app", IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSecret)), ClockSkew = TimeSpan.Zero }; }); builder.Services.AddAuthorization(); // ---- 业务服务 ---- builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); // ---- AI 客户端(使用 IHttpClientFactory 区分 LLM 和 VLM)---- builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri((builder.Configuration["DEEPSEEK_BASE_URL"] ?? "https://api.deepseek.com/v1").TrimEnd('/') + "/"); client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", builder.Configuration["DEEPSEEK_API_KEY"] ?? ""); client.Timeout = TimeSpan.FromSeconds(60); }); builder.Services.AddHttpClient(client => { client.BaseAddress = new Uri((builder.Configuration["VLM_BASE_URL"] ?? "https://dashscope.aliyuncs.com/compatible-mode/v1").TrimEnd('/') + "/"); client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", builder.Configuration["VLM_API_KEY"] ?? ""); client.Timeout = TimeSpan.FromSeconds(120); }); // ---- 后台服务 ---- builder.Services.AddHostedService(); builder.Services.AddHostedService(); // ---- OpenAPI ---- builder.Services.AddOpenApi(); // ---- SignalR ---- builder.Services.AddSignalR(); // ---- CORS(SignalR 需要明确 origins + AllowCredentials)---- builder.Services.AddCors(options => { options.AddDefaultPolicy(policy => { policy.SetIsOriginAllowed(_ => true).AllowAnyMethod().AllowAnyHeader().AllowCredentials(); }); }); var app = builder.Build(); // ---- 中间件管道(ExceptionMiddleware 放最前面)---- app.UseMiddleware(); app.UseCors(); app.UseAuthentication(); app.UseAuthorization(); if (app.Environment.IsDevelopment()) app.MapOpenApi(); // ---- 初始化数据库(开发环境:每次重建)---- using (var scope = app.Services.CreateScope()) { var db = scope.ServiceProvider.GetRequiredService(); await db.Database.EnsureCreatedAsync(); await DataSeeder.SeedAsync(db); await DevDataSeeder.SeedIfEnabled(db, app.Configuration); } // ---- 注册 API 端点 ---- app.MapAuthEndpoints(); app.MapHealthEndpoints(); app.MapDietEndpoints(); app.MapMedicationEndpoints(); app.MapReportEndpoints(); app.MapConsultationEndpoints(); app.MapExerciseEndpoints(); app.MapUserEndpoints(); app.MapAiChatEndpoints(); app.MapFileEndpoints(); app.MapCalendarEndpoints(); app.MapDoctorEndpoints(); app.MapFollowUpEndpoints(); // SignalR Hub app.MapHub("/hubs/consultation"); app.Run();