using Microsoft.Extensions.Logging; using XiangYi.Application.DTOs.Requests; using XiangYi.Application.DTOs.Responses; using XiangYi.Application.Interfaces; using XiangYi.Core.Constants; using XiangYi.Core.Entities.Biz; using XiangYi.Core.Enums; using XiangYi.Core.Exceptions; using XiangYi.Core.Interfaces; using XiangYi.Infrastructure.RealName; using XiangYi.Infrastructure.WeChat; using XiangYi.Infrastructure.Cache; namespace XiangYi.Application.Services; /// /// 实名认证服务实现 /// public class RealNameService : IRealNameService { private readonly IRepository _userRepository; private readonly IRepository _orderRepository; private readonly IRepository _realNameAuthRepository; private readonly IWeChatService _weChatService; private readonly IRealNameProvider _realNameProvider; private readonly ICacheService _cacheService; private readonly ILogger _logger; /// /// 实名认证价格(单位:元) /// private const decimal RealNamePrice = 88m; /// /// 临时OCR数据缓存前缀 /// private const string OcrCachePrefix = "realname:ocr:"; /// /// 临时OCR数据缓存时间(分钟) /// private const int OcrCacheMinutes = 30; /// /// 认证状态名称 /// private static readonly Dictionary StatusNames = new() { { 0, "未认证" }, { 1, "待审核" }, { 2, "已通过" }, { 3, "已拒绝" } }; public RealNameService( IRepository userRepository, IRepository orderRepository, IRepository realNameAuthRepository, IWeChatService weChatService, IRealNameProvider realNameProvider, ICacheService cacheService, ILogger logger) { _userRepository = userRepository; _orderRepository = orderRepository; _realNameAuthRepository = realNameAuthRepository; _weChatService = weChatService; _realNameProvider = realNameProvider; _cacheService = cacheService; _logger = logger; } /// public async Task CreateRealNameOrderAsync(long userId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 已实名用户不能重复认证 if (user.IsRealName) { throw new BusinessException(ErrorCodes.RealNameAlreadyVerified, "您已完成实名认证"); } // 检查是否有待处理的认证记录 var existingAuth = (await _realNameAuthRepository.GetListAsync( a => a.UserId == userId && a.Status == (int)RealNameAuthStatus.Pending)) .FirstOrDefault(); if (existingAuth != null) { throw new BusinessException(ErrorCodes.InvalidParameter, "您有待处理的实名认证申请"); } // 会员用户免费认证 if (IRealNameService.ShouldBeFreeForMember(user.IsMember, user.IsRealName)) { _logger.LogInformation("会员用户免费实名认证: UserId={UserId}", userId); return new RealNameOrderResponse { IsFree = true, Amount = 0 }; } // 非会员用户创建付费订单 var orderNo = GenerateOrderNo(); var order = new Order { OrderNo = orderNo, UserId = userId, OrderType = (int)OrderType.RealName, ProductName = "实名认证", Amount = RealNamePrice, PayAmount = RealNamePrice, Status = (int)OrderStatus.Pending, ExpireTime = DateTime.Now.AddMinutes(30), CreateTime = DateTime.Now, UpdateTime = DateTime.Now }; order = await _orderRepository.AddAsync(order); _logger.LogInformation("创建实名认证订单: OrderId={OrderId}, OrderNo={OrderNo}, UserId={UserId}", order.Id, orderNo, userId); // 调用微信支付创建预支付订单 var payRequest = new CreatePaymentRequest { OutTradeNo = orderNo, Amount = (int)(RealNamePrice * 100), Description = "实名认证", OpenId = user.OpenId, Attach = "realname" }; var payResult = await _weChatService.CreatePaymentAsync(payRequest); if (!payResult.Success) { _logger.LogWarning("创建微信支付订单失败: OrderNo={OrderNo}, Error={Error}", orderNo, payResult.ErrorMessage); throw new BusinessException(ErrorCodes.PaymentServiceError, payResult.ErrorMessage ?? "创建支付订单失败"); } return new RealNameOrderResponse { OrderId = order.Id, OrderNo = orderNo, Amount = RealNamePrice, IsFree = false, PayParams = payResult.PayParams != null ? new DTOs.Responses.WeChatPayParams { TimeStamp = payResult.PayParams.TimeStamp, NonceStr = payResult.PayParams.NonceStr, Package = payResult.PayParams.Package, SignType = payResult.PayParams.SignType, PaySign = payResult.PayParams.PaySign } : null }; } /// public async Task SubmitRealNameAsync(long userId, RealNameSubmitRequest request) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 已实名用户不能重复认证 if (user.IsRealName) { throw new BusinessException(ErrorCodes.RealNameAlreadyVerified, "您已完成实名认证"); } // 验证参数 if (string.IsNullOrWhiteSpace(request.RealName)) { throw new BusinessException(ErrorCodes.ValidationFailed, "请输入真实姓名"); } if (string.IsNullOrWhiteSpace(request.IdCard)) { throw new BusinessException(ErrorCodes.ValidationFailed, "请输入身份证号"); } // 非会员用户需要检查是否已支付 if (!user.IsMember) { var paidOrder = (await _orderRepository.GetListAsync( o => o.UserId == userId && o.OrderType == (int)OrderType.RealName && o.Status == (int)OrderStatus.Paid)) .FirstOrDefault(); if (paidOrder == null) { throw new BusinessException(ErrorCodes.InvalidParameter, "请先完成实名认证费用支付"); } } // 调用第三方实名认证接口验证 var verifyResult = await _realNameProvider.VerifyIdCardAsync(request.RealName, request.IdCard); if (!verifyResult.IsVerified) { _logger.LogWarning("实名认证验证失败: UserId={UserId}, Error={Error}", userId, verifyResult.ErrorMessage); return new RealNameSubmitResponse { Success = false, Message = verifyResult.ErrorMessage ?? "身份信息验证失败", Status = (int)RealNameAuthStatus.Rejected }; } // 脱敏存储身份信息 var maskedName = IRealNameService.MaskName(request.RealName); var maskedIdCard = IRealNameService.MaskIdCard(request.IdCard); // 查找关联订单 long orderId = 0; if (!user.IsMember) { var order = (await _orderRepository.GetListAsync( o => o.UserId == userId && o.OrderType == (int)OrderType.RealName && o.Status == (int)OrderStatus.Paid)) .FirstOrDefault(); orderId = order?.Id ?? 0; } // 创建实名认证记录 var realNameAuth = new RealNameAuth { UserId = userId, OrderId = orderId, RealName = maskedName, IdCard = maskedIdCard, Status = (int)RealNameAuthStatus.Approved, VerifyTime = DateTime.Now, CreateTime = DateTime.Now, UpdateTime = DateTime.Now }; await _realNameAuthRepository.AddAsync(realNameAuth); // 更新用户实名状态 user.IsRealName = true; user.UpdateTime = DateTime.Now; await _userRepository.UpdateAsync(user); _logger.LogInformation("实名认证成功: UserId={UserId}", userId); return new RealNameSubmitResponse { Success = true, Message = "实名认证成功", Status = (int)RealNameAuthStatus.Approved, MaskedName = maskedName, MaskedIdCard = maskedIdCard }; } /// public async Task GetRealNameStatusAsync(long userId) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 检查是否已支付实名认证费用 var hasPaidOrder = false; if (!user.IsMember) { var paidOrder = (await _orderRepository.GetListAsync( o => o.UserId == userId && o.OrderType == (int)OrderType.RealName && o.Status == (int)OrderStatus.Paid)) .FirstOrDefault(); hasPaidOrder = paidOrder != null; } // 查找最新的实名认证记录 var authRecords = await _realNameAuthRepository.GetListAsync(a => a.UserId == userId); var latestAuth = authRecords.OrderByDescending(a => a.CreateTime).FirstOrDefault(); if (latestAuth == null) { // 未认证 return new RealNameStatusResponse { IsRealName = false, IsPaid = user.IsMember || hasPaidOrder, Status = 0, StatusName = GetStatusName(0), NeedPayment = !user.IsMember && !hasPaidOrder, PaymentAmount = user.IsMember ? null : RealNamePrice }; } return new RealNameStatusResponse { IsRealName = user.IsRealName, IsPaid = user.IsMember || hasPaidOrder, Status = latestAuth.Status, StatusName = GetStatusName(latestAuth.Status), MaskedName = latestAuth.RealName, MaskedIdCard = latestAuth.IdCard, Name = latestAuth.RealName, IdNumber = latestAuth.IdCard, RejectReason = latestAuth.RejectReason, VerifyTime = latestAuth.VerifyTime, NeedPayment = !user.IsMember && latestAuth.Status != (int)RealNameAuthStatus.Approved, PaymentAmount = user.IsMember ? null : RealNamePrice }; } /// public async Task VerifyWithImagesAsync(long userId, string frontImageBase64, string? backImageBase64) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 已实名用户不能重复认证 if (user.IsRealName) { return new RealNameVerifyResponse { Success = false, Message = "您已完成实名认证" }; } // 非会员用户需要检查是否已支付 if (!user.IsMember) { var paidOrder = (await _orderRepository.GetListAsync( o => o.UserId == userId && o.OrderType == (int)OrderType.RealName && o.Status == (int)OrderStatus.Paid)) .FirstOrDefault(); if (paidOrder == null) { return new RealNameVerifyResponse { Success = false, Message = "请先完成实名认证费用支付" }; } } // OCR识别身份证正面 var frontOcrResult = await _realNameProvider.OcrIdCardFrontAsync(frontImageBase64); if (!frontOcrResult.Success) { _logger.LogWarning("身份证正面OCR识别失败: UserId={UserId}, Error={Error}", userId, frontOcrResult.ErrorMessage); return new RealNameVerifyResponse { Success = false, Message = frontOcrResult.ErrorMessage ?? "身份证正面识别失败,请重新拍照" }; } var name = frontOcrResult.Name!; var idNumber = frontOcrResult.IdNumber!; // 如果没有反面图片,需要分步上传 if (string.IsNullOrEmpty(backImageBase64)) { // 缓存正面OCR结果,等待反面上传 var cacheKey = $"{OcrCachePrefix}{userId}"; await _cacheService.SetAsync(cacheKey, new OcrCacheData { Name = name, IdNumber = idNumber }, OcrCacheMinutes * 60); return new RealNameVerifyResponse { Success = true, Message = "身份证正面识别成功,请上传反面", NeedBackImage = true }; } // OCR识别身份证反面 var backOcrResult = await _realNameProvider.OcrIdCardBackAsync(backImageBase64); if (!backOcrResult.Success) { _logger.LogWarning("身份证反面OCR识别失败: UserId={UserId}, Error={Error}", userId, backOcrResult.ErrorMessage); return new RealNameVerifyResponse { Success = false, Message = backOcrResult.ErrorMessage ?? "身份证反面识别失败,请重新拍照" }; } // 调用实名认证接口验证 return await CompleteVerificationAsync(userId, user, name, idNumber); } /// public async Task UploadBackImageAsync(long userId, string backImageBase64) { var user = await _userRepository.GetByIdAsync(userId); if (user == null) { throw new BusinessException(ErrorCodes.UserNotFound, "用户不存在"); } // 已实名用户不能重复认证 if (user.IsRealName) { return new RealNameVerifyResponse { Success = false, Message = "您已完成实名认证" }; } // 获取缓存的正面OCR数据 var cacheKey = $"{OcrCachePrefix}{userId}"; var cachedData = await _cacheService.GetAsync(cacheKey); if (cachedData == null) { return new RealNameVerifyResponse { Success = false, Message = "请先上传身份证正面" }; } // OCR识别身份证反面 var backOcrResult = await _realNameProvider.OcrIdCardBackAsync(backImageBase64); if (!backOcrResult.Success) { _logger.LogWarning("身份证反面OCR识别失败: UserId={UserId}, Error={Error}", userId, backOcrResult.ErrorMessage); return new RealNameVerifyResponse { Success = false, Message = backOcrResult.ErrorMessage ?? "身份证反面识别失败,请重新拍照" }; } // 清除缓存 await _cacheService.RemoveAsync(cacheKey); // 调用实名认证接口验证 return await CompleteVerificationAsync(userId, user, cachedData.Name, cachedData.IdNumber); } /// /// 完成实名认证验证 /// private async Task CompleteVerificationAsync(long userId, User user, string name, string idNumber) { // 调用第三方实名认证接口验证 var verifyResult = await _realNameProvider.VerifyIdCardAsync(name, idNumber); if (!verifyResult.IsVerified) { _logger.LogWarning("实名认证验证失败: UserId={UserId}, Error={Error}", userId, verifyResult.ErrorMessage); return new RealNameVerifyResponse { Success = false, Message = verifyResult.ErrorMessage ?? "身份信息验证失败,请确认身份证信息正确" }; } // 脱敏存储身份信息 var maskedName = IRealNameService.MaskName(name); var maskedIdCard = IRealNameService.MaskIdCard(idNumber); // 查找关联订单 long orderId = 0; if (!user.IsMember) { var order = (await _orderRepository.GetListAsync( o => o.UserId == userId && o.OrderType == (int)OrderType.RealName && o.Status == (int)OrderStatus.Paid)) .FirstOrDefault(); orderId = order?.Id ?? 0; } // 创建实名认证记录 var realNameAuth = new RealNameAuth { UserId = userId, OrderId = orderId, RealName = maskedName, IdCard = maskedIdCard, Status = (int)RealNameAuthStatus.Approved, VerifyTime = DateTime.Now, CreateTime = DateTime.Now, UpdateTime = DateTime.Now }; await _realNameAuthRepository.AddAsync(realNameAuth); // 更新用户实名状态 user.IsRealName = true; user.UpdateTime = DateTime.Now; await _userRepository.UpdateAsync(user); _logger.LogInformation("实名认证成功: UserId={UserId}", userId); return new RealNameVerifyResponse { Success = true, Message = "实名认证成功", NeedBackImage = false, Data = new RealNameVerifyData { Name = maskedName, IdNumber = maskedIdCard } }; } /// /// OCR缓存数据 /// private class OcrCacheData { public string Name { get; set; } = string.Empty; public string IdNumber { get; set; } = string.Empty; } #region 私有方法 /// /// 生成订单号 /// private static string GenerateOrderNo() { var randomSuffix = Random.Shared.Next(1000, 9999); return $"R{DateTime.Now:yyyyMMddHHmmss}{randomSuffix:D4}"; } /// /// 获取状态名称 /// private static string GetStatusName(int status) { return StatusNames.TryGetValue(status, out var name) ? name : "未知"; } #endregion #region 静态辅助方法(用于属性测试) /// /// 脱敏姓名 /// public static string MaskName(string name) { return IRealNameService.MaskName(name); } /// /// 脱敏身份证号 /// public static string MaskIdCard(string idCard) { return IRealNameService.MaskIdCard(idCard); } /// /// 验证脱敏是否正确 /// public static bool ValidateMasking(string originalName, string maskedName, string originalIdCard, string maskedIdCard) { return IRealNameService.ValidateMasking(originalName, maskedName, originalIdCard, maskedIdCard); } /// /// 检查会员是否应该免费实名认证 /// public static bool ShouldBeFreeForMember(bool isMember, bool isAlreadyRealName) { return IRealNameService.ShouldBeFreeForMember(isMember, isAlreadyRealName); } #endregion }