using Health.Application.Admin; namespace Health.Infrastructure.Admin; public sealed class AdminService(AppDbContext db) : IAdminService { private readonly AppDbContext _db = db; public async Task ListDoctorsAsync(CancellationToken ct) { var doctors = await _db.Doctors.AsNoTracking().OrderBy(x => x.CreatedAt) .Select(x => new { x.Id, x.Name, x.Title, x.Department, x.Phone, x.ProfessionalDirection, x.IsActive, x.AvatarUrl, x.Introduction, x.CreatedAt }) .ToListAsync(ct); return Ok(doctors); } public async Task AddDoctorAsync(AddDoctorCommand command, CancellationToken ct) { var phone = command.Phone.Trim(); var name = command.Name.Trim(); if (string.IsNullOrWhiteSpace(phone)) return Error(40001, "手机号不能为空"); if (string.IsNullOrWhiteSpace(name)) return Error(40002, "姓名不能为空"); if (await _db.Users.AnyAsync(x => x.Phone == phone, ct) || await _db.Doctors.AnyAsync(x => x.Phone == phone, ct)) return Error(40003, "该手机号已被其他账号使用"); var doctor = new Doctor { Id = Guid.NewGuid(), Name = name, Title = command.Title?.Trim(), Department = command.Department?.Trim(), Phone = phone, ProfessionalDirection = command.ProfessionalDirection?.Trim(), IsActive = true, CreatedAt = DateTime.UtcNow, }; var user = new User { Id = Guid.NewGuid(), Phone = phone, Role = "Doctor", Name = name, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow }; _db.Doctors.Add(doctor); _db.Users.Add(user); _db.DoctorProfiles.Add(new DoctorProfile { Id = Guid.NewGuid(), UserId = user.Id, DoctorId = doctor.Id, Name = name, Title = doctor.Title, Department = doctor.Department, IsActive = true, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }); await _db.SaveChangesAsync(ct); return Ok(new { doctor.Id, doctor.Name }); } public async Task UpdateDoctorAsync(Guid id, UpdateDoctorCommand command, CancellationToken ct) { var doctor = await _db.Doctors.FindAsync([id], ct); if (doctor == null) return Error(404, "医生不存在"); var profile = await _db.DoctorProfiles.Include(x => x.User) .FirstOrDefaultAsync(x => x.DoctorId == id, ct); if (profile == null) return Error(40901, "医生登录资料不完整,请联系技术人员修复"); var user = profile.User; if (command.Phone != null) { var phone = command.Phone.Trim(); if (phone.Length == 0) return Error(40001, "手机号不能为空"); if (await _db.Users.AnyAsync(x => x.Id != user.Id && x.Phone == phone, ct) || await _db.Doctors.AnyAsync(x => x.Id != id && x.Phone == phone, ct)) return Error(40003, "该手机号已被其他账号使用"); doctor.Phone = phone; user.Phone = phone; } if (command.Name != null) { var name = command.Name.Trim(); if (name.Length == 0) return Error(40002, "姓名不能为空"); doctor.Name = name; user.Name = name; profile.Name = name; } if (command.Title != null) { doctor.Title = command.Title.Trim(); profile.Title = doctor.Title; } if (command.Department != null) { doctor.Department = command.Department.Trim(); profile.Department = doctor.Department; } if (command.ProfessionalDirection != null) doctor.ProfessionalDirection = command.ProfessionalDirection.Trim(); user.UpdatedAt = DateTime.UtcNow; profile.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); return Ok(new { doctor.Id }); } public async Task ToggleDoctorAsync(Guid id, CancellationToken ct) { var doctor = await _db.Doctors.FindAsync([id], ct); if (doctor == null) return Error(404, "医生不存在"); var profile = await _db.DoctorProfiles.FirstOrDefaultAsync(x => x.DoctorId == id, ct); if (profile == null) return Error(40901, "医生登录资料不完整,请联系技术人员修复"); doctor.IsActive = !doctor.IsActive; profile.IsActive = doctor.IsActive; profile.UpdatedAt = DateTime.UtcNow; await _db.SaveChangesAsync(ct); return Ok(new { doctor.Id, doctor.IsActive }); } public async Task DeleteDoctorAsync(Guid id, CancellationToken ct) { var doctor = await _db.Doctors.FindAsync([id], ct); if (doctor == null) return Error(404, "医生不存在"); if (await _db.Users.AnyAsync(x => x.Role == "User" && x.DoctorId == id, ct)) return Error(40004, "该医生仍有绑定患者,请先停用,不能直接删除"); if (await _db.Consultations.AnyAsync(x => x.DoctorId == id, ct)) return Error(40005, "该医生已有问诊记录,只能停用,不能删除"); var profile = await _db.DoctorProfiles.Include(x => x.User) .FirstOrDefaultAsync(x => x.DoctorId == id, ct); if (profile != null) { var user = profile.User; var refreshTokens = await _db.RefreshTokens.Where(x => x.UserId == user.Id).ToListAsync(ct); _db.RefreshTokens.RemoveRange(refreshTokens); _db.DoctorProfiles.Remove(profile); _db.Users.Remove(user); } _db.Doctors.Remove(doctor); await _db.SaveChangesAsync(ct); return Ok(new { success = true }); } public async Task ListPatientsAsync(string? search, int page, int pageSize, CancellationToken ct) { page = Math.Max(page, 1); pageSize = Math.Clamp(pageSize, 1, 100); var query = _db.Users.AsNoTracking().Where(x => x.Role == "User"); if (!string.IsNullOrWhiteSpace(search)) query = query.Where(x => (x.Name != null && x.Name.Contains(search)) || (x.Phone != null && x.Phone.Contains(search))); var total = await query.CountAsync(ct); var patients = await query.OrderByDescending(x => x.CreatedAt).Skip((page - 1) * pageSize).Take(pageSize) .Select(x => new { x.Id, x.Name, x.Phone, x.Gender, x.BirthDate, x.CreatedAt, DoctorName = x.Doctor != null ? x.Doctor.Name : null, DoctorDepartment = x.Doctor != null ? x.Doctor.Department : null }) .ToListAsync(ct); return Ok(new { patients, total, page, pageSize }); } private static AdminResult Ok(object data) => new(0, data); private static AdminResult Error(int code, string message) => new(code, null, message); }