Files
AI-Health/backend/src/Health.Application/HealthArchives/HealthArchiveService.cs
MingNian 4d213b5a44 feat: 后端架构重构 — Endpoint→Service→Repository分层 + AI确认机制 + 异步任务持久化
- 核心业务拆分为 Endpoint → Application Service → Repository 三层
- AI写入操作必须用户确认后才写库(确认卡片机制)
- 报告/饮食/用药分析改为持久化任务队列(原子领取/重试/重启恢复)
- 运动计划修复: 连续真实日期替代周模板
- 用药提醒去重 + 通知Outbox预留
- 认证收拢到AuthService, 管理员收拢到AdminService
- AI会话加用户归属校验防串号
- 提示词调整为患者视角
- 开发假数据已关闭
- 21/21测试通过, 0警告0错误
2026-06-20 20:41:42 +08:00

112 lines
4.4 KiB
C#

using Health.Domain.Entities;
namespace Health.Application.HealthArchives;
public sealed class HealthArchiveService(IHealthArchiveRepository archives) : IHealthArchiveService
{
private readonly IHealthArchiveRepository _archives = archives;
public async Task<HealthArchiveDto?> GetAsync(Guid userId, CancellationToken ct)
{
var archive = await _archives.GetByUserIdAsync(userId, ct);
return archive == null ? null : ToDto(archive);
}
public async Task<HealthArchiveDto> GetOrCreateAsync(Guid userId, CancellationToken ct)
{
var archive = await GetOrCreateEntityAsync(userId, ct);
await _archives.SaveChangesAsync(ct);
return ToDto(archive);
}
public async Task<HealthArchiveDto> UpdateAsync(Guid userId, HealthArchiveUpdateRequest request, CancellationToken ct)
{
var archive = await GetOrCreateEntityAsync(userId, ct);
Apply(archive, request);
archive.UpdatedAt = DateTime.UtcNow;
await _archives.SaveChangesAsync(ct);
return ToDto(archive);
}
public async Task<object> ExecuteAiActionAsync(Guid userId, string action, HealthArchiveUpdateRequest request, CancellationToken ct)
{
if (action == "query")
{
var archive = await GetAsync(userId, ct);
return archive == null ? new { found = false } : ToAiResult(archive);
}
var current = await GetOrCreateEntityAsync(userId, ct);
var scopedRequest = action switch
{
"update_diagnosis" => request with
{
SurgeryType = null, SurgeryDate = null, Allergies = null,
DietRestrictions = null, ChronicDiseases = null, FamilyHistory = null
},
"update_surgery" => request with
{
Diagnosis = null, Allergies = null, DietRestrictions = null,
ChronicDiseases = null, FamilyHistory = null
},
"update_allergies" => new HealthArchiveUpdateRequest(null, null, null, request.Allergies, null, null, null),
"update_chronic_diseases" => new HealthArchiveUpdateRequest(null, null, null, null, null, request.ChronicDiseases, null),
"update_diet_restrictions" => new HealthArchiveUpdateRequest(null, null, null, null, request.DietRestrictions, null, null),
_ => null
};
if (scopedRequest == null)
return new { success = false, message = $"未知档案操作: {action}" };
Apply(current, scopedRequest);
current.UpdatedAt = DateTime.UtcNow;
await _archives.SaveChangesAsync(ct);
return new { success = true };
}
private async Task<HealthArchive> GetOrCreateEntityAsync(Guid userId, CancellationToken ct)
{
var archive = await _archives.GetByUserIdAsync(userId, ct);
if (archive != null) return archive;
archive = new HealthArchive { Id = Guid.NewGuid(), UserId = userId, UpdatedAt = DateTime.UtcNow };
await _archives.AddAsync(archive, ct);
return archive;
}
private static void Apply(HealthArchive archive, HealthArchiveUpdateRequest request)
{
if (request.Diagnosis != null) archive.Diagnosis = request.Diagnosis;
if (request.SurgeryType != null) archive.SurgeryType = request.SurgeryType;
if (request.SurgeryDate.HasValue) archive.SurgeryDate = request.SurgeryDate.Value;
if (request.Allergies != null) archive.Allergies = request.Allergies.ToList();
if (request.DietRestrictions != null) archive.DietRestrictions = request.DietRestrictions.ToList();
if (request.ChronicDiseases != null) archive.ChronicDiseases = request.ChronicDiseases.ToList();
if (request.FamilyHistory != null) archive.FamilyHistory = request.FamilyHistory;
}
private static object ToAiResult(HealthArchiveDto archive) => new
{
found = true,
archive.Diagnosis,
archive.SurgeryType,
SurgeryDate = archive.SurgeryDate?.ToString("yyyy-MM-dd"),
archive.Allergies,
archive.DietRestrictions,
archive.ChronicDiseases,
archive.FamilyHistory,
};
private static HealthArchiveDto ToDto(HealthArchive archive) => new(
archive.Id,
archive.UserId,
archive.Diagnosis,
archive.SurgeryType,
archive.SurgeryDate,
archive.Allergies,
archive.DietRestrictions,
archive.ChronicDiseases,
archive.FamilyHistory,
archive.UpdatedAt);
}