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..]}";
}
}