mi-assessment/server/MiAssessment/src/MiAssessment.Core/Services/OrderService.cs
18631081161 301ade0fe6
All checks were successful
continuous-integration/drone/push Build is passing
佣金
2026-03-26 00:30:15 +08:00

950 lines
35 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using MiAssessment.Core.Interfaces;
using MiAssessment.Model.Data;
using MiAssessment.Model.Models.Common;
using MiAssessment.Model.Models.Order;
using MiAssessment.Model.Models.Payment;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace MiAssessment.Core.Services;
/// <summary>
/// 订单服务实现
/// </summary>
/// <remarks>
/// 提供订单模块的核心业务功能,包括:
/// - 订单列表查询
/// - 订单详情查询
/// - 订单创建
/// - 发起支付
/// - 支付结果查询
/// </remarks>
public class OrderService : IOrderService
{
private readonly MiAssessmentDbContext _dbContext;
private readonly ILogger<OrderService> _logger;
private readonly IWechatPayService _wechatPayService;
private readonly IConfigService _configService;
/// <summary>
/// 构造函数
/// </summary>
public OrderService(
MiAssessmentDbContext dbContext,
ILogger<OrderService> logger,
IWechatPayService wechatPayService,
IConfigService configService)
{
_dbContext = dbContext;
_logger = logger;
_wechatPayService = wechatPayService;
_configService = configService;
}
/// <inheritdoc />
public async Task<PagedResult<OrderItemDto>> GetListAsync(long userId, int page, int pageSize, int? orderType)
{
_logger.LogDebug("获取订单列表userId: {UserId}, page: {Page}, pageSize: {PageSize}, orderType: {OrderType}",
userId, page, pageSize, orderType);
// 确保分页参数有效
if (page < 1) page = 1;
if (pageSize < 1) pageSize = 20;
if (pageSize > 100) pageSize = 100;
// 构建查询 - 用户数据隔离Requirements 7.1
// 只展示已支付或退款的订单,排除待支付(1)和已取消(6)Requirements 9
var query = _dbContext.Orders
.AsNoTracking()
.Where(o => o.UserId == userId && !o.IsDeleted && o.Status != 1 && o.Status != 6);
// 支持按订单类型筛选Requirements 7.1
if (orderType.HasValue)
{
query = query.Where(o => o.OrderType == orderType.Value);
}
// 获取总数
var total = await query.CountAsync();
// 分页查询,按创建时间降序排列
var orders = await query
.OrderByDescending(o => o.CreateTime)
.Skip((page - 1) * pageSize)
.Take(pageSize)
.Select(o => new OrderItemDto
{
Id = o.Id,
OrderNo = o.OrderNo,
OrderType = o.OrderType,
ProductName = o.ProductName,
Amount = o.Amount,
Status = o.Status,
StatusText = GetOrderStatusText(o.Status),
CreateTime = o.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
AssessmentRecordId = null // 稍后填充
})
.ToListAsync();
// 获取测评订单关联的测评记录信息ID + 状态)
if (orders.Any(o => o.OrderType == 1))
{
var orderIds = orders.Where(o => o.OrderType == 1).Select(o => o.Id).ToList();
var assessmentRecords = await _dbContext.AssessmentRecords
.AsNoTracking()
.Where(r => orderIds.Contains(r.OrderId) && !r.IsDeleted)
.Select(r => new { r.OrderId, r.Id, r.Status })
.ToListAsync();
foreach (var order in orders.Where(o => o.OrderType == 1))
{
var record = assessmentRecords.FirstOrDefault(r => r.OrderId == order.Id);
if (record != null)
{
order.AssessmentRecordId = record.Id;
order.AssessmentStatus = record.Status;
}
}
}
// 获取规划订单关联的预约记录状态
if (orders.Any(o => o.OrderType == 2))
{
var bookingOrderIds = orders.Where(o => o.OrderType == 2).Select(o => o.Id).ToList();
var bookings = await _dbContext.PlannerBookings
.AsNoTracking()
.Where(b => bookingOrderIds.Contains(b.OrderId) && !b.IsDeleted)
.Select(b => new { b.OrderId, b.Status })
.ToListAsync();
foreach (var order in orders.Where(o => o.OrderType == 2))
{
var booking = bookings.FirstOrDefault(b => b.OrderId == order.Id);
if (booking != null)
{
order.BookingStatus = booking.Status;
}
}
}
// 计算综合显示状态文本
foreach (var order in orders)
{
order.DisplayStatusText = GetDisplayStatusText(order.Status, order.AssessmentStatus, order.OrderType, order.BookingStatus);
}
_logger.LogDebug("获取到 {Count} 条订单记录,总数: {Total}", orders.Count, total);
return PagedResult<OrderItemDto>.Create(orders, total, page, pageSize);
}
/// <inheritdoc />
public async Task<OrderDetailDto?> GetDetailAsync(long userId, long orderId)
{
_logger.LogDebug("获取订单详情userId: {UserId}, orderId: {OrderId}", userId, orderId);
// 查询订单验证归属当前用户Requirements 7.2, 7.3
var order = await _dbContext.Orders
.AsNoTracking()
.Where(o => o.Id == orderId && !o.IsDeleted)
.FirstOrDefaultAsync();
// 订单不存在
if (order == null)
{
_logger.LogWarning("订单不存在orderId: {OrderId}", orderId);
return null;
}
// 验证订单归属当前用户Requirements 7.3
if (order.UserId != userId)
{
_logger.LogWarning("订单不属于当前用户userId: {UserId}, orderUserId: {OrderUserId}, orderId: {OrderId}",
userId, order.UserId, orderId);
return null;
}
// 构建订单详情
var orderDetail = new OrderDetailDto
{
Id = order.Id,
OrderNo = order.OrderNo,
OrderType = order.OrderType,
ProductName = order.ProductName,
Amount = order.Amount,
PayAmount = order.PayAmount,
Status = order.Status,
StatusText = GetOrderStatusText(order.Status),
CreateTime = order.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
PayTime = order.PayTime?.ToString("yyyy-MM-dd HH:mm:ss")
};
// 根据订单类型获取关联信息Requirements 7.2
if (order.OrderType == 1)
{
// 测评订单 - 获取关联的测评记录
var assessmentRecord = await _dbContext.AssessmentRecords
.AsNoTracking()
.Where(r => r.OrderId == orderId && !r.IsDeleted)
.Join(
_dbContext.AssessmentTypes.AsNoTracking(),
r => r.AssessmentTypeId,
at => at.Id,
(r, at) => new { Record = r, AssessmentType = at }
)
.FirstOrDefaultAsync();
if (assessmentRecord != null)
{
orderDetail.AssessmentRecordId = assessmentRecord.Record.Id;
orderDetail.AssessmentInfo = new OrderAssessmentInfoDto
{
RecordId = assessmentRecord.Record.Id,
AssessmentName = assessmentRecord.AssessmentType.Name,
Status = assessmentRecord.Record.Status,
StatusText = GetAssessmentStatusText(assessmentRecord.Record.Status)
};
}
}
else if (order.OrderType == 2)
{
// 规划订单 - 获取关联的规划预约
var plannerBooking = await _dbContext.PlannerBookings
.AsNoTracking()
.Where(b => b.OrderId == orderId && !b.IsDeleted)
.Join(
_dbContext.Planners.AsNoTracking(),
b => b.PlannerId,
p => p.Id,
(b, p) => new { Booking = b, Planner = p }
)
.FirstOrDefaultAsync();
if (plannerBooking != null)
{
orderDetail.PlannerBookingId = plannerBooking.Booking.Id;
orderDetail.PlannerInfo = new OrderPlannerInfoDto
{
BookingId = plannerBooking.Booking.Id,
PlannerName = plannerBooking.Planner.Name,
Status = plannerBooking.Booking.Status,
StatusText = GetBookingStatusText(plannerBooking.Booking.Status)
};
}
}
_logger.LogDebug("获取订单详情成功orderId: {OrderId}, orderType: {OrderType}", orderId, order.OrderType);
return orderDetail;
}
/// <inheritdoc />
public async Task<PayResultDto?> GetPayResultAsync(long userId, long orderId)
{
_logger.LogDebug("查询支付结果userId: {UserId}, orderId: {OrderId}", userId, orderId);
// 查询订单验证归属当前用户Requirements 9.2
var order = await _dbContext.Orders
.AsNoTracking()
.Where(o => o.Id == orderId && !o.IsDeleted)
.FirstOrDefaultAsync();
// 订单不存在
if (order == null)
{
_logger.LogWarning("订单不存在orderId: {OrderId}", orderId);
return null;
}
// 验证订单归属当前用户
if (order.UserId != userId)
{
_logger.LogWarning("订单不属于当前用户userId: {UserId}, orderUserId: {OrderUserId}, orderId: {OrderId}",
userId, order.UserId, orderId);
return null;
}
// 构建支付结果
var payResult = new PayResultDto
{
IsPaid = order.Status >= 2 && order.Status != 6, // 已支付、已完成、退款中、已退款都算已支付,已取消不算
Status = order.Status,
AssessmentRecordId = null
};
// 如果是测评订单获取关联的测评记录ID
if (order.OrderType == 1)
{
var assessmentRecord = await _dbContext.AssessmentRecords
.AsNoTracking()
.Where(r => r.OrderId == orderId && !r.IsDeleted)
.Select(r => r.Id)
.FirstOrDefaultAsync();
if (assessmentRecord > 0)
{
payResult.AssessmentRecordId = assessmentRecord;
}
}
_logger.LogDebug("查询支付结果成功orderId: {OrderId}, isPaid: {IsPaid}, status: {Status}",
orderId, payResult.IsPaid, payResult.Status);
return payResult;
}
/// <inheritdoc />
public async Task<bool> HandlePaymentSuccessAsync(string orderNo, string transactionId)
{
_logger.LogInformation("处理支付成功回调orderNo: {OrderNo}, transactionId: {TransactionId}", orderNo, transactionId);
var order = await _dbContext.Orders
.FirstOrDefaultAsync(o => o.OrderNo == orderNo && !o.IsDeleted);
if (order == null)
{
_logger.LogWarning("支付回调订单不存在orderNo: {OrderNo}", orderNo);
return false;
}
// 幂等:已支付的订单不重复处理
if (order.Status >= 2)
{
_logger.LogInformation("订单已处理跳过orderNo: {OrderNo}, status: {Status}", orderNo, order.Status);
return true;
}
// 更新订单状态为已支付
order.Status = 2;
order.PayTime = DateTime.Now;
order.TransactionId = transactionId;
order.PayType = 1; // 微信支付
order.UpdateTime = DateTime.Now;
// 测评订单:更新关联的测评记录状态为待测评
if (order.OrderType == 1)
{
var record = await _dbContext.AssessmentRecords
.FirstOrDefaultAsync(r => r.OrderId == order.Id && !r.IsDeleted);
if (record != null)
{
record.Status = 1; // 待测评
record.UpdateTime = DateTime.Now;
}
}
await _dbContext.SaveChangesAsync();
// 佣金分配Requirements 3.8.3
try
{
await CreateCommissionsAsync(order);
}
catch (Exception ex)
{
// 佣金分配失败不影响订单状态,记录日志后续补偿
_logger.LogError(ex, "佣金分配异常orderId: {OrderId},需人工处理", order.Id);
}
_logger.LogInformation("订单支付状态更新成功orderNo: {OrderNo}, orderId: {OrderId}", orderNo, order.Id);
return true;
}
/// <summary>
/// 支付成功后创建佣金记录
/// 规则:
/// - 有直接上级和间接上级直接上级30%间接上级10%
/// - 只有直接上级无间接上级直接上级40%
/// - 无上级:不产生佣金
/// </summary>
private async Task CreateCommissionsAsync(MiAssessment.Model.Entities.Order order)
{
if (order.PayAmount <= 0)
{
_logger.LogDebug("订单实付金额为0跳过佣金分配orderId: {OrderId}", order.Id);
return;
}
// 获取下单用户
var user = await _dbContext.Users
.AsNoTracking()
.FirstOrDefaultAsync(u => u.Id == order.UserId && !u.IsDeleted);
if (user == null || !user.ParentUserId.HasValue)
{
_logger.LogDebug("用户无上级跳过佣金分配userId: {UserId}", order.UserId);
return;
}
// 读取佣金比例配置
var directRateStr = await _configService.GetConfigValueAsync("commission_rate_direct");
var indirectRateStr = await _configService.GetConfigValueAsync("commission_rate_indirect");
var directRate = decimal.TryParse(directRateStr, out var dr) ? dr : 0.30m;
var indirectRate = decimal.TryParse(indirectRateStr, out var ir) ? ir : 0.10m;
// 查找直接上级
var parentUser = await _dbContext.Users
.FirstOrDefaultAsync(u => u.Id == user.ParentUserId.Value && !u.IsDeleted);
if (parentUser == null)
{
_logger.LogDebug("直接上级用户不存在跳过佣金分配parentUserId: {ParentUserId}", user.ParentUserId);
return;
}
// 查找间接上级(上上级)
MiAssessment.Model.Entities.User? grandParentUser = null;
if (parentUser.ParentUserId.HasValue)
{
grandParentUser = await _dbContext.Users
.FirstOrDefaultAsync(u => u.Id == parentUser.ParentUserId.Value && !u.IsDeleted);
}
var now = DateTime.Now;
if (grandParentUser != null)
{
// 有间接上级直接上级30%间接上级10%
var directCommission = Math.Round(order.PayAmount * directRate, 2);
var indirectCommission = Math.Round(order.PayAmount * indirectRate, 2);
// 直接上级佣金
_dbContext.Commissions.Add(new MiAssessment.Model.Entities.Commission
{
UserId = parentUser.Id,
FromUserId = user.Id,
OrderId = order.Id,
OrderAmount = order.PayAmount,
CommissionRate = directRate * 100,
CommissionAmount = directCommission,
Level = 1,
Status = 1,
CreateTime = now,
UpdateTime = now
});
parentUser.Balance += directCommission;
parentUser.TotalIncome += directCommission;
parentUser.UpdateTime = now;
// 间接上级佣金
_dbContext.Commissions.Add(new MiAssessment.Model.Entities.Commission
{
UserId = grandParentUser.Id,
FromUserId = user.Id,
OrderId = order.Id,
OrderAmount = order.PayAmount,
CommissionRate = indirectRate * 100,
CommissionAmount = indirectCommission,
Level = 2,
Status = 1,
CreateTime = now,
UpdateTime = now
});
grandParentUser.Balance += indirectCommission;
grandParentUser.TotalIncome += indirectCommission;
grandParentUser.UpdateTime = now;
_logger.LogInformation("佣金分配完成:直接上级{ParentId}获得{DirectAmount},间接上级{GrandParentId}获得{IndirectAmount}orderId: {OrderId}",
parentUser.Id, directCommission, grandParentUser.Id, indirectCommission, order.Id);
}
else
{
// 无间接上级:直接上级获得 directRate + indirectRate
var totalRate = directRate + indirectRate;
var commission = Math.Round(order.PayAmount * totalRate, 2);
_dbContext.Commissions.Add(new MiAssessment.Model.Entities.Commission
{
UserId = parentUser.Id,
FromUserId = user.Id,
OrderId = order.Id,
OrderAmount = order.PayAmount,
CommissionRate = totalRate * 100,
CommissionAmount = commission,
Level = 1,
Status = 1,
CreateTime = now,
UpdateTime = now
});
parentUser.Balance += commission;
parentUser.TotalIncome += commission;
parentUser.UpdateTime = now;
_logger.LogInformation("佣金分配完成:直接上级{ParentId}获得{Amount}无间接上级orderId: {OrderId}",
parentUser.Id, commission, order.Id);
}
await _dbContext.SaveChangesAsync();
}
/// <inheritdoc />
/// <summary>
/// 创建订单
/// </summary>
/// <remarks>
/// 使用数据库事务确保多表操作的数据一致性Requirements 8.4, 8.5
/// - 测评订单创建订单记录和测评记录Requirements 8.1
/// - 规划订单创建订单记录和规划预约记录Requirements 8.2
/// - 邀请码抵扣金额设为0标记邀请码已使用Requirements 8.3
/// </remarks>
public async Task<CreateOrderResponse> CreateAsync(long userId, CreateOrderRequest request)
{
_logger.LogDebug("创建订单userId: {UserId}, orderType: {OrderType}, productId: {ProductId}, inviteCodeId: {InviteCodeId}",
userId, request.OrderType, request.ProductId, request.InviteCodeId);
// 验证订单类型
if (request.OrderType != 1 && request.OrderType != 2)
{
throw new ArgumentException("无效的订单类型");
}
// 获取产品信息和价格
string productName;
decimal amount;
if (request.OrderType == 1)
{
// 测评订单 - 获取测评类型信息
var assessmentType = await _dbContext.AssessmentTypes
.AsNoTracking()
.Where(at => at.Id == request.ProductId && !at.IsDeleted && at.Status == 1)
.FirstOrDefaultAsync();
if (assessmentType == null)
{
throw new ArgumentException("测评类型不存在或已下线");
}
productName = assessmentType.Name;
amount = assessmentType.Price;
// 验证测评信息
if (request.AssessmentInfo == null)
{
throw new ArgumentException("测评订单必须提供测评信息");
}
}
else
{
// 规划订单 - 获取规划师信息
var planner = await _dbContext.Planners
.AsNoTracking()
.Where(p => p.Id == request.ProductId && !p.IsDeleted && p.Status == 1)
.FirstOrDefaultAsync();
if (planner == null)
{
throw new ArgumentException("规划师不存在或已下线");
}
productName = planner.Name + " - 学业规划";
amount = planner.Price;
// 验证规划预约信息
if (request.PlannerInfo == null)
{
throw new ArgumentException("规划订单必须提供预约信息");
}
}
// 计算实付金额
decimal payAmount = amount;
bool needPay = true;
int? payType = null;
// 如果使用邀请码验证并设置金额为0Requirements 8.3
if (request.InviteCodeId.HasValue)
{
var inviteCode = await _dbContext.InviteCodes
.Where(ic => ic.Id == request.InviteCodeId.Value && !ic.IsDeleted)
.FirstOrDefaultAsync();
if (inviteCode == null)
{
throw new ArgumentException("邀请码不存在");
}
if (inviteCode.Status == 3)
{
throw new ArgumentException("邀请码已被使用");
}
if (inviteCode.Status != 2)
{
throw new ArgumentException("邀请码状态无效");
}
// 使用邀请码金额设为0
payAmount = 0;
needPay = false;
payType = 2; // 邀请码支付
}
// 生成订单编号
var orderNo = GenerateOrderNo();
// 使用事务确保数据一致性Requirements 8.4, 8.5
using var transaction = await _dbContext.Database.BeginTransactionAsync();
try
{
var now = DateTime.Now;
// 创建订单记录
var order = new MiAssessment.Model.Entities.Order
{
OrderNo = orderNo,
UserId = userId,
OrderType = request.OrderType,
ProductId = request.ProductId,
ProductName = productName,
Amount = amount,
PayAmount = payAmount,
PayType = payType,
InviteCodeId = request.InviteCodeId,
Status = needPay ? 1 : 2, // 需要支付则待支付,否则已支付
PayTime = needPay ? null : now,
CreateTime = now,
UpdateTime = now,
IsDeleted = false
};
_dbContext.Orders.Add(order);
await _dbContext.SaveChangesAsync();
long? assessmentRecordId = null;
if (request.OrderType == 1)
{
// 测评订单 - 创建测评记录Requirements 8.1
// 需要支付的订单测评记录初始状态为0待支付支付成功后由回调设为1待测评
// 免支付订单邀请码直接设为1待测评
var assessmentRecord = new MiAssessment.Model.Entities.AssessmentRecord
{
UserId = userId,
OrderId = order.Id,
AssessmentTypeId = request.ProductId,
Name = request.AssessmentInfo!.Name,
Phone = request.AssessmentInfo.Phone,
Gender = request.AssessmentInfo.Gender,
Age = request.AssessmentInfo.Age,
EducationStage = request.AssessmentInfo.EducationStage,
Province = request.AssessmentInfo.Province,
City = request.AssessmentInfo.City,
District = request.AssessmentInfo.District,
Status = needPay ? 0 : 1, // 需要支付则待支付(0),免支付则待测评(1)
CreateTime = now,
UpdateTime = now,
IsDeleted = false
};
_dbContext.AssessmentRecords.Add(assessmentRecord);
await _dbContext.SaveChangesAsync();
assessmentRecordId = assessmentRecord.Id;
}
else
{
// 规划订单 - 创建规划预约记录Requirements 8.2
// 解析预约日期时间
DateTime bookingDate = DateTime.Today;
string bookingTime = "待确认";
if (!string.IsNullOrEmpty(request.PlannerInfo!.BookDateTime))
{
if (DateTime.TryParse(request.PlannerInfo.BookDateTime, out var parsedDateTime))
{
bookingDate = parsedDateTime.Date;
bookingTime = parsedDateTime.ToString("HH:mm");
}
}
var plannerBooking = new MiAssessment.Model.Entities.PlannerBooking
{
UserId = userId,
OrderId = order.Id,
PlannerId = request.ProductId,
BookingDate = bookingDate,
BookingTime = bookingTime,
Name = request.PlannerInfo.Name,
Phone = request.PlannerInfo.Phone,
Gender = 0,
Grade = request.PlannerInfo.Grade,
MajorName = request.PlannerInfo.MajorName,
ScoreChinese = request.PlannerInfo.ScoreChinese,
ScoreMath = request.PlannerInfo.ScoreMath,
ScoreEnglish = request.PlannerInfo.ScoreEnglish,
ScorePhysics = request.PlannerInfo.ScorePhysics,
ScoreChemistry = request.PlannerInfo.ScoreChemistry,
ScoreBiology = request.PlannerInfo.ScoreBiology,
ScoreGeography = request.PlannerInfo.ScoreGeography,
ScorePolitics = request.PlannerInfo.ScorePolitics,
FamilyAtmosphere = request.PlannerInfo.FamilyAtmosphere,
Expectation = request.PlannerInfo.Expectation,
Status = 1, // 待确认
CreateTime = now,
UpdateTime = now,
IsDeleted = false
};
_dbContext.PlannerBookings.Add(plannerBooking);
await _dbContext.SaveChangesAsync();
}
// 如果使用邀请码标记邀请码已使用Requirements 8.3
if (request.InviteCodeId.HasValue)
{
var inviteCode = await _dbContext.InviteCodes
.Where(ic => ic.Id == request.InviteCodeId.Value)
.FirstOrDefaultAsync();
if (inviteCode != null)
{
inviteCode.Status = 3; // 已使用
inviteCode.UseUserId = userId;
inviteCode.UseOrderId = order.Id;
inviteCode.UseTime = now;
inviteCode.UpdateTime = now;
await _dbContext.SaveChangesAsync();
}
}
// 提交事务
await transaction.CommitAsync();
_logger.LogInformation("订单创建成功orderId: {OrderId}, orderNo: {OrderNo}, orderType: {OrderType}, payAmount: {PayAmount}, needPay: {NeedPay}",
order.Id, orderNo, request.OrderType, payAmount, needPay);
return new CreateOrderResponse
{
OrderId = order.Id,
OrderNo = orderNo,
PayAmount = payAmount,
NeedPay = needPay,
AssessmentRecordId = assessmentRecordId
};
}
catch (Exception ex)
{
// 回滚事务Requirements 8.4
await transaction.RollbackAsync();
_logger.LogError(ex, "创建订单失败userId: {UserId}, orderType: {OrderType}, productId: {ProductId}",
userId, request.OrderType, request.ProductId);
throw;
}
}
/// <summary>
/// 生成唯一订单编号
/// </summary>
/// <returns>订单编号格式yyyyMMddHHmmss + 6位随机数</returns>
private static string GenerateOrderNo()
{
var timestamp = DateTime.Now.ToString("yyyyMMddHHmmss");
var random = new Random().Next(100000, 999999);
return $"{timestamp}{random}";
}
/// <inheritdoc />
/// <summary>
/// 发起支付
/// </summary>
/// <remarks>
/// 调用微信支付统一下单接口,返回小程序调起支付所需的参数。
/// - 验证订单存在且归属当前用户
/// - 验证订单状态为待支付Status=1
/// - 如果订单已支付Status>=2或已取消Status=6拒绝支付请求
/// - 调用IWechatPayService发起微信支付
/// Requirements: 9.1, 9.3
/// </remarks>
public async Task<PayResponse> PayAsync(long userId, long orderId)
{
_logger.LogDebug("发起支付userId: {UserId}, orderId: {OrderId}", userId, orderId);
// 1. 查询订单
var order = await _dbContext.Orders
.AsNoTracking()
.Where(o => o.Id == orderId && !o.IsDeleted)
.FirstOrDefaultAsync();
// 2. 验证订单存在
if (order == null)
{
_logger.LogWarning("订单不存在orderId: {OrderId}", orderId);
throw new ArgumentException("订单不存在");
}
// 3. 验证订单归属当前用户
if (order.UserId != userId)
{
_logger.LogWarning("订单不属于当前用户userId: {UserId}, orderUserId: {OrderUserId}, orderId: {OrderId}",
userId, order.UserId, orderId);
throw new ArgumentException("无权限操作此订单");
}
// 4. 验证订单状态Requirements 9.3
// 订单已支付Status>=2 且不是已取消)
if (order.Status >= 2 && order.Status != 6)
{
_logger.LogWarning("订单已支付不能重复支付orderId: {OrderId}, status: {Status}", orderId, order.Status);
throw new ArgumentException("订单已支付,不能重复支付");
}
// 订单已取消
if (order.Status == 6)
{
_logger.LogWarning("订单已取消不能支付orderId: {OrderId}", orderId);
throw new ArgumentException("订单已取消,不能支付");
}
// 订单状态不是待支付
if (order.Status != 1)
{
_logger.LogWarning("订单状态异常不能支付orderId: {OrderId}, status: {Status}", orderId, order.Status);
throw new ArgumentException("订单状态异常,不能支付");
}
// 5. 获取用户信息
var user = await _dbContext.Users
.AsNoTracking()
.Where(u => u.Id == userId)
.FirstOrDefaultAsync();
if (user == null)
{
_logger.LogWarning("用户不存在userId: {UserId}", userId);
throw new ArgumentException("用户不存在");
}
// 6. 构建微信支付请求Requirements 9.1
var payRequest = new WechatPayRequest
{
OrderNo = order.OrderNo,
Amount = order.PayAmount,
Body = order.ProductName,
Attach = order.OrderType == 1 ? "assessment" : "planner", // 根据订单类型设置附加数据
OpenId = user.OpenId ?? string.Empty,
UserId = (int)userId
};
_logger.LogDebug("调用微信支付orderNo: {OrderNo}, amount: {Amount}, body: {Body}",
payRequest.OrderNo, payRequest.Amount, payRequest.Body);
// 7. 调用微信支付服务
var payResult = await _wechatPayService.CreatePaymentAsync(payRequest);
// 8. 检查支付结果
if (payResult.Status != 1 || payResult.Data == null)
{
_logger.LogWarning("微信支付下单失败orderId: {OrderId}, msg: {Msg}", orderId, payResult.Msg);
throw new InvalidOperationException(payResult.Msg ?? "支付下单失败,请稍后重试");
}
_logger.LogInformation("微信支付下单成功orderId: {OrderId}, orderNo: {OrderNo}", orderId, order.OrderNo);
// 9. 返回支付参数
return new PayResponse
{
TimeStamp = payResult.Data.TimeStamp,
NonceStr = payResult.Data.NonceStr,
Package = payResult.Data.Package,
SignType = payResult.Data.SignType,
PaySign = payResult.Data.PaySign
};
}
/// <summary>
/// 获取订单状态文本
/// </summary>
/// <param name="status">订单状态值</param>
/// <returns>状态文本</returns>
private static string GetOrderStatusText(int status)
{
return status switch
{
1 => "待支付",
2 => "已支付",
3 => "已完成",
4 => "退款中",
5 => "已退款",
6 => "已取消",
_ => "未知"
};
}
/// <summary>
/// 获取综合显示状态文本(结合订单状态和测评记录状态)
/// </summary>
/// <param name="orderStatus">订单状态</param>
/// <param name="assessmentStatus">测评记录状态可为null</param>
/// <returns>用于前端展示的状态文本</returns>
private static string GetDisplayStatusText(int orderStatus, int? assessmentStatus, int orderType = 1, int? bookingStatus = null)
{
// 退款相关状态优先
if (orderStatus == 4) return "退款中";
if (orderStatus == 5) return "已退款";
if (orderStatus == 6) return "已取消";
if (orderStatus == 1) return "待支付";
// 规划订单,根据预约状态显示
if (orderType == 2 && bookingStatus.HasValue)
{
return GetBookingStatusText(bookingStatus.Value);
}
// 测评订单,根据测评记录状态显示
if (assessmentStatus.HasValue)
{
return assessmentStatus.Value switch
{
1 => "待测评",
2 => "测评中",
3 => "测评生成中",
4 => "已测评",
5 => "生成失败",
6 => "报告生成中",
_ => GetOrderStatusText(orderStatus)
};
}
return GetOrderStatusText(orderStatus);
}
/// <summary>
/// 获取测评状态文本
/// </summary>
/// <param name="status">测评状态值</param>
/// <returns>状态文本</returns>
private static string GetAssessmentStatusText(int status)
{
return status switch
{
0 => "待支付",
1 => "待测评",
2 => "测评中",
3 => "生成中",
4 => "已完成",
5 => "生成失败",
6 => "报告生成中",
_ => "未知"
};
}
/// <summary>
/// 获取预约状态文本
/// </summary>
/// <param name="status">预约状态值</param>
/// <returns>状态文本</returns>
private static string GetBookingStatusText(int status)
{
return status switch
{
1 => "待联系",
2 => "联系中",
3 => "已完成",
4 => "已取消",
_ => "未知"
};
}
}