using Health.WebApi.Hubs; namespace Health.WebApi.Endpoints; public static class ConsultationEndpoints { public static void MapConsultationEndpoints(this WebApplication app) { // 医生列表(注册时无需登录) app.MapGet("/api/doctors", async (AppDbContext db) => { var doctors = await db.Doctors.Where(d => d.IsActive).Select(d => new { d.Id, d.Name, d.Title, d.Department, d.Introduction }).ToListAsync(); return Results.Ok(new { code = 0, data = doctors, message = (string?)null }); }); var group = app.MapGroup("/api").RequireAuthorization(); group.MapGet("/consultations", async (HttpContext http, AppDbContext db) => { var userId = GetUserId(http); var consultations = await db.Consultations.Where(c => c.UserId == userId).OrderByDescending(c => c.CreatedAt).ToListAsync(); return Results.Ok(new { code = 0, data = consultations, message = (string?)null }); }); group.MapPost("/consultations", async (CreateConsultationRequest req, HttpContext http, AppDbContext db, CancellationToken ct) => { var userId = GetUserId(http); var consultation = new Consultation { Id = Guid.NewGuid(), UserId = userId, DoctorId = req.DoctorId, Status = ConsultationStatus.AiTalking, Month = DateTime.UtcNow.Year * 100 + DateTime.UtcNow.Month, }; db.Consultations.Add(consultation); await db.SaveChangesAsync(ct); return Results.Ok(new { code = 0, data = new { consultation.Id }, message = (string?)null }); }); group.MapGet("/consultations/{id:guid}/messages", async (Guid id, string? after, HttpContext http, AppDbContext db, CancellationToken ct) => { var userId = GetUserId(http); var query = db.ConsultationMessages.Where(m => m.ConsultationId == id && m.Consultation.UserId == userId); if (Guid.TryParse(after, out var afterId)) query = query.Where(m => m.Id.CompareTo(afterId) > 0); var messages = await query.OrderBy(m => m.CreatedAt).Take(50).Select(m => new { m.Id, SenderType = m.SenderType.ToString(), m.SenderName, m.Content, m.CreatedAt }).ToListAsync(ct); return Results.Ok(new { code = 0, data = messages, message = (string?)null }); }); group.MapPost("/consultations/{id:guid}/messages", async (Guid id, SendMessageRequest req, HttpContext http, AppDbContext db, IHubContext hubContext, CancellationToken ct) => { var userId = GetUserId(http); var msg = new ConsultationMessage { Id = Guid.NewGuid(), ConsultationId = id, SenderType = ConsultationSenderType.User, Content = req.Content, SenderName = null, CreatedAt = DateTime.UtcNow }; db.ConsultationMessages.Add(msg); // 用户发消息后,状态从 AiTalking → WaitingDoctor var consultation = await db.Consultations.FirstOrDefaultAsync(c => c.Id == id, ct); if (consultation != null && consultation.Status == ConsultationStatus.AiTalking) { consultation.Status = ConsultationStatus.WaitingDoctor; } await db.SaveChangesAsync(ct); // 通过 SignalR 广播给问诊房间(医生端实时接收) await hubContext.Clients.Group(id.ToString()).SendAsync("ReceiveMessage", new { msg.Id, consultationId = id.ToString(), senderType = "User", senderName = (string?)null, content = req.Content, msg.CreatedAt }); return Results.Ok(new { code = 0, data = new { msg.Id }, message = (string?)null }); }); group.MapGet("/user/consultation-quota", async (HttpContext http, AppDbContext db) => { var userId = GetUserId(http); var now = DateTime.UtcNow; var currentPeriod = now.Year * 100 + now.Month; var used = await db.Consultations.CountAsync(c => c.UserId == userId && c.Month == currentPeriod); return Results.Ok(new { code = 0, data = new { total = 3, used, remaining = 3 - used }, message = (string?)null }); }); } private static Guid GetUserId(HttpContext http) => Guid.TryParse(http.User.FindFirst(System.Security.Claims.ClaimTypes.NameIdentifier)?.Value, out var id) ? id : Guid.Empty; } public sealed record CreateConsultationRequest(Guid DoctorId); public sealed record SendMessageRequest(string Content);