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
}