feat: AI 提示词模块化 + AI 同意门控 + AI 草稿存储 + 意图路由 + 医疗引用知识库 + 多页面 UI 优化

- AI 提示词拆分为 markdown 模块(Prompts/global + modules + rag + router)
- 新增 AI 同意门控(用户需同意后才能使用 AI 功能)
- 新增 AI 录入草稿存储(AiEntryDraftContracts/EfAiEntryDraftStore/AiEntryDraftRecord)
- 新增 AI 意图路由(ai_intent_router)
- 新增医疗引用知识库(MedicalCitationKnowledge)
- 重构 prompt_manager 和 ai_chat_endpoints
- 优化聊天/趋势/档案/抽屉/医生端等多个页面 UI
- 新增 agent 插画和趋势指标图标资源
- 删除 HANDOFF-2026-07-17.md
This commit is contained in:
MingNian
2026-07-27 16:53:20 +08:00
parent ad93e38b7e
commit 3b5cec10a6
80 changed files with 4358 additions and 1339 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -20,11 +20,12 @@ public static class UserEndpoints
var birthDate = DateOnly.TryParse(req.BirthDate, out var parsed) ? parsed : (DateOnly?)null;
var updated = await users.UpdateProfileAsync(
GetUserId(http),
new UserProfileUpdateRequest(req.Name, req.Gender, birthDate),
new UserProfileUpdateRequest(req.Name, req.Gender, birthDate, req.AvatarUrl),
ct);
if (!updated) return Results.Ok(new { code = 40004, message = "用户不存在" });
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
var profile = await users.GetProfileAsync(GetUserId(http), ct);
return Results.Ok(new { code = 0, data = profile, message = (string?)null });
});
group.MapGet("/health-archive", async (HttpContext http, IHealthArchiveService archives, CancellationToken ct) =>
@@ -66,7 +67,7 @@ public static class UserEndpoints
Guid.TryParse(http.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value, out var id) ? id : Guid.Empty;
}
public sealed record UpdateProfileRequest(string? Name, string? Gender, string? BirthDate);
public sealed record UpdateProfileRequest(string? Name, string? Gender, string? BirthDate, string? AvatarUrl);
public sealed record UpdateArchiveRequest(
string? Diagnosis, string? SurgeryType, string? SurgeryDate,
List<string>? Allergies, List<string>? DietRestrictions,

View File

@@ -16,6 +16,10 @@ public sealed class ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionM
{
await _next(context);
}
catch (OperationCanceledException) when (context.RequestAborted.IsCancellationRequested)
{
// 客户端或启动探针取消请求时,不再尝试写入错误响应。
}
catch (Health.Domain.ValidationException vex)
{
// 业务输入校验失败:返回 400message 为我们自己产生的提示,可安全展示
@@ -37,6 +41,7 @@ public sealed class ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionM
{
// 生产环境不暴露内部异常详情
_logger.LogError(ex, "未处理的异常: {Path}", context.Request.Path);
if (context.Response.HasStarted) return;
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.ContentType = "application/json";

View File

@@ -112,6 +112,7 @@ builder.Services.AddSingleton<SmsService>();
builder.Services.AddSingleton<AppleTokenValidator>(); // Apple Sign-In JWT 验证
builder.Services.AddSingleton<PromptManager>();
builder.Services.AddScoped<IAiWriteConfirmationStore, EfAiWriteConfirmationStore>();
builder.Services.AddScoped<IAiEntryDraftStore, EfAiEntryDraftStore>();
builder.Services.AddScoped<IAiToolExecutionService, AiToolExecutionService>();
builder.Services.AddScoped<IAdminService, AdminService>();
builder.Services.AddScoped<IAuthService, AuthService>();
@@ -165,8 +166,10 @@ builder.Services.AddHttpClient<DeepSeekClient>(client =>
});
builder.Services.AddHttpClient<VisionClient>(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"] ?? "");
var baseUrl = builder.Configuration["VLM_BASE_URL"] ?? builder.Configuration["QWEN_BASE_URL"] ?? "https://dashscope.aliyuncs.com/compatible-mode/v1";
var apiKey = builder.Configuration["VLM_API_KEY"] ?? builder.Configuration["QWEN_API_KEY"] ?? "";
client.BaseAddress = new Uri(baseUrl.TrimEnd('/') + "/");
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", apiKey);
client.Timeout = TimeSpan.FromSeconds(120);
});
builder.Services.AddHttpClient<FastGptKnowledgeClient>(client =>

View File

@@ -14,7 +14,8 @@
"JWT_AUDIENCE": "health-manager-app",
"DEEPSEEK_BASE_URL": "https://api.deepseek.com/v1",
"DEEPSEEK_API_KEY": "sk-your-key-here",
"DEEPSEEK_MODEL": "deepseek-chat",
"DEEPSEEK_MODEL": "deepseek-v4-flash",
"ENABLE_MEDICAL_RAG": false,
"QWEN_BASE_URL": "https://dashscope.aliyuncs.com/compatible-mode/v1",
"QWEN_API_KEY": "sk-your-key-here",
"QWEN_VISION_MODEL": "qwen-vl-max",