feat: 饮食记录逻辑重构 + 通知管理分隔线修复 + 多页面 UI 调整
- 饮食: 新增 DietCommentaryPolicy + diet_record_logic, 饮食记录逻辑抽取复用 - 通知管理: 分隔线改为仿通知中心样式, 跳过图标区域只留文字下方 - 侧边栏: 对话记录操作栏浮层修复 + 常用功能间距收紧 - 智能体: 欢迎卡片去 400ms 延迟 + 胶囊去阴影 - 后端: AI 会话仓库新增方法 + 饮食评论策略 + 健康记录规则调整 - 其他: api_client IP 适配 + 多页面 UI 微调 + 新增测试
This commit is contained in:
@@ -335,6 +335,49 @@ public static class AiChatEndpoints
|
||||
return Results.Ok(new { code = 0, data = new { deleted = count }, message = (string?)null });
|
||||
});
|
||||
|
||||
// 饮食分析页专用建议:不创建会话,不保存提示词或回复到历史记录。
|
||||
app.MapPost("/api/ai/diet-commentary", async (
|
||||
DietCommentaryRequest request,
|
||||
HttpContext http,
|
||||
DeepSeekClient llmClient,
|
||||
IPatientContextService patientContexts,
|
||||
CancellationToken ct) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
if (userId == null)
|
||||
return Results.Json(new { code = 40002, data = (object?)null, message = "未登录" }, statusCode: 401);
|
||||
|
||||
var foods = request.Foods?
|
||||
.Where(food => !string.IsNullOrWhiteSpace(food.Name) &&
|
||||
!string.IsNullOrWhiteSpace(food.Portion) &&
|
||||
food.Calories > 0)
|
||||
.Take(20)
|
||||
.Select(food => new DietCommentaryFood(food.Name, food.Portion, food.Calories))
|
||||
.ToList() ?? [];
|
||||
if (foods.Count == 0)
|
||||
return Results.Ok(new { code = 40001, data = (object?)null, message = "请先完善食物信息" });
|
||||
|
||||
var patientContext = await patientContexts.BuildAsync(userId.Value, ct);
|
||||
var foodDescription = DietCommentaryPolicy.BuildFoodDescription(foods);
|
||||
var response = await llmClient.ChatAsync(
|
||||
[
|
||||
new ChatMessage
|
||||
{
|
||||
Role = "system",
|
||||
Content = DietCommentaryPolicy.SystemPrompt + "\n\n当前用户健康档案:\n" + patientContext,
|
||||
},
|
||||
new ChatMessage { Role = "user", Content = "本餐食物:" + foodDescription },
|
||||
],
|
||||
maxTokens: 300,
|
||||
temperature: 0.3f,
|
||||
ct: ct);
|
||||
var commentary = response.Choices?.FirstOrDefault()?.Message?.Content?.Trim();
|
||||
if (string.IsNullOrWhiteSpace(commentary))
|
||||
return Results.Ok(new { code = 50001, data = (object?)null, message = "饮食建议生成失败" });
|
||||
|
||||
return Results.Ok(new { code = 0, data = new { commentary }, message = (string?)null });
|
||||
}).RequireAuthorization();
|
||||
|
||||
app.MapPost("/api/ai/analyze-food-image", async (
|
||||
HttpRequest httpRequest,
|
||||
HttpContext http,
|
||||
@@ -623,7 +666,7 @@ public static class AiChatEndpoints
|
||||
AddMetricPreview(preview, HealthMetricType.HeartRate, GetDecimal(args, "heart_rate"), "次/分", v => v > 100 || v < 60);
|
||||
break;
|
||||
case "glucose":
|
||||
AddMetricPreview(preview, HealthMetricType.Glucose, GetDecimal(args, "glucose"), "mmol/L", v => v >= 7.0m || v <= 3.8m);
|
||||
AddMetricPreview(preview, HealthMetricType.Glucose, GetDecimal(args, "glucose"), "mmol/L", v => v > 6.1m || v < 3.9m);
|
||||
break;
|
||||
case "spo2":
|
||||
AddMetricPreview(preview, HealthMetricType.SpO2, GetDecimal(args, "spo2"), "%", v => v <= 94);
|
||||
@@ -818,3 +861,6 @@ public static class AiChatEndpoints
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public sealed record DietCommentaryFoodRequest(string Name, string Portion, int Calories);
|
||||
public sealed record DietCommentaryRequest(IReadOnlyList<DietCommentaryFoodRequest>? Foods);
|
||||
|
||||
Reference in New Issue
Block a user