2026-01-17 12:52:58 +08:00
|
|
|
|
using System.Text;
|
2026-04-05 20:58:17 +08:00
|
|
|
|
using OfficeOpenXml;
|
|
|
|
|
|
using OfficeOpenXml.Style;
|
|
|
|
|
|
using System.Drawing;
|
2026-03-10 16:54:18 +08:00
|
|
|
|
using YD_Event.Application.Auth;
|
2026-04-15 09:58:05 +08:00
|
|
|
|
using YD_Event.Application.Event;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
using YD_Event.Application.Order.Dtos;
|
|
|
|
|
|
using YD_Event.Core.Entities;
|
|
|
|
|
|
using YD_Event.Core.Enum;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
using YD_Event.Core.Domain;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
|
|
|
|
|
namespace YD_Event.Application.Order;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 订单管理服务
|
|
|
|
|
|
/// </summary>
|
2026-03-07 23:47:13 +08:00
|
|
|
|
public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransient
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-03-07 23:47:13 +08:00
|
|
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
|
|
|
|
|
private readonly ISqlSugarRepository<EventRegistration> _registrationRepository;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
private readonly ISqlSugarRepository<EventRegistrationItem> _itemRepository;
|
|
|
|
|
|
private readonly ISqlSugarRepository<EventRegistrationPerson> _personRepository;
|
|
|
|
|
|
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
|
2026-03-02 09:54:52 +08:00
|
|
|
|
private readonly ISqlSugarRepository<EventInfo> _eventRepository;
|
2026-03-07 23:47:13 +08:00
|
|
|
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Merchant> _merchantRepository;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
private readonly IIdGenerator _idGenerator;
|
2026-03-10 16:54:18 +08:00
|
|
|
|
private readonly AuthManager _authManager;
|
2026-03-30 17:06:12 +08:00
|
|
|
|
private readonly ISqlSugarClient _db;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
private readonly OrderNoGeneratorService _orderNoGeneratorService;
|
|
|
|
|
|
private readonly ILogger<OrderService> _logger;
|
2026-04-15 09:58:05 +08:00
|
|
|
|
private readonly UnifiedRegistrationService _unifiedRegistrationService;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
2026-03-07 23:47:13 +08:00
|
|
|
|
public OrderService(
|
|
|
|
|
|
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
|
|
|
|
|
ISqlSugarRepository<EventRegistration> registrationRepository,
|
2026-01-17 12:52:58 +08:00
|
|
|
|
ISqlSugarRepository<EventRegistrationItem> itemRepository,
|
|
|
|
|
|
ISqlSugarRepository<EventRegistrationPerson> personRepository,
|
|
|
|
|
|
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
|
2026-03-02 09:54:52 +08:00
|
|
|
|
ISqlSugarRepository<EventInfo> eventRepository,
|
2026-03-07 23:47:13 +08:00
|
|
|
|
ISqlSugarRepository<YD_Event.Core.Entities.Merchant> merchantRepository,
|
2026-03-10 16:54:18 +08:00
|
|
|
|
IIdGenerator idGenerator,
|
2026-03-30 17:06:12 +08:00
|
|
|
|
AuthManager authManager,
|
2026-04-10 22:39:32 +08:00
|
|
|
|
ISqlSugarClient db,
|
|
|
|
|
|
OrderNoGeneratorService orderNoGeneratorService,
|
2026-04-15 09:58:05 +08:00
|
|
|
|
ILogger<OrderService> logger,
|
|
|
|
|
|
UnifiedRegistrationService unifiedRegistrationService) : base(orderRepository)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-03-07 23:47:13 +08:00
|
|
|
|
_orderRepository = orderRepository;
|
|
|
|
|
|
_registrationRepository = registrationRepository;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
_itemRepository = itemRepository;
|
|
|
|
|
|
_personRepository = personRepository;
|
|
|
|
|
|
_paymentRepository = paymentRepository;
|
2026-03-02 09:54:52 +08:00
|
|
|
|
_eventRepository = eventRepository;
|
2026-03-07 23:47:13 +08:00
|
|
|
|
_merchantRepository = merchantRepository;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
_idGenerator = idGenerator;
|
2026-03-10 16:54:18 +08:00
|
|
|
|
_authManager = authManager;
|
2026-03-30 17:06:12 +08:00
|
|
|
|
_db = db;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
_orderNoGeneratorService = orderNoGeneratorService;
|
|
|
|
|
|
_logger = logger;
|
2026-04-15 09:58:05 +08:00
|
|
|
|
_unifiedRegistrationService = unifiedRegistrationService;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 创建订单
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("创建订单")]
|
|
|
|
|
|
[HttpPost("create")]
|
|
|
|
|
|
[UnitOfWork]
|
|
|
|
|
|
public async Task<string> Create(CreateOrderInput input)
|
|
|
|
|
|
{
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 1. 创建报名记录
|
2026-01-17 12:52:58 +08:00
|
|
|
|
var entity = input.Adapt<EventRegistration>();
|
|
|
|
|
|
entity.Id = _idGenerator.NextId();
|
2026-04-10 22:39:32 +08:00
|
|
|
|
entity.OrderNo = await _orderNoGeneratorService.GenerateOrderNoAsync();
|
2026-04-07 09:04:38 +08:00
|
|
|
|
entity.PaymentStatus = 0; // 未支付
|
2026-01-17 12:52:58 +08:00
|
|
|
|
entity.RegistrationTime = DateTime.Now;
|
2026-04-07 09:04:38 +08:00
|
|
|
|
entity.RegistrationMode = 2; // 后台管理页面只做团队报名
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 计算总金额
|
2026-01-17 12:52:58 +08:00
|
|
|
|
decimal totalAmount = 0;
|
|
|
|
|
|
int totalPerson = 0;
|
|
|
|
|
|
if (input.Items != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var item in input.Items)
|
|
|
|
|
|
{
|
|
|
|
|
|
totalAmount += item.UnitPrice * item.PersonCount;
|
|
|
|
|
|
totalPerson += item.PersonCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
entity.TotalAmount = totalAmount;
|
|
|
|
|
|
entity.TotalPersonCount = totalPerson;
|
|
|
|
|
|
|
2026-03-07 23:47:13 +08:00
|
|
|
|
await _registrationRepository.InsertAsync(entity);
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 2. 创建报名项目
|
2026-03-11 16:00:09 +08:00
|
|
|
|
var createdItems = new List<EventRegistrationItem>();
|
2026-01-17 12:52:58 +08:00
|
|
|
|
if (input.Items != null && input.Items.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
var items = input.Items.Adapt<List<EventRegistrationItem>>();
|
|
|
|
|
|
items.ForEach(x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
x.Id = _idGenerator.NextId();
|
|
|
|
|
|
x.RegistrationId = entity.Id;
|
|
|
|
|
|
x.TotalPrice = x.UnitPrice * x.PersonCount;
|
|
|
|
|
|
});
|
|
|
|
|
|
await _itemRepository.InsertRangeAsync(items);
|
2026-03-11 16:00:09 +08:00
|
|
|
|
createdItems = items;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 3. 创建报名人员
|
2026-01-17 12:52:58 +08:00
|
|
|
|
if (input.Persons != null && input.Persons.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
var persons = input.Persons.Adapt<List<EventRegistrationPerson>>();
|
|
|
|
|
|
persons.ForEach(x =>
|
|
|
|
|
|
{
|
|
|
|
|
|
x.Id = _idGenerator.NextId();
|
|
|
|
|
|
x.RegistrationId = entity.Id;
|
2026-03-11 16:00:09 +08:00
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 根据项目ID和组别名称关联到报名项目
|
2026-03-11 16:00:09 +08:00
|
|
|
|
var linkedItem = createdItems.FirstOrDefault(i => i.ProjectId == x.ProjectId && i.GroupName == x.GroupName);
|
|
|
|
|
|
if (linkedItem != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
x.RegistrationItemId = linkedItem.Id;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 确保组别名称不为空(数据库约束)
|
2026-03-11 16:00:09 +08:00
|
|
|
|
if (string.IsNullOrEmpty(x.GroupName)) x.GroupName = "";
|
2026-01-17 12:52:58 +08:00
|
|
|
|
});
|
|
|
|
|
|
await _personRepository.InsertRangeAsync(persons);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 4. 创建订单(收入)
|
2026-03-11 16:00:09 +08:00
|
|
|
|
// 获取赛事关联的商户ID
|
|
|
|
|
|
var eventInfo = await _eventRepository.GetByIdAsync(input.EventId);
|
|
|
|
|
|
|
2026-03-30 17:06:12 +08:00
|
|
|
|
// 获取当前用户的单位认证ID
|
|
|
|
|
|
var certification = await _db.Queryable<MerchantCertification>()
|
|
|
|
|
|
.FirstAsync(c => c.UserId == entity.CreatedUserId && c.AuditStatus == AuditStatus.Pass && !c.DeleteMark);
|
|
|
|
|
|
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var order = new YD_Event.Core.Entities.Order
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = _idGenerator.NextId(),
|
|
|
|
|
|
OrderNo = entity.OrderNo,
|
2026-04-07 09:04:38 +08:00
|
|
|
|
OrderType = 1, // 收入
|
2026-03-30 17:06:12 +08:00
|
|
|
|
MerchantId = eventInfo?.MerchantId, // 设置商户ID(支付商户)
|
|
|
|
|
|
MerchantCertificationId = certification?.Id, // 设置单位认证ID
|
2026-03-07 23:47:13 +08:00
|
|
|
|
EventId = input.EventId,
|
|
|
|
|
|
RegistrationId = entity.Id,
|
|
|
|
|
|
OriginalAmount = totalAmount,
|
2026-04-07 09:04:38 +08:00
|
|
|
|
ActualAmount = totalAmount, // 折扣前金额
|
2026-03-07 23:47:13 +08:00
|
|
|
|
DiscountAmount = 0,
|
2026-04-07 09:04:38 +08:00
|
|
|
|
OrderStatus = OrderStatus.Pending, // 待支付
|
2026-03-07 23:47:13 +08:00
|
|
|
|
CreatedTime = DateTime.Now,
|
|
|
|
|
|
CreatedUserId = entity.CreatedUserId
|
|
|
|
|
|
};
|
|
|
|
|
|
await _orderRepository.InsertAsync(order);
|
|
|
|
|
|
|
2026-01-17 12:52:58 +08:00
|
|
|
|
return entity.OrderNo;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 订单分页查询
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("订单分页查询")]
|
2026-03-07 23:47:13 +08:00
|
|
|
|
[HttpGet("page")]
|
|
|
|
|
|
public async Task<PageResult<OrderListOutput>> Page([FromQuery] OrderQueryInput input)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-03-10 16:54:18 +08:00
|
|
|
|
var userId = _authManager.UserId;
|
2026-03-30 17:06:12 +08:00
|
|
|
|
// 判断是否是单位认证账号
|
|
|
|
|
|
var certification = await _db.Queryable<MerchantCertification>()
|
|
|
|
|
|
.FirstAsync(c => c.UserId == userId && c.AuditStatus == AuditStatus.Pass && !c.DeleteMark);
|
2026-03-10 16:54:18 +08:00
|
|
|
|
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var query = _orderRepository.AsQueryable()
|
|
|
|
|
|
.LeftJoin<EventInfo>((o, e) => o.EventId == e.Id)
|
|
|
|
|
|
.LeftJoin<YD_Event.Core.Entities.Merchant>((o, e, m) => o.MerchantId == m.Id)
|
|
|
|
|
|
.LeftJoin<EventRegistration>((o, e, m, r) => o.RegistrationId == r.Id)
|
2026-04-07 09:04:38 +08:00
|
|
|
|
.LeftJoin<YD_Event.Core.Entities.Order>((o, e, m, r, ro) => o.RelatedOrderId == ro.Id) // 自连接获取关联订单编号
|
2026-04-05 20:58:17 +08:00
|
|
|
|
// 默认只显示已完成支付流程的订单(已支付、已发货、已完成、已退款)
|
|
|
|
|
|
.WhereIF(!input.OrderStatus.HasValue, (o, e, m, r, ro) =>
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Paid ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Shipped ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Completed ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Refunded)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.OrderNo), (o, e, m, r, ro) => o.OrderNo.Contains(input.OrderNo))
|
|
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.EventName), (o, e, m, r, ro) => e.EventName.Contains(input.EventName))
|
|
|
|
|
|
.WhereIF(input.OrderType.HasValue, (o, e, m, r, ro) => o.OrderType == input.OrderType)
|
|
|
|
|
|
.WhereIF(input.OrderStatus.HasValue, (o, e, m, r, ro) => o.OrderStatus == input.OrderStatus)
|
2026-04-10 14:29:41 +08:00
|
|
|
|
.WhereIF(input.PaymentMethod.HasValue, (o, e, m, r, ro) => r.PaymentMethod == input.PaymentMethod)
|
|
|
|
|
|
.WhereIF(input.RegistrationMode.HasValue, (o, e, m, r, ro) => r.RegistrationMode == input.RegistrationMode)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.ContactPhone), (o, e, m, r, ro) => r.ContactPhone.Contains(input.ContactPhone))
|
2026-03-10 16:54:18 +08:00
|
|
|
|
.WhereIF(input.StartTime.HasValue, (o, e, m, r, ro) => o.PaymentTime >= input.StartTime) // 支付时间筛选
|
|
|
|
|
|
.WhereIF(input.EndTime.HasValue, (o, e, m, r, ro) => o.PaymentTime <= input.EndTime)
|
|
|
|
|
|
.OrderByDescending((o, e, m, r, ro) => o.CreatedTime);
|
|
|
|
|
|
|
2026-03-30 17:06:12 +08:00
|
|
|
|
// 权限判断:如果是单位认证用户,只能查看自己单位认证的订单
|
|
|
|
|
|
if (certification != null && !_authManager.IsSuperAdmin)
|
2026-03-10 16:54:18 +08:00
|
|
|
|
{
|
2026-03-30 17:06:12 +08:00
|
|
|
|
query = query.Where((o, e, m, r, ro) => o.MerchantCertificationId == certification.Id);
|
2026-03-10 16:54:18 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var result = await query.Select((o, e, m, r, ro) => new OrderListOutput
|
2026-03-07 23:47:13 +08:00
|
|
|
|
{
|
|
|
|
|
|
Id = o.Id,
|
|
|
|
|
|
OrderNo = o.OrderNo,
|
|
|
|
|
|
UnitName = SqlFunc.IsNullOrEmpty(m.MerchantName) ? "/" : m.MerchantName,
|
|
|
|
|
|
EventName = e.EventName,
|
|
|
|
|
|
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
|
|
|
|
|
RelatedOrderNo = ro.OrderNo,
|
2026-04-10 14:29:41 +08:00
|
|
|
|
PaymentMethod = r.PaymentMethod == 2 ? "线上" : "线下",
|
2026-04-13 06:58:21 +08:00
|
|
|
|
PaymentMethodType = r.PaymentMethodType,
|
|
|
|
|
|
PaymentMethodTypeText =
|
|
|
|
|
|
r.PaymentMethodType == PaymentMethodType.WeChatPay ? "微信支付" :
|
|
|
|
|
|
r.PaymentMethodType == PaymentMethodType.AliPay ? "支付宝支付" :
|
|
|
|
|
|
r.PaymentMethodType == PaymentMethodType.BankTransfer ? "银行卡转账" : "未选择",
|
2026-04-10 14:29:41 +08:00
|
|
|
|
PaymentProofUrl = r.PaymentProofUrl,
|
|
|
|
|
|
RegistrationMode = r.RegistrationMode == 1 ? "个人" : "团队",
|
2026-03-07 23:47:13 +08:00
|
|
|
|
ContactName = r.ContactName,
|
|
|
|
|
|
ContactPhone = r.ContactPhone,
|
|
|
|
|
|
OriginalAmount = o.OriginalAmount,
|
|
|
|
|
|
DiscountAmount = o.DiscountAmount,
|
|
|
|
|
|
ActualAmount = o.ActualAmount,
|
|
|
|
|
|
OrderStatus = o.OrderStatus,
|
2026-04-03 09:42:10 +08:00
|
|
|
|
OrderStatusText =
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Pending ? "待支付" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Paid ? "已支付" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Cancelled ? "已取消" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Refunded ? "已退款" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Shipped ? "已发货" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Completed ? "已完成" : "",
|
2026-03-10 16:54:18 +08:00
|
|
|
|
CreatedTime = o.CreatedTime,
|
|
|
|
|
|
PaymentTime = o.PaymentTime,
|
|
|
|
|
|
RefundAmount = o.OrderType == 2 ? o.ActualAmount : 0, // 如果是退款订单,ActualAmount即为退款金额
|
2026-04-21 08:38:45 +08:00
|
|
|
|
RefundTime = o.OrderType == 2 ? o.PaymentTime : null, // 退款时间
|
|
|
|
|
|
RelatedOrderActualAmount = o.OrderType == 2 ? ro.ActualAmount : null, // 退款订单显示原始订单实付金额
|
2026-04-21 09:40:58 +08:00
|
|
|
|
IsRefundOrder = o.OrderType == 2,
|
|
|
|
|
|
PayeeName = o.PayeeName,
|
|
|
|
|
|
PayeeBankName = o.PayeeBankName,
|
|
|
|
|
|
PayeeBankAccount = o.PayeeBankAccount
|
2026-03-10 16:54:18 +08:00
|
|
|
|
})
|
|
|
|
|
|
.ToPagedListAsync(input);
|
2026-03-07 23:47:13 +08:00
|
|
|
|
|
2026-03-10 16:54:18 +08:00
|
|
|
|
return result;
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取订单详情
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("获取订单详情")]
|
|
|
|
|
|
[HttpGet("detail")]
|
2026-03-07 23:47:13 +08:00
|
|
|
|
public async Task<OrderDetailOutput> Detail([FromQuery] long id)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-04-09 23:12:51 +08:00
|
|
|
|
var query = _orderRepository.AsQueryable()
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.LeftJoin<EventInfo>((o, e) => o.EventId == e.Id)
|
2026-04-09 23:12:51 +08:00
|
|
|
|
.LeftJoin<EventRegistration>((o, e, r) => o.RegistrationId == r.Id)
|
|
|
|
|
|
.LeftJoin<YD_Event.Core.Entities.Order>((o, e, r, ro) => o.RelatedOrderId == ro.Id)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.Where(o => o.Id == id)
|
2026-04-21 08:38:45 +08:00
|
|
|
|
.Select((o, e, r, ro) => new { Order = o, EventName = e.EventName, Registration = r, RelatedOrderNo = ro.OrderNo, RelatedOrderActualAmount = ro.ActualAmount });
|
2026-04-09 23:12:51 +08:00
|
|
|
|
|
|
|
|
|
|
var orderInfo = await query.FirstAsync();
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
2026-04-09 23:12:51 +08:00
|
|
|
|
if (orderInfo == null || orderInfo.Order == null) throw Oops.Bah("订单不存在");
|
2026-03-07 23:47:13 +08:00
|
|
|
|
|
2026-04-09 23:12:51 +08:00
|
|
|
|
var registrationId = orderInfo.Order.RegistrationId;
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var items = await _itemRepository.AsQueryable().Where(x => x.RegistrationId == registrationId).ToListAsync();
|
|
|
|
|
|
var persons = await _personRepository.AsQueryable().Where(x => x.RegistrationId == registrationId).ToListAsync();
|
|
|
|
|
|
|
2026-04-07 09:04:38 +08:00
|
|
|
|
// 将项目和人员转换为DTO
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var itemDtos = items.Select(x => new OrderItemDto
|
|
|
|
|
|
{
|
2026-04-09 23:12:51 +08:00
|
|
|
|
ProjectName = x.ProjectName,
|
2026-03-07 23:47:13 +08:00
|
|
|
|
GroupName = x.GroupName,
|
|
|
|
|
|
Fee = x.TotalPrice
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
var personDtos = persons.Select(x => new OrderPersonDto
|
|
|
|
|
|
{
|
|
|
|
|
|
Name = x.Name,
|
|
|
|
|
|
GroupName = x.GroupName
|
|
|
|
|
|
}).ToList();
|
|
|
|
|
|
|
|
|
|
|
|
return new OrderDetailOutput
|
|
|
|
|
|
{
|
2026-04-09 23:12:51 +08:00
|
|
|
|
Id = orderInfo.Order.Id,
|
|
|
|
|
|
OrderNo = orderInfo.Order.OrderNo,
|
|
|
|
|
|
OrderType = orderInfo.Order.OrderType,
|
|
|
|
|
|
OrderTypeText = orderInfo.Order.OrderType == 1 ? "收入" : "退款",
|
|
|
|
|
|
OrderStatus = orderInfo.Order.OrderStatus,
|
|
|
|
|
|
OrderStatusText =
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Pending ? "待支付" :
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Paid ? "已支付" :
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Cancelled ? "已取消" :
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Refunded ? "已退款" :
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Shipped ? "已发货" :
|
|
|
|
|
|
orderInfo.Order.OrderStatus == OrderStatus.Completed ? "已完成" : "",
|
|
|
|
|
|
EventName = orderInfo.EventName,
|
2026-03-07 23:47:13 +08:00
|
|
|
|
ProjectNames = string.Join(",", items.Select(x => x.ProjectName).Distinct()),
|
|
|
|
|
|
GroupNames = string.Join(",", items.Select(x => x.GroupName).Distinct()),
|
|
|
|
|
|
Items = itemDtos,
|
|
|
|
|
|
Persons = personDtos,
|
2026-04-10 14:29:41 +08:00
|
|
|
|
PaymentMethod = orderInfo.Registration?.PaymentMethod == 2 ? "线上" : "线下",
|
2026-04-13 06:58:21 +08:00
|
|
|
|
PaymentMethodType = orderInfo.Registration?.PaymentMethodType ?? PaymentMethodType.None,
|
|
|
|
|
|
PaymentMethodTypeText =
|
|
|
|
|
|
orderInfo.Registration?.PaymentMethodType == PaymentMethodType.WeChatPay ? "微信支付" :
|
|
|
|
|
|
orderInfo.Registration?.PaymentMethodType == PaymentMethodType.AliPay ? "支付宝支付" :
|
|
|
|
|
|
orderInfo.Registration?.PaymentMethodType == PaymentMethodType.BankTransfer ? "银行卡转账" : "未选择",
|
2026-04-10 14:29:41 +08:00
|
|
|
|
PaymentProofUrl = orderInfo.Registration?.PaymentProofUrl,
|
|
|
|
|
|
RegistrationMode = orderInfo.Registration?.RegistrationMode == 1 ? "个人" : "团队",
|
2026-04-09 23:12:51 +08:00
|
|
|
|
OriginalAmount = orderInfo.Order.OriginalAmount,
|
|
|
|
|
|
DiscountAmount = orderInfo.Order.DiscountAmount,
|
|
|
|
|
|
ActualAmount = orderInfo.Order.ActualAmount,
|
|
|
|
|
|
CreatedTime = orderInfo.Order.CreatedTime,
|
|
|
|
|
|
RelatedOrderNo = orderInfo.RelatedOrderNo,
|
|
|
|
|
|
ContactName = orderInfo.Registration?.ContactName,
|
2026-04-21 08:38:45 +08:00
|
|
|
|
ContactPhone = orderInfo.Registration?.ContactPhone,
|
|
|
|
|
|
RelatedOrderActualAmount = orderInfo.Order.OrderType == 2 ? orderInfo.RelatedOrderActualAmount : null,
|
|
|
|
|
|
IsRefundOrder = orderInfo.Order.OrderType == 2
|
2026-03-07 23:47:13 +08:00
|
|
|
|
};
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-04-09 20:49:36 +08:00
|
|
|
|
/// 批量更新订单支付状态
|
2026-01-17 12:52:58 +08:00
|
|
|
|
/// </summary>
|
2026-04-09 20:49:36 +08:00
|
|
|
|
[DisplayName("批量更新订单支付状态")]
|
|
|
|
|
|
[HttpPut("payment-status")]
|
2026-04-10 22:39:32 +08:00
|
|
|
|
[UnitOfWork]
|
2026-04-09 20:49:36 +08:00
|
|
|
|
public async Task UpdatePaymentStatus(UpdatePaymentStatusInput input)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-04-09 20:49:36 +08:00
|
|
|
|
if (input.Ids == null || !input.Ids.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
throw Oops.Bah("请选择要更新的订单");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 22:39:32 +08:00
|
|
|
|
// 验证支付状态值
|
|
|
|
|
|
if (input.PaymentStatus < 0 || input.PaymentStatus > 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw Oops.Bah("无效的支付状态值");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 操作前数据一致性检查:确保所有订单都存在且状态一致
|
2026-04-09 20:49:36 +08:00
|
|
|
|
var orders = await _orderRepository.AsQueryable()
|
|
|
|
|
|
.Where(o => input.Ids.Contains(o.Id))
|
|
|
|
|
|
.ToListAsync();
|
2026-01-17 12:52:58 +08:00
|
|
|
|
|
2026-04-09 20:49:36 +08:00
|
|
|
|
if (!orders.Any())
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-04-09 20:49:36 +08:00
|
|
|
|
throw Oops.Bah("未找到订单");
|
2026-03-07 23:47:13 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-10 22:39:32 +08:00
|
|
|
|
// 检查是否有重复的订单ID
|
|
|
|
|
|
if (orders.Count != input.Ids.Distinct().Count())
|
|
|
|
|
|
{
|
|
|
|
|
|
throw Oops.Bah("订单ID列表存在重复");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 检查所有订单对应的报名记录是否存在
|
|
|
|
|
|
var registrationIds = orders.Select(o => o.RegistrationId).Distinct().ToList();
|
|
|
|
|
|
var registrations = await _registrationRepository.AsQueryable()
|
|
|
|
|
|
.Where(r => registrationIds.Contains(r.Id))
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
if (registrations.Count != registrationIds.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
var missingIds = registrationIds.Except(registrations.Select(r => r.Id)).ToList();
|
|
|
|
|
|
_logger.LogError("批量更新支付状态时发现缺失的报名记录: {MissingIds}", string.Join(",", missingIds));
|
|
|
|
|
|
throw Oops.Bah("部分订单关联的报名记录不存在,请联系管理员");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-09 20:49:36 +08:00
|
|
|
|
var currentUserId = _authManager.UserId;
|
|
|
|
|
|
var now = DateTime.Now;
|
2026-04-10 22:39:32 +08:00
|
|
|
|
var updatedCount = 0;
|
2026-04-09 20:49:36 +08:00
|
|
|
|
|
|
|
|
|
|
foreach (var order in orders)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
{
|
2026-04-09 20:49:36 +08:00
|
|
|
|
// 更新订单的 OrderStatus(以 EventRegistration 的支付状态为准)
|
|
|
|
|
|
if (input.PaymentStatus == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
order.OrderStatus = OrderStatus.Paid;
|
|
|
|
|
|
order.PaymentTime = now;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (input.PaymentStatus == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
order.OrderStatus = OrderStatus.Refunded;
|
|
|
|
|
|
order.PaymentTime = now;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
order.OrderStatus = OrderStatus.Pending;
|
|
|
|
|
|
}
|
|
|
|
|
|
order.UpdatedTime = now;
|
|
|
|
|
|
order.UpdatedUserId = currentUserId;
|
|
|
|
|
|
await _orderRepository.UpdateAsync(order);
|
|
|
|
|
|
|
|
|
|
|
|
// 同步更新报名记录的支付状态
|
|
|
|
|
|
var reg = await _registrationRepository.GetByIdAsync(order.RegistrationId);
|
|
|
|
|
|
if (reg != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
reg.PaymentStatus = input.PaymentStatus;
|
|
|
|
|
|
reg.PaymentTime = order.PaymentTime;
|
|
|
|
|
|
reg.UpdatedTime = now;
|
|
|
|
|
|
reg.UpdatedUserId = currentUserId;
|
|
|
|
|
|
await _registrationRepository.UpdateAsync(reg);
|
|
|
|
|
|
}
|
2026-04-10 22:39:32 +08:00
|
|
|
|
updatedCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 操作后结果校验
|
|
|
|
|
|
var verifyOrders = await _orderRepository.AsQueryable()
|
|
|
|
|
|
.Where(o => input.Ids.Contains(o.Id))
|
|
|
|
|
|
.Select(o => new { o.Id, o.OrderStatus })
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
var expectedStatus = input.PaymentStatus == 1 ? OrderStatus.Paid :
|
|
|
|
|
|
input.PaymentStatus == 3 ? OrderStatus.Refunded :
|
|
|
|
|
|
OrderStatus.Pending;
|
|
|
|
|
|
|
|
|
|
|
|
var failedOrders = verifyOrders.Where(o => o.OrderStatus != expectedStatus).ToList();
|
|
|
|
|
|
if (failedOrders.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
_logger.LogError("批量更新支付状态后校验失败: {FailedOrderIds}",
|
|
|
|
|
|
string.Join(",", failedOrders.Select(o => o.Id)));
|
|
|
|
|
|
throw Oops.Bah("部分订单状态更新失败,请重试");
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
2026-04-10 22:39:32 +08:00
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("批量更新支付状态成功: 更新数量={Count}, 目标状态={Status}, 操作人={UserId}",
|
|
|
|
|
|
updatedCount, input.PaymentStatus, currentUserId);
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-03-02 09:54:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取建议退款金额
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("获取建议退款金额")]
|
|
|
|
|
|
[HttpGet("refund-amount")]
|
|
|
|
|
|
public async Task<decimal> GetRefundAmount([FromQuery] long orderId)
|
|
|
|
|
|
{
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var order = await _orderRepository.GetByIdAsync(orderId);
|
2026-03-02 09:54:52 +08:00
|
|
|
|
if (order == null) throw Oops.Bah("订单不存在");
|
|
|
|
|
|
|
|
|
|
|
|
var eventInfo = await _eventRepository.GetByIdAsync(order.EventId);
|
2026-03-07 23:47:13 +08:00
|
|
|
|
if (eventInfo == null) return order.ActualAmount;
|
2026-03-02 09:54:52 +08:00
|
|
|
|
|
2026-04-15 09:58:05 +08:00
|
|
|
|
_logger.LogInformation("调用统一退款金额计算:订单ID={OrderId}, 总金额={TotalAmount}, 赛事开始时间={EventStartTime}",
|
|
|
|
|
|
orderId, order.ActualAmount, eventInfo.MatchStartTime);
|
|
|
|
|
|
return await _unifiedRegistrationService.CalculateRefundAmountAsync(order.ActualAmount, eventInfo.MatchStartTime);
|
2026-03-02 09:54:52 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-17 12:52:58 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 订单退款
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("订单退款")]
|
|
|
|
|
|
[HttpPost("refund")]
|
|
|
|
|
|
[UnitOfWork]
|
|
|
|
|
|
public async Task Refund(RefundOrderInput input)
|
|
|
|
|
|
{
|
2026-04-09 20:49:36 +08:00
|
|
|
|
var currentUserId = _authManager.UserId;
|
2026-04-15 09:58:05 +08:00
|
|
|
|
_logger.LogInformation("调用统一后台退款:订单ID={OrderId}, 操作人={UserId}", input.Id, currentUserId);
|
|
|
|
|
|
await _unifiedRegistrationService.ExecuteAdminRefundAsync(input, currentUserId);
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2026-04-09 20:49:36 +08:00
|
|
|
|
/// 删除订单(软删除)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("删除订单")]
|
|
|
|
|
|
[HttpPost("delete")]
|
|
|
|
|
|
public async Task Delete([FromBody] List<long> ids)
|
|
|
|
|
|
{
|
2026-04-09 20:49:36 +08:00
|
|
|
|
if (ids == null || !ids.Any())
|
|
|
|
|
|
{
|
|
|
|
|
|
throw Oops.Bah("请选择要删除的订单");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var currentUserId = _authManager.UserId;
|
|
|
|
|
|
var now = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
|
|
var orders = await _orderRepository.AsQueryable()
|
|
|
|
|
|
.Where(o => ids.Contains(o.Id))
|
|
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var order in orders)
|
|
|
|
|
|
{
|
|
|
|
|
|
order.DeleteMark = true;
|
|
|
|
|
|
order.UpdatedTime = now;
|
|
|
|
|
|
order.UpdatedUserId = currentUserId;
|
|
|
|
|
|
await _orderRepository.UpdateAsync(order);
|
|
|
|
|
|
}
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 导出订单
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[DisplayName("导出订单")]
|
|
|
|
|
|
[HttpGet("export")]
|
|
|
|
|
|
public async Task<IActionResult> Export([FromQuery] OrderQueryInput input)
|
|
|
|
|
|
{
|
2026-03-07 23:47:13 +08:00
|
|
|
|
var list = await _orderRepository.AsQueryable()
|
|
|
|
|
|
.LeftJoin<EventInfo>((o, e) => o.EventId == e.Id)
|
|
|
|
|
|
.LeftJoin<YD_Event.Core.Entities.Merchant>((o, e, m) => o.MerchantId == m.Id)
|
|
|
|
|
|
.LeftJoin<EventRegistration>((o, e, m, r) => o.RegistrationId == r.Id)
|
|
|
|
|
|
.LeftJoin<YD_Event.Core.Entities.Order>((o, e, m, r, ro) => o.RelatedOrderId == ro.Id)
|
2026-04-05 20:58:17 +08:00
|
|
|
|
// 默认只导出已完成支付流程的订单(已支付、已发货、已完成、已退款)
|
|
|
|
|
|
.WhereIF(!input.OrderStatus.HasValue, (o, e, m, r, ro) =>
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Paid ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Shipped ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Completed ||
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Refunded)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.OrderNo), (o, e, m, r, ro) => o.OrderNo.Contains(input.OrderNo))
|
|
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.EventName), (o, e, m, r, ro) => e.EventName.Contains(input.EventName))
|
|
|
|
|
|
.WhereIF(input.OrderType.HasValue, (o, e, m, r, ro) => o.OrderType == input.OrderType)
|
|
|
|
|
|
.WhereIF(input.OrderStatus.HasValue, (o, e, m, r, ro) => o.OrderStatus == input.OrderStatus)
|
2026-04-10 14:29:41 +08:00
|
|
|
|
.WhereIF(input.PaymentMethod.HasValue, (o, e, m, r, ro) => r.PaymentMethod == input.PaymentMethod)
|
|
|
|
|
|
.WhereIF(input.RegistrationMode.HasValue, (o, e, m, r, ro) => r.RegistrationMode == input.RegistrationMode)
|
2026-03-07 23:47:13 +08:00
|
|
|
|
.WhereIF(!string.IsNullOrWhiteSpace(input.ContactPhone), (o, e, m, r, ro) => r.ContactPhone.Contains(input.ContactPhone))
|
|
|
|
|
|
.WhereIF(input.StartTime.HasValue, (o, e, m, r, ro) => o.CreatedTime >= input.StartTime)
|
|
|
|
|
|
.WhereIF(input.EndTime.HasValue, (o, e, m, r, ro) => o.CreatedTime <= input.EndTime)
|
|
|
|
|
|
.OrderByDescending((o, e, m, r, ro) => o.CreatedTime)
|
|
|
|
|
|
.Select((o, e, m, r, ro) => new OrderListOutput
|
|
|
|
|
|
{
|
|
|
|
|
|
OrderNo = o.OrderNo,
|
|
|
|
|
|
UnitName = SqlFunc.IsNullOrEmpty(m.MerchantName) ? "/" : m.MerchantName,
|
|
|
|
|
|
EventName = e.EventName,
|
|
|
|
|
|
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
|
|
|
|
|
RelatedOrderNo = ro.OrderNo,
|
2026-04-10 14:29:41 +08:00
|
|
|
|
PaymentMethod = r.PaymentMethod == 2 ? "线上" : "线下",
|
|
|
|
|
|
RegistrationMode = r.RegistrationMode == 1 ? "个人" : "团队",
|
2026-03-07 23:47:13 +08:00
|
|
|
|
ContactName = r.ContactName,
|
|
|
|
|
|
ContactPhone = r.ContactPhone,
|
|
|
|
|
|
OriginalAmount = o.OriginalAmount,
|
|
|
|
|
|
DiscountAmount = o.DiscountAmount,
|
|
|
|
|
|
ActualAmount = o.ActualAmount,
|
|
|
|
|
|
OrderStatus = o.OrderStatus,
|
2026-04-03 09:42:10 +08:00
|
|
|
|
OrderStatusText =
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Pending ? "待支付" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Paid ? "已支付" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Cancelled ? "已取消" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Refunded ? "已退款" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Shipped ? "已发货" :
|
|
|
|
|
|
o.OrderStatus == OrderStatus.Completed ? "已完成" : "",
|
2026-04-21 08:38:45 +08:00
|
|
|
|
CreatedTime = o.CreatedTime,
|
|
|
|
|
|
RelatedOrderActualAmount = o.OrderType == 2 ? ro.ActualAmount : null,
|
2026-04-21 09:40:58 +08:00
|
|
|
|
IsRefundOrder = o.OrderType == 2,
|
|
|
|
|
|
PayeeName = o.PayeeName,
|
|
|
|
|
|
PayeeBankName = o.PayeeBankName,
|
|
|
|
|
|
PayeeBankAccount = o.PayeeBankAccount
|
2026-03-07 23:47:13 +08:00
|
|
|
|
})
|
2026-01-17 12:52:58 +08:00
|
|
|
|
.ToListAsync();
|
|
|
|
|
|
|
2026-04-05 20:58:17 +08:00
|
|
|
|
var bytes = await GenerateOrderExcelAsync(list);
|
|
|
|
|
|
|
|
|
|
|
|
return new FileContentResult(bytes, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
|
|
|
|
|
{
|
|
|
|
|
|
FileDownloadName = $"订单列表_{DateTime.Now:yyyyMMddHHmmss}.xlsx"
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
2026-03-07 23:47:13 +08:00
|
|
|
|
|
2026-04-05 20:58:17 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 生成订单Excel文件
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private async Task<byte[]> GenerateOrderExcelAsync(List<OrderListOutput> orders)
|
|
|
|
|
|
{
|
|
|
|
|
|
ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
|
2026-03-07 23:47:13 +08:00
|
|
|
|
|
2026-04-05 20:58:17 +08:00
|
|
|
|
using var package = new ExcelPackage();
|
|
|
|
|
|
var worksheet = package.Workbook.Worksheets.Add("订单列表");
|
|
|
|
|
|
|
|
|
|
|
|
var rowIndex = 1;
|
|
|
|
|
|
|
|
|
|
|
|
// 计算汇总数据
|
|
|
|
|
|
var totalOriginal = orders.Sum(x => x.OriginalAmount);
|
|
|
|
|
|
var totalDiscount = orders.Sum(x => x.DiscountAmount);
|
|
|
|
|
|
var totalRefund = orders.Where(x => x.OrderType == "退款").Sum(x => x.ActualAmount);
|
2026-04-21 20:39:24 +08:00
|
|
|
|
var totalActual = orders.Where(x => x.OrderType == "收款").Sum(x => x.ActualAmount);
|
2026-04-05 20:58:17 +08:00
|
|
|
|
|
|
|
|
|
|
// 汇总信息行
|
2026-04-21 20:39:24 +08:00
|
|
|
|
var summaryRange = worksheet.Cells[rowIndex, 1, rowIndex, 19];
|
2026-04-05 20:58:17 +08:00
|
|
|
|
summaryRange.Merge = true;
|
|
|
|
|
|
summaryRange.Value = $"汇总信息: 原金额:{totalOriginal}, 优惠金额:{totalDiscount}, 退款金额:{totalRefund}, 实付金额:{totalActual}";
|
|
|
|
|
|
var summaryStyle = summaryRange.Style;
|
|
|
|
|
|
summaryStyle.Font.Bold = true;
|
|
|
|
|
|
summaryStyle.Font.Size = 12;
|
|
|
|
|
|
summaryStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
|
|
|
|
|
summaryStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
|
|
|
|
|
|
summaryStyle.Fill.PatternType = ExcelFillStyle.Solid;
|
|
|
|
|
|
summaryStyle.Fill.BackgroundColor.SetColor(Color.LightGray);
|
|
|
|
|
|
rowIndex++;
|
|
|
|
|
|
|
|
|
|
|
|
// 列标题
|
|
|
|
|
|
var headers = new[]
|
|
|
|
|
|
{
|
|
|
|
|
|
"订单编号", "单位名称", "所属赛事", "订单类型", "关联订单",
|
|
|
|
|
|
"支付方式", "报名方式", "报名用户", "报名账号", "原金额(元)",
|
2026-04-21 20:39:24 +08:00
|
|
|
|
"实付金额(元)", "优惠金额(元)", "退款金额(元)", "订单状态", "生成时间",
|
2026-04-21 09:40:58 +08:00
|
|
|
|
"收款人姓名", "收款人开户行", "收款人银行卡号"
|
2026-04-05 20:58:17 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < headers.Length; i++)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-04-05 20:58:17 +08:00
|
|
|
|
worksheet.Cells[rowIndex, i + 1].Value = headers[i];
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-05 20:58:17 +08:00
|
|
|
|
var headerRange = worksheet.Cells[rowIndex, 1, rowIndex, headers.Length];
|
|
|
|
|
|
var headerStyle = headerRange.Style;
|
|
|
|
|
|
headerStyle.Font.Bold = true;
|
|
|
|
|
|
headerStyle.Font.Size = 11;
|
|
|
|
|
|
headerStyle.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
|
|
|
|
|
headerStyle.VerticalAlignment = ExcelVerticalAlignment.Center;
|
|
|
|
|
|
headerStyle.Fill.PatternType = ExcelFillStyle.Solid;
|
|
|
|
|
|
headerStyle.Fill.BackgroundColor.SetColor(Color.LightBlue);
|
|
|
|
|
|
SetCellBorders(headerRange, ExcelBorderStyle.Thin);
|
|
|
|
|
|
rowIndex++;
|
|
|
|
|
|
|
|
|
|
|
|
// 数据行
|
|
|
|
|
|
foreach (var item in orders)
|
2026-01-17 12:52:58 +08:00
|
|
|
|
{
|
2026-04-21 20:39:24 +08:00
|
|
|
|
// 统一显示为"收款"而不是"收入"
|
|
|
|
|
|
var orderTypeText = item.OrderType == "收入" ? "收款" : item.OrderType;
|
2026-04-05 20:58:17 +08:00
|
|
|
|
var refundAmount = item.OrderType == "退款" ? item.ActualAmount : 0;
|
|
|
|
|
|
var actualPayAmount = item.OrderType == "收入" ? item.ActualAmount : 0;
|
|
|
|
|
|
|
|
|
|
|
|
worksheet.Cells[rowIndex, 1].Value = item.OrderNo;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 2].Value = item.UnitName;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 3].Value = item.EventName;
|
2026-04-21 20:39:24 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 4].Value = orderTypeText;
|
2026-04-05 20:58:17 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 5].Value = item.RelatedOrderNo;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 6].Value = item.PaymentMethod;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 7].Value = item.RegistrationMode;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 8].Value = item.ContactName;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 9].Value = item.ContactPhone;
|
2026-04-21 09:40:58 +08:00
|
|
|
|
|
|
|
|
|
|
// 金额为0时显示为空白
|
|
|
|
|
|
if (item.OriginalAmount != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
worksheet.Cells[rowIndex, 10].Value = item.OriginalAmount;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 10].Style.Numberformat.Format = "0.00";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 20:39:24 +08:00
|
|
|
|
if (actualPayAmount != 0)
|
2026-04-21 09:40:58 +08:00
|
|
|
|
{
|
2026-04-21 20:39:24 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 11].Value = actualPayAmount;
|
2026-04-21 09:40:58 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 11].Style.Numberformat.Format = "0.00";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 20:39:24 +08:00
|
|
|
|
if (item.DiscountAmount != 0)
|
2026-04-21 09:40:58 +08:00
|
|
|
|
{
|
2026-04-21 20:39:24 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 12].Value = item.DiscountAmount;
|
2026-04-21 09:40:58 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 12].Style.Numberformat.Format = "0.00";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 20:39:24 +08:00
|
|
|
|
if (refundAmount != 0)
|
2026-04-21 09:40:58 +08:00
|
|
|
|
{
|
2026-04-21 20:39:24 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 13].Value = refundAmount;
|
2026-04-21 09:40:58 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 13].Style.Numberformat.Format = "0.00";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-21 20:39:24 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 14].Value = item.OrderStatusText;
|
2026-04-21 08:38:45 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 15].Value = item.CreatedTime.ToString("yyyy-MM-dd HH:mm:ss");
|
2026-04-21 09:40:58 +08:00
|
|
|
|
worksheet.Cells[rowIndex, 16].Value = item.PayeeName;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 17].Value = item.PayeeBankName;
|
|
|
|
|
|
worksheet.Cells[rowIndex, 18].Value = item.PayeeBankAccount;
|
2026-04-05 20:58:17 +08:00
|
|
|
|
|
2026-04-21 09:40:58 +08:00
|
|
|
|
SetDataRowStyle(worksheet, rowIndex, 18);
|
2026-04-05 20:58:17 +08:00
|
|
|
|
rowIndex++;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 设置列宽
|
2026-04-21 20:39:24 +08:00
|
|
|
|
var columnWidths = new double[] { 18, 15, 15, 10, 12, 10, 10, 12, 14, 12, 14, 14, 14, 10, 18, 15, 20, 20 };
|
2026-04-05 20:58:17 +08:00
|
|
|
|
for (int i = 0; i < columnWidths.Length; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
worksheet.Column(i + 1).Width = columnWidths[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 冻结表头
|
|
|
|
|
|
worksheet.View.FreezePanes(3, 1);
|
|
|
|
|
|
|
|
|
|
|
|
return await package.GetAsByteArrayAsync();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置单元格边框
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SetCellBorders(ExcelRange range, ExcelBorderStyle borderStyle)
|
|
|
|
|
|
{
|
|
|
|
|
|
range.Style.Border.Top.Style = borderStyle;
|
|
|
|
|
|
range.Style.Border.Bottom.Style = borderStyle;
|
|
|
|
|
|
range.Style.Border.Left.Style = borderStyle;
|
|
|
|
|
|
range.Style.Border.Right.Style = borderStyle;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 设置数据行样式
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void SetDataRowStyle(ExcelWorksheet worksheet, int rowIndex, int colCount)
|
|
|
|
|
|
{
|
|
|
|
|
|
var range = worksheet.Cells[rowIndex, 1, rowIndex, colCount];
|
|
|
|
|
|
var style = range.Style;
|
|
|
|
|
|
style.HorizontalAlignment = ExcelHorizontalAlignment.Center;
|
|
|
|
|
|
style.VerticalAlignment = ExcelVerticalAlignment.Center;
|
|
|
|
|
|
SetCellBorders(range, ExcelBorderStyle.Thin);
|
2026-01-17 12:52:58 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|