using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; using XiangYi.Application.DTOs.Responses; using XiangYi.Application.Interfaces; using XiangYi.Application.Options; using XiangYi.Core.Constants; using XiangYi.Core.Entities.Biz; using XiangYi.Core.Exceptions; using XiangYi.Core.Interfaces; using XiangYi.Infrastructure.Cache; using XiangYi.Infrastructure.WeChat; namespace XiangYi.Application.Services; /// /// 认证服务实现 /// public class AuthService : IAuthService { private readonly IRepository _userRepository; private readonly IWeChatService _weChatService; private readonly ICacheService _cacheService; private readonly ISystemConfigService _systemConfigService; private readonly JwtOptions _jwtOptions; private readonly ILogger _logger; private static readonly Random _random = new(); public AuthService( IRepository userRepository, IWeChatService weChatService, ICacheService cacheService, ISystemConfigService systemConfigService, IOptions jwtOptions, ILogger logger) { _userRepository = userRepository; _weChatService = weChatService; _cacheService = cacheService; _systemConfigService = systemConfigService; _jwtOptions = jwtOptions.Value; _logger = logger; } /// public async Task LoginAsync(string code) { // 1. 调用微信接口获取openid var weChatResult = await _weChatService.Code2SessionAsync(code); if (!weChatResult.Success || string.IsNullOrEmpty(weChatResult.OpenId)) { _logger.LogWarning("微信登录失败: {ErrorCode} - {ErrorMessage}", weChatResult.ErrorCode, weChatResult.ErrorMessage); throw new BusinessException(ErrorCodes.WeChatLoginFailed, weChatResult.ErrorMessage ?? "微信登录失败"); } // 2. 查找或创建用户 var user = await _userRepository.GetListAsync(u => u.OpenId == weChatResult.OpenId); var existingUser = user.FirstOrDefault(); bool isNewUser = false; if (existingUser == null) { // 新用户,创建用户记录 isNewUser = true; var xiangQinNo = await GenerateXiangQinNoAsync(); // 获取默认头像 var defaultAvatar = await _systemConfigService.GetDefaultAvatarAsync(); existingUser = new User { OpenId = weChatResult.OpenId, UnionId = weChatResult.UnionId, XiangQinNo = xiangQinNo, Avatar = defaultAvatar, Status = 1, ContactCount = 2, CreateTime = DateTime.Now, UpdateTime = DateTime.Now }; existingUser = await _userRepository.AddAsync(existingUser); _logger.LogInformation("新用户注册成功: UserId={UserId}, XiangQinNo={XiangQinNo}", existingUser.Id, xiangQinNo); } else { // 更新最后登录时间 existingUser.LastLoginTime = DateTime.Now; existingUser.UpdateTime = DateTime.Now; await _userRepository.UpdateAsync(existingUser); } // 3. 生成JWT Token var token = GenerateToken(existingUser.Id, existingUser.OpenId); // 4. 缓存Token await _cacheService.SetUserTokenAsync(existingUser.Id, token); _logger.LogInformation("用户登录成功: UserId={UserId}", existingUser.Id); return new LoginResponse { Token = token, UserId = existingUser.Id, Nickname = existingUser.Nickname, Avatar = existingUser.Avatar, XiangQinNo = existingUser.XiangQinNo, IsProfileCompleted = existingUser.IsProfileCompleted, IsMember = existingUser.IsMember, MemberLevel = existingUser.MemberLevel, IsRealName = existingUser.IsRealName, IsNewUser = isNewUser }; } /// public async Task BindPhoneAsync(long userId, string code) { // 1. 获取用户 var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 2. 调用微信接口获取手机号 var phone = await _weChatService.GetPhoneNumberAsync(code); if (string.IsNullOrEmpty(phone)) { throw new BusinessException(ErrorCodes.WeChatServiceError, "获取手机号失败"); } // 3. 更新用户手机号 user.Phone = phone; user.UpdateTime = DateTime.Now; await _userRepository.UpdateAsync(user); _logger.LogInformation("用户绑定手机号成功: UserId={UserId}", userId); // 4. 返回脱敏后的手机号 return new BindPhoneResponse { Phone = MaskPhone(phone) }; } /// public string GenerateToken(long userId, string openId) { var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOptions.Secret)); var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId.ToString()), new Claim("openid", openId), new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Iat, DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }; var token = new JwtSecurityToken( issuer: _jwtOptions.Issuer, audience: _jwtOptions.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwtOptions.ExpireMinutes), signingCredentials: credentials ); return new JwtSecurityTokenHandler().WriteToken(token); } /// public async Task GenerateXiangQinNoAsync() { const int maxAttempts = 100; for (int i = 0; i < maxAttempts; i++) { var xiangQinNo = GenerateXiangQinNo(); // 检查是否已存在 var exists = await _userRepository.ExistsAsync(u => u.XiangQinNo == xiangQinNo); if (!exists) { return xiangQinNo; } } // 如果多次尝试都失败,抛出异常 throw new BusinessException(ErrorCodes.SystemError, "生成相亲编号失败,请重试"); } /// public async Task ValidateTokenAsync(long userId, string token) { return await _cacheService.ValidateUserTokenAsync(userId, token); } /// /// 生成6位相亲编号(首位不为0) /// /// 相亲编号 public static string GenerateXiangQinNo() { // 首位:1-9 var firstDigit = _random.Next(1, 10); // 后5位:0-9 var remainingDigits = _random.Next(0, 100000); return $"{firstDigit}{remainingDigits:D5}"; } /// /// 手机号脱敏 /// private static string MaskPhone(string phone) { if (string.IsNullOrEmpty(phone) || phone.Length < 7) { return phone; } return $"{phone[..3]}****{phone[^4..]}"; } }