using Health.Application.Auth; using Health.Infrastructure.Services; namespace Health.Infrastructure.Auth; public sealed class AuthService(AppDbContext db, JwtProvider jwt, SmsService sms) : IAuthService { private const string AdminPhone = "12345678910"; private const string AdminSmsCode = "000000"; private static readonly Guid AdminId = Guid.Parse("00000000-0000-0000-0000-000000000001"); private readonly AppDbContext _db = db; private readonly JwtProvider _jwt = jwt; private readonly SmsService _sms = sms; public async Task SendCodeAsync(string phone, bool revealDevelopmentCode, CancellationToken ct) { var code = phone == AdminPhone ? AdminSmsCode : _sms.GenerateCode(); await _db.VerificationCodes.AddAsync(new VerificationCode { Id = Guid.NewGuid(), Phone = phone, Code = code, ExpiresAt = DateTime.UtcNow.AddMinutes(5), }, ct); await _db.SaveChangesAsync(ct); if (phone != AdminPhone) await _sms.SendCodeAsync(phone, code); return new AuthResult(0, new { success = true, devCode = revealDevelopmentCode ? code : null }); } public async Task RegisterAsync(RegisterCommand command, CancellationToken ct) { var code = await TakeCodeAsync(command.Phone, command.SmsCode, ct); if (code == null) return Error(40001, "验证码错误或已过期"); if (await _db.Users.AnyAsync(x => x.Phone == command.Phone, ct)) return Error(40002, "该手机号已注册,请直接登录"); if (string.IsNullOrWhiteSpace(command.Name)) return Error(40003, "请输入姓名"); if (!await _db.Doctors.AnyAsync(x => x.Id == command.DoctorId && x.IsActive, ct)) return Error(40004, "所选医生不存在或已停诊"); code.IsUsed = true; var user = new User { Id = Guid.NewGuid(), Phone = command.Phone, Role = "User", Name = command.Name, DoctorId = command.DoctorId, CreatedAt = DateTime.UtcNow, UpdatedAt = DateTime.UtcNow, }; _db.Users.Add(user); _db.NotificationPreferences.Add(new NotificationPreference { Id = Guid.NewGuid(), UserId = user.Id }); _db.HealthArchives.Add(new HealthArchive { Id = Guid.NewGuid(), UserId = user.Id }); var tokens = AddTokens(user.Id, user.Phone, user.Role); await _db.SaveChangesAsync(ct); return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { user.Id, user.Phone, user.Role, isNew = true } }); } public async Task LoginAsync(string phone, string smsCode, CancellationToken ct) { var code = await TakeCodeAsync(phone, smsCode, ct); if (code == null) return Error(40001, "验证码错误或已过期"); code.IsUsed = true; if (phone == AdminPhone) { var tokens = AddTokens(AdminId, AdminPhone, "Admin"); await _db.SaveChangesAsync(ct); return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { id = AdminId, phone = AdminPhone, role = "Admin", name = "管理员", isNew = false } }); } var user = await _db.Users.FirstOrDefaultAsync(x => x.Phone == phone, ct); if (user == null) return Error(40004, "该手机号未注册,请先注册"); user.UpdatedAt = DateTime.UtcNow; var userTokens = AddTokens(user.Id, user.Phone, user.Role); await _db.SaveChangesAsync(ct); return new AuthResult(0, new { userTokens.accessToken, userTokens.refreshToken, user = new { user.Id, user.Phone, user.Role, user.Name, user.Gender, user.AvatarUrl, BirthDate = user.BirthDate?.ToString("yyyy-MM-dd"), isNew = false } }); } public async Task RefreshAsync(string refreshToken, CancellationToken ct) { var oldToken = await _db.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken && !x.IsRevoked, ct); if (oldToken == null || oldToken.ExpiresAt < DateTime.UtcNow) return Error(40002, "登录已过期,请重新登录"); oldToken.IsRevoked = true; if (oldToken.UserId == AdminId) { var tokens = AddTokens(AdminId, AdminPhone, "Admin"); await _db.SaveChangesAsync(ct); return new AuthResult(0, new { tokens.accessToken, tokens.refreshToken, user = new { role = "Admin" } }); } var user = await _db.Users.FindAsync([oldToken.UserId], ct); if (user == null) return Error(40002, "用户不存在"); var userTokens = AddTokens(user.Id, user.Phone, user.Role); await _db.SaveChangesAsync(ct); return new AuthResult(0, new { userTokens.accessToken, userTokens.refreshToken, user = new { user.Role } }); } public async Task LogoutAsync(string refreshToken, CancellationToken ct) { var token = await _db.RefreshTokens.FirstOrDefaultAsync(x => x.Token == refreshToken, ct); if (token != null) token.IsRevoked = true; await _db.SaveChangesAsync(ct); } private Task TakeCodeAsync(string phone, string code, CancellationToken ct) => _db.VerificationCodes.Where(x => x.Phone == phone && x.Code == code && x.ExpiresAt > DateTime.UtcNow && !x.IsUsed) .OrderByDescending(x => x.CreatedAt).FirstOrDefaultAsync(ct); private (string accessToken, string refreshToken) AddTokens(Guid userId, string phone, string role) { var access = _jwt.GenerateAccessToken(userId, phone, role); var refresh = _jwt.GenerateRefreshToken(); _db.RefreshTokens.Add(new RefreshToken { Id = Guid.NewGuid(), UserId = userId, Token = refresh, ExpiresAt = DateTime.UtcNow.AddDays(30) }); return (access, refresh); } private static AuthResult Error(int code, string message) => new(code, null, message); }