- 核心业务拆分为 Endpoint → Application Service → Repository 三层 - AI写入操作必须用户确认后才写库(确认卡片机制) - 报告/饮食/用药分析改为持久化任务队列(原子领取/重试/重启恢复) - 运动计划修复: 连续真实日期替代周模板 - 用药提醒去重 + 通知Outbox预留 - 认证收拢到AuthService, 管理员收拢到AdminService - AI会话加用户归属校验防串号 - 提示词调整为患者视角 - 开发假数据已关闭 - 21/21测试通过, 0警告0错误
93 lines
3.8 KiB
C#
93 lines
3.8 KiB
C#
namespace Health.Application.Calendars;
|
|
|
|
public sealed class CalendarService(ICalendarRepository calendar) : ICalendarService
|
|
{
|
|
private readonly ICalendarRepository _calendar = calendar;
|
|
|
|
public async Task<IReadOnlyList<object>> GetMonthAsync(Guid userId, int year, int month, CancellationToken ct)
|
|
{
|
|
var start = new DateOnly(year, month, 1);
|
|
var end = start.AddMonths(1);
|
|
var snapshot = await _calendar.GetSnapshotAsync(userId, start, end, ct);
|
|
var result = new List<object>();
|
|
|
|
for (var date = start; date < end; date = date.AddDays(1))
|
|
{
|
|
var entries = BuildEntries(snapshot, date);
|
|
if (entries.Count == 0) continue;
|
|
|
|
result.Add(new
|
|
{
|
|
date = date.ToString("yyyy-MM-dd"),
|
|
events = entries.Select(entry => entry.Type).Distinct().ToList(),
|
|
details = entries.Select(entry => entry.Value).ToList()
|
|
});
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public async Task<object> GetDayAsync(Guid userId, DateOnly date, CancellationToken ct)
|
|
{
|
|
var snapshot = await _calendar.GetSnapshotAsync(userId, date, date.AddDays(1), ct);
|
|
var medications = snapshot.Medications
|
|
.Where(m => IsMedicationActiveOn(m, date))
|
|
.Select(m => new { m.Name, m.Dosage, timeOfDay = m.TimeOfDay.Select(t => t.ToString(@"hh\:mm")).ToList() })
|
|
.ToList();
|
|
var exercises = snapshot.ExercisePlans
|
|
.SelectMany(p => p.Items)
|
|
.Where(i => i.ScheduledDate == date && !i.IsRestDay)
|
|
.Select(i => new { type = i.ExerciseType, duration = i.DurationMinutes, isCompleted = i.IsCompleted, scheduledDate = i.ScheduledDate })
|
|
.ToList();
|
|
var followUps = snapshot.FollowUps
|
|
.Where(f => DateOnly.FromDateTime(f.ScheduledAt) == date)
|
|
.Select(f => new { f.Title, f.DoctorName, f.Department, status = f.Status.ToString() })
|
|
.ToList();
|
|
|
|
return new { medications, exercises, followUps };
|
|
}
|
|
|
|
private static List<CalendarEntry> BuildEntries(CalendarDataSnapshot snapshot, DateOnly date)
|
|
{
|
|
var entries = new List<CalendarEntry>();
|
|
foreach (var medication in snapshot.Medications.Where(m => IsMedicationActiveOn(m, date)))
|
|
{
|
|
entries.Add(new CalendarEntry("medication", new
|
|
{
|
|
type = "medication",
|
|
name = medication.Name,
|
|
dosage = medication.Dosage,
|
|
timeOfDay = medication.TimeOfDay.Select(t => t.ToString(@"hh\:mm")).ToList()
|
|
}));
|
|
}
|
|
|
|
foreach (var plan in snapshot.ExercisePlans)
|
|
{
|
|
foreach (var item in plan.Items.Where(i => i.ScheduledDate == date && !i.IsRestDay))
|
|
entries.Add(new CalendarEntry("exercise", new { type = "exercise", name = item.ExerciseType, duration = item.DurationMinutes, isCompleted = item.IsCompleted, scheduledDate = item.ScheduledDate }));
|
|
}
|
|
|
|
foreach (var followUp in snapshot.FollowUps.Where(f => DateOnly.FromDateTime(f.ScheduledAt) == date))
|
|
{
|
|
entries.Add(new CalendarEntry("followup", new
|
|
{
|
|
type = "followup",
|
|
title = followUp.Title,
|
|
doctorName = followUp.DoctorName,
|
|
department = followUp.Department,
|
|
status = followUp.Status.ToString()
|
|
}));
|
|
}
|
|
|
|
return entries;
|
|
}
|
|
|
|
private static bool IsMedicationActiveOn(Health.Domain.Entities.Medication medication, DateOnly date) =>
|
|
medication.IsActive
|
|
&& medication.TimeOfDay.Count > 0
|
|
&& (medication.StartDate == null || medication.StartDate <= date)
|
|
&& (medication.EndDate == null || medication.EndDate >= date);
|
|
|
|
private sealed record CalendarEntry(string Type, object Value);
|
|
}
|