fix: 修复 Flutter 前端多项功能 + 后端运动计划 API

- Android 添加相机/存储权限,拍照和相册功能可用
- AI 回复支持 Markdown 渲染(加粗/表格不再显示**乱码)
- 附件按钮接线,支持拍照/相册/文件选择
- 智能体面板按钮全部接线(拍照/上传/手动录入/导航)
- 侧边栏 AI 录入后自动刷新健康数据
- 运动计划页增加创建按钮 + 打卡功能
- 后端运动计划支持 AI 创建和打卡(Tool Calling)
- 修复 CreateExercisePlanRequest JSON 反序列化
This commit is contained in:
MingNian
2026-06-02 16:34:36 +08:00
parent df263baa5d
commit 498708e568
11 changed files with 212 additions and 18 deletions

View File

@@ -470,13 +470,44 @@ public static class AiChatEndpoints
private static async Task<object> ExecuteManageExercise(AppDbContext db, Guid userId, JsonElement args)
{
var action = args.TryGetProperty("action", out var a) ? a.GetString()! : "query";
if (action != "query") return new { success = false, message = "运动计划管理暂未实现" };
switch (action)
{
case "create":
var weekStart = args.TryGetProperty("week_start_date", out var wsd) ? DateOnly.Parse(wsd.GetString()!) : DateOnly.FromDateTime(DateTime.Now);
var plan = new ExercisePlan { Id = Guid.NewGuid(), UserId = userId, WeekStartDate = weekStart };
if (args.TryGetProperty("items", out var items))
{
foreach (var item in items.EnumerateArray())
{
plan.Items.Add(new ExercisePlanItem
{
Id = Guid.NewGuid(), DayOfWeek = item.GetProperty("day_of_week").GetInt32(),
ExerciseType = item.GetProperty("exercise_type").GetString() ?? "散步",
DurationMinutes = item.GetProperty("duration_minutes").GetInt32(),
IsRestDay = item.TryGetProperty("is_rest_day", out var rd) && rd.GetBoolean(),
});
}
}
db.ExercisePlans.Add(plan);
await db.SaveChangesAsync();
return new { success = true, plan_id = plan.Id };
var plan = await db.ExercisePlans.Where(p => p.UserId == userId)
.OrderByDescending(p => p.WeekStartDate).FirstOrDefaultAsync();
if (plan == null) return new { found = false };
var items = await db.ExercisePlanItems.Where(i => i.PlanId == plan.Id).OrderBy(i => i.DayOfWeek).ToListAsync();
return new { found = true, plan_id = plan.Id, items = items.Select(i => new { i.DayOfWeek, i.ExerciseType, i.DurationMinutes, i.IsCompleted }) };
case "checkin":
var itemId = args.TryGetProperty("item_id", out var iid) ? iid.GetGuid() : Guid.Empty;
var exerciseItem = await db.ExercisePlanItems.FindAsync([itemId]);
if (exerciseItem == null) return new { success = false, message = "条目不存在" };
exerciseItem.IsCompleted = true;
exerciseItem.CompletedAt = DateTime.UtcNow;
await db.SaveChangesAsync();
return new { success = true };
default: // query
var existingPlan = await db.ExercisePlans.Where(p => p.UserId == userId)
.OrderByDescending(p => p.WeekStartDate).FirstOrDefaultAsync();
if (existingPlan == null) return new { found = false };
var exerciseItems = await db.ExercisePlanItems.Where(i => i.PlanId == existingPlan.Id).OrderBy(i => i.DayOfWeek).ToListAsync();
return new { found = true, plan_id = existingPlan.Id, items = exerciseItems.Select(i => new { i.Id, i.DayOfWeek, i.ExerciseType, i.DurationMinutes, i.IsCompleted }) };
}
}
private static async Task<string> BuildPatientContext(AppDbContext db, Guid userId, CancellationToken ct)

View File

@@ -271,5 +271,15 @@ public sealed record CreateMedicationRequest(string Name, string? Dosage, Medica
public sealed record CreateConsultationRequest(Guid DoctorId);
public sealed record SendMessageRequest(string Content);
public sealed record CreateExercisePlanRequest(DateOnly WeekStartDate, List<ExerciseItemDto>? Items);
public sealed record ExerciseItemDto(int DayOfWeek, string ExerciseType, int DurationMinutes, bool IsRestDay);
public sealed class CreateExercisePlanRequest
{
public DateOnly WeekStartDate { get; init; }
public List<ExerciseItemDto>? Items { get; init; }
}
public sealed class ExerciseItemDto
{
public int DayOfWeek { get; init; }
public string ExerciseType { get; init; } = "";
public int DurationMinutes { get; init; }
public bool IsRestDay { get; init; }
}