fix: 用药模块重做 + 运动计划不限7天 + 打卡可切换 + 删除修复
- 用药: 重写列表页(标签筛选+详情弹窗+打卡切换+滑动删除) - 用药: 重写编辑页(选择器替代输入+同列布局+频率改为每天几次) - 用药: 加Notes字段+DELETE端点+修复重复端点 - 运动: 不限7天, startDate+索引推算当天 - 打卡: 药/运动打卡可切换(点✓取消) - 删除: 修复滑动删除热区, 一次点击即删除 - 侧边栏: 一键清理历史对话 - 饮食/运动/用药/问诊 API路径加/api/前缀 - 通知时机修正: 新建计划不再提前弹窗
This commit is contained in:
@@ -17,6 +17,7 @@ public sealed class Medication
|
||||
public DateOnly? EndDate { get; set; }
|
||||
public bool IsActive { get; set; } = true;
|
||||
public MedicationSource Source { get; set; }
|
||||
public string? Notes { get; set; }
|
||||
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAt { get; set; } = DateTime.UtcNow;
|
||||
|
||||
|
||||
@@ -21,7 +21,8 @@ public static class ExerciseEndpoints
|
||||
plan.Id, plan.WeekStartDate, plan.CreatedAt, plan.UpdatedAt,
|
||||
items = plan.Items.Select(i => new
|
||||
{
|
||||
i.Id, i.DayOfWeek, i.ExerciseType, i.DurationMinutes,
|
||||
i.Id, i.DayOfWeek,
|
||||
i.ExerciseType, i.DurationMinutes,
|
||||
i.IsCompleted, i.CompletedAt, i.IsRestDay
|
||||
})
|
||||
},
|
||||
@@ -38,34 +39,85 @@ public static class ExerciseEndpoints
|
||||
request.Body.Position = 0;
|
||||
using var json = JsonDocument.Parse(body);
|
||||
var root = json.RootElement;
|
||||
var ws = root.GetProperty("weekStartDate").GetString()!;
|
||||
var plan = new ExercisePlan { Id = Guid.NewGuid(), UserId = userId, WeekStartDate = DateOnly.Parse(ws) };
|
||||
if (root.TryGetProperty("items", out var items))
|
||||
var exerciseType = root.TryGetProperty("exerciseType", out var et) ? et.GetString()! : "运动";
|
||||
var duration = root.TryGetProperty("durationMinutes", out var dm) ? dm.GetInt32() : 30;
|
||||
var startDate = DateOnly.Parse((root.TryGetProperty("startDate", out var sd) ? sd.GetString()! : DateTime.Now.ToString("yyyy-MM-dd")));
|
||||
var endDate = DateOnly.Parse((root.TryGetProperty("endDate", out var ed) ? ed.GetString()! : DateTime.Now.AddDays(6).ToString("yyyy-MM-dd")));
|
||||
var plan = new ExercisePlan { Id = Guid.NewGuid(), UserId = userId, WeekStartDate = startDate };
|
||||
for (var d = startDate; d <= endDate; d = d.AddDays(1))
|
||||
{
|
||||
foreach (var item in items.EnumerateArray())
|
||||
plan.Items.Add(new ExercisePlanItem
|
||||
{
|
||||
plan.Items.Add(new ExercisePlanItem
|
||||
{
|
||||
Id = Guid.NewGuid(),
|
||||
DayOfWeek = item.GetProperty("dayOfWeek").GetInt32(),
|
||||
ExerciseType = item.TryGetProperty("exerciseType", out var et) ? et.GetString()! : "休息",
|
||||
DurationMinutes = item.TryGetProperty("durationMinutes", out var dm) ? dm.GetInt32() : 0,
|
||||
IsRestDay = item.TryGetProperty("isRestDay", out var rd) && rd.GetBoolean(),
|
||||
});
|
||||
}
|
||||
Id = Guid.NewGuid(),
|
||||
DayOfWeek = (int)d.DayOfWeek,
|
||||
ExerciseType = exerciseType,
|
||||
DurationMinutes = duration,
|
||||
IsRestDay = false,
|
||||
});
|
||||
}
|
||||
db.ExercisePlans.Add(plan);
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { plan.Id }, message = (string?)null });
|
||||
});
|
||||
|
||||
group.MapGet("/", async (HttpContext http, AppDbContext db) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
var plans = await db.ExercisePlans.Where(p => p.UserId == userId)
|
||||
.OrderByDescending(p => p.WeekStartDate).Take(20)
|
||||
.Select(p => new
|
||||
{
|
||||
p.Id, p.WeekStartDate, p.CreatedAt,
|
||||
totalDays = p.Items.Count,
|
||||
completedDays = p.Items.Count(i => i.IsCompleted),
|
||||
items = p.Items.OrderBy(i => i.DayOfWeek).Select(i => new
|
||||
{
|
||||
i.Id, i.DayOfWeek,
|
||||
i.ExerciseType, i.DurationMinutes,
|
||||
i.IsCompleted, i.IsRestDay
|
||||
})
|
||||
}).ToListAsync();
|
||||
return Results.Ok(new { code = 0, data = plans, message = (string?)null });
|
||||
});
|
||||
|
||||
group.MapGet("/{id:guid}", async (Guid id, HttpContext http, AppDbContext db) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
var plan = await db.ExercisePlans.Include(p => p.Items)
|
||||
.FirstOrDefaultAsync(p => p.Id == id && p.UserId == userId);
|
||||
if (plan == null) return Results.Ok(new { code = 40004, message = "不存在" });
|
||||
return Results.Ok(new
|
||||
{
|
||||
code = 0,
|
||||
data = new
|
||||
{
|
||||
plan.Id, plan.WeekStartDate, plan.CreatedAt,
|
||||
items = plan.Items.OrderBy(i => i.DayOfWeek).Select(i => new
|
||||
{
|
||||
i.Id, i.DayOfWeek, i.ExerciseType, i.DurationMinutes,
|
||||
i.IsCompleted, i.CompletedAt, i.IsRestDay
|
||||
})
|
||||
},
|
||||
message = (string?)null
|
||||
});
|
||||
});
|
||||
|
||||
group.MapDelete("/{id:guid}", async (Guid id, HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
var plan = await db.ExercisePlans.Include(p => p.Items).FirstOrDefaultAsync(p => p.Id == id && p.UserId == userId, ct);
|
||||
if (plan != null) { db.ExercisePlans.Remove(plan); await db.SaveChangesAsync(ct); }
|
||||
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
|
||||
});
|
||||
|
||||
group.MapPost("/items/{itemId:guid}/checkin", async (Guid itemId, HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
var item = await db.ExercisePlanItems.FindAsync([itemId], ct);
|
||||
if (item == null) return Results.Ok(new { code = 40004, message = "不存在" });
|
||||
item.IsCompleted = true; item.CompletedAt = DateTime.UtcNow;
|
||||
item.IsCompleted = !item.IsCompleted;
|
||||
item.CompletedAt = item.IsCompleted ? DateTime.UtcNow : null;
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
|
||||
return Results.Ok(new { code = 0, data = new { completed = item.IsCompleted }, message = (string?)null });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,10 +6,19 @@ public static class MedicationEndpoints
|
||||
{
|
||||
var group = app.MapGroup("/api/medications").RequireAuthorization();
|
||||
|
||||
group.MapGet("/", async (HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
group.MapGet("/", async (string? filter, HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
var meds = await db.Medications.Where(m => m.UserId == userId).OrderByDescending(m => m.CreatedAt).ToListAsync(ct);
|
||||
var query = db.Medications.Where(m => m.UserId == userId);
|
||||
if (filter == "active") query = query.Where(m => m.IsActive);
|
||||
else if (filter == "inactive") query = query.Where(m => !m.IsActive);
|
||||
var meds = await query.OrderByDescending(m => m.CreatedAt).Select(m => new
|
||||
{
|
||||
m.Id, m.Name, m.Dosage, Frequency = m.Frequency.ToString(),
|
||||
m.TimeOfDay, m.StartDate, m.EndDate, m.IsActive, m.Notes,
|
||||
Source = m.Source.ToString(), m.CreatedAt, m.UpdatedAt,
|
||||
todayTaken = m.Logs.Any(l => l.CreatedAt.Date == DateTime.UtcNow.Date && l.Status == MedicationLogStatus.Taken)
|
||||
}).ToListAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = meds, message = (string?)null });
|
||||
});
|
||||
|
||||
@@ -37,6 +46,7 @@ public static class MedicationEndpoints
|
||||
med.StartDate = DateOnly.TryParse(sd.GetString(), out var d) ? d : null;
|
||||
if (root.TryGetProperty("endDate", out var ed))
|
||||
med.EndDate = DateOnly.TryParse(ed.GetString(), out var d2) ? d2 : null;
|
||||
if (root.TryGetProperty("notes", out var nt)) med.Notes = nt.GetString();
|
||||
db.Medications.Add(med);
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { med.Id }, message = (string?)null });
|
||||
@@ -59,6 +69,7 @@ public static class MedicationEndpoints
|
||||
if (root.TryGetProperty("timeOfDay", out var tod2)) med.TimeOfDay = tod2.EnumerateArray().Select(t => TimeOnly.Parse(t.GetString()!)).ToList();
|
||||
if (root.TryGetProperty("startDate", out var sd2) && DateOnly.TryParse(sd2.GetString(), out var d3)) med.StartDate = d3;
|
||||
if (root.TryGetProperty("endDate", out var ed2) && DateOnly.TryParse(ed2.GetString(), out var d4)) med.EndDate = d4;
|
||||
if (root.TryGetProperty("notes", out var nt2)) med.Notes = nt2.GetString();
|
||||
med.UpdatedAt = DateTime.UtcNow;
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
|
||||
@@ -75,6 +86,13 @@ public static class MedicationEndpoints
|
||||
group.MapPost("/{id:guid}/confirm", async (Guid id, HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
{
|
||||
var userId = GetUserId(http);
|
||||
var today = DateOnly.FromDateTime(DateTime.UtcNow);
|
||||
var existing = await db.MedicationLogs.FirstOrDefaultAsync(l => l.MedicationId == id && l.CreatedAt.Date == DateTime.UtcNow.Date && l.Status == MedicationLogStatus.Taken, ct);
|
||||
if (existing != null) {
|
||||
db.MedicationLogs.Remove(existing);
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { taken = false }, message = (string?)null });
|
||||
}
|
||||
var log = new MedicationLog
|
||||
{
|
||||
Id = Guid.NewGuid(), MedicationId = id, UserId = userId,
|
||||
@@ -82,7 +100,7 @@ public static class MedicationEndpoints
|
||||
};
|
||||
db.MedicationLogs.Add(log);
|
||||
await db.SaveChangesAsync(ct);
|
||||
return Results.Ok(new { code = 0, data = new { success = true }, message = (string?)null });
|
||||
return Results.Ok(new { code = 0, data = new { taken = true }, message = (string?)null });
|
||||
});
|
||||
|
||||
group.MapGet("/reminders", async (HttpContext http, AppDbContext db, CancellationToken ct) =>
|
||||
|
||||
Reference in New Issue
Block a user