订单管理
This commit is contained in:
parent
a9a0ecf0d5
commit
daa5f7b973
@ -4,6 +4,7 @@ using Microsoft.Extensions.Logging;
|
|||||||
using YD_Event.Application.Client.Dtos;
|
using YD_Event.Application.Client.Dtos;
|
||||||
using YD_Event.Core.Entities;
|
using YD_Event.Core.Entities;
|
||||||
using YD_Event.Core.Enum;
|
using YD_Event.Core.Enum;
|
||||||
|
using YD_Event.Core.Utils;
|
||||||
using YD_Event.Core.Infrastructure.WeChat;
|
using YD_Event.Core.Infrastructure.WeChat;
|
||||||
using YD_Event.Core.Infrastructure.WeChat.Dto;
|
using YD_Event.Core.Infrastructure.WeChat.Dto;
|
||||||
using YD_Event.Core.Options;
|
using YD_Event.Core.Options;
|
||||||
@ -48,6 +49,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
private readonly ISqlSugarRepository<SysDistrict> _districtRepository;
|
private readonly ISqlSugarRepository<SysDistrict> _districtRepository;
|
||||||
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
private readonly ISqlSugarRepository<YD_Event.Core.Entities.Order> _orderRepository;
|
||||||
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
private readonly ISqlSugarRepository<MerchantCertification> _merchantCertificationRepository;
|
||||||
|
private readonly ISqlSugarRepository<EventRegistrationPayment> _paymentRepository;
|
||||||
private readonly IHttpContextAccessor _httpContextAccessor;
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
private readonly IIdGenerator _idGenerator;
|
private readonly IIdGenerator _idGenerator;
|
||||||
private readonly WeChatPayService _weChatPayService;
|
private readonly WeChatPayService _weChatPayService;
|
||||||
@ -79,6 +81,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
ISqlSugarRepository<SysDistrict> districtRepository,
|
ISqlSugarRepository<SysDistrict> districtRepository,
|
||||||
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
ISqlSugarRepository<YD_Event.Core.Entities.Order> orderRepository,
|
||||||
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
ISqlSugarRepository<MerchantCertification> merchantCertificationRepository,
|
||||||
|
ISqlSugarRepository<EventRegistrationPayment> paymentRepository,
|
||||||
IHttpContextAccessor httpContextAccessor,
|
IHttpContextAccessor httpContextAccessor,
|
||||||
IIdGenerator idGenerator,
|
IIdGenerator idGenerator,
|
||||||
WeChatPayService weChatPayService,
|
WeChatPayService weChatPayService,
|
||||||
@ -109,6 +112,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
_districtRepository = districtRepository;
|
_districtRepository = districtRepository;
|
||||||
_orderRepository = orderRepository;
|
_orderRepository = orderRepository;
|
||||||
_merchantCertificationRepository = merchantCertificationRepository;
|
_merchantCertificationRepository = merchantCertificationRepository;
|
||||||
|
_paymentRepository = paymentRepository;
|
||||||
_httpContextAccessor = httpContextAccessor;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
_idGenerator = idGenerator;
|
_idGenerator = idGenerator;
|
||||||
_weChatPayService = weChatPayService;
|
_weChatPayService = weChatPayService;
|
||||||
@ -929,7 +933,7 @@ public class EventClientController : IDynamicApiController
|
|||||||
AppId = configOptions.AppId,
|
AppId = configOptions.AppId,
|
||||||
Description = $"{evt.EventName} - 报名费",
|
Description = $"{evt.EventName} - 报名费",
|
||||||
NotifyUrl = configOptions.NotifyUrl,
|
NotifyUrl = configOptions.NotifyUrl,
|
||||||
TotalAmount = (int)(reg.TotalAmount * 100),
|
TotalAmount = AmountHelper.YuanToFen(reg.TotalAmount),
|
||||||
ExpireTime = DateTimeOffset.Now.AddMinutes(30),
|
ExpireTime = DateTimeOffset.Now.AddMinutes(30),
|
||||||
OpenId = input.OpenId
|
OpenId = input.OpenId
|
||||||
};
|
};
|
||||||
@ -1279,6 +1283,24 @@ public class EventClientController : IDynamicApiController
|
|||||||
await _registrationRepository.UpdateAsync(reg);
|
await _registrationRepository.UpdateAsync(reg);
|
||||||
_logger.LogInformation($"订单 {reg.OrderNo} 支付状态同步成功,微信交易号:{queryResult.TransactionId}");
|
_logger.LogInformation($"订单 {reg.OrderNo} 支付状态同步成功,微信交易号:{queryResult.TransactionId}");
|
||||||
|
|
||||||
|
// 创建支付记录
|
||||||
|
var payment = new EventRegistrationPayment
|
||||||
|
{
|
||||||
|
Id = _idGenerator.NewLong(),
|
||||||
|
RegistrationId = reg.Id,
|
||||||
|
TransactionNo = queryResult.TransactionId ?? "",
|
||||||
|
PaymentChannel = 1, // 微信支付
|
||||||
|
PaymentAmount = reg.TotalAmount,
|
||||||
|
PaymentTime = queryResult.SuccessTime?.DateTime ?? DateTime.Now,
|
||||||
|
PaymentStatus = 1, // 已支付
|
||||||
|
RefundStatus = 0, // 未退款
|
||||||
|
PayeeAccount = "", // 收款账户
|
||||||
|
CreatedUserId = userId,
|
||||||
|
CreatedTime = DateTime.Now
|
||||||
|
};
|
||||||
|
await _paymentRepository.InsertAsync(payment);
|
||||||
|
_logger.LogInformation($"支付记录创建成功,报名ID={reg.Id},交易号={queryResult.TransactionId}");
|
||||||
|
|
||||||
// 更新 Order 记录状态
|
// 更新 Order 记录状态
|
||||||
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
|
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
|
||||||
if (order != null)
|
if (order != null)
|
||||||
@ -1412,6 +1434,24 @@ public class EventClientController : IDynamicApiController
|
|||||||
await _registrationRepository.UpdateAsync(reg);
|
await _registrationRepository.UpdateAsync(reg);
|
||||||
_logger.LogInformation($"订单 {reg.OrderNo} 支付成功,微信交易号:{notifyData.TransactionId}");
|
_logger.LogInformation($"订单 {reg.OrderNo} 支付成功,微信交易号:{notifyData.TransactionId}");
|
||||||
|
|
||||||
|
// 创建支付记录
|
||||||
|
var payment = new EventRegistrationPayment
|
||||||
|
{
|
||||||
|
Id = _idGenerator.NewLong(),
|
||||||
|
RegistrationId = reg.Id,
|
||||||
|
TransactionNo = notifyData.TransactionId ?? "",
|
||||||
|
PaymentChannel = 1, // 微信支付
|
||||||
|
PaymentAmount = reg.TotalAmount,
|
||||||
|
PaymentTime = notifyData.SuccessTime?.DateTime ?? DateTime.Now,
|
||||||
|
PaymentStatus = 1, // 已支付
|
||||||
|
RefundStatus = 0, // 未退款
|
||||||
|
PayeeAccount = "", // 收款账户
|
||||||
|
CreatedUserId = reg.CreatedUserId,
|
||||||
|
CreatedTime = DateTime.Now
|
||||||
|
};
|
||||||
|
await _paymentRepository.InsertAsync(payment);
|
||||||
|
_logger.LogInformation($"支付记录创建成功,报名ID={reg.Id},交易号={notifyData.TransactionId}");
|
||||||
|
|
||||||
// 更新 Order 记录状态
|
// 更新 Order 记录状态
|
||||||
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
|
var order = await _orderRepository.GetFirstAsync(o => o.OrderNo == reg.OrderNo);
|
||||||
if (order != null)
|
if (order != null)
|
||||||
|
|||||||
@ -2,6 +2,7 @@ using YD_Event.Application.Event.Dtos;
|
|||||||
using YD_Event.Application.Project.Dtos;
|
using YD_Event.Application.Project.Dtos;
|
||||||
using YD_Event.Core.Entities;
|
using YD_Event.Core.Entities;
|
||||||
using YD_Event.Core.Enum;
|
using YD_Event.Core.Enum;
|
||||||
|
using YD_Event.Core.Utils;
|
||||||
using MiniExcelLibs;
|
using MiniExcelLibs;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
@ -402,7 +403,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
EventId = eventInfo.Id,
|
EventId = eventInfo.Id,
|
||||||
ProjectId = projInput.ProjectId,
|
ProjectId = projInput.ProjectId,
|
||||||
GenderType = projInput.GenderType,
|
GenderType = projInput.GenderType,
|
||||||
Price = (int)(projInput.Fee * 100),
|
Price = AmountHelper.YuanToFen(projInput.Fee),
|
||||||
CreatedTime = DateTime.Now
|
CreatedTime = DateTime.Now
|
||||||
};
|
};
|
||||||
await _eventProjectRepository.InsertAsync(eventProject);
|
await _eventProjectRepository.InsertAsync(eventProject);
|
||||||
@ -576,7 +577,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
EventId = eventInfo.Id,
|
EventId = eventInfo.Id,
|
||||||
ProjectId = projInput.ProjectId,
|
ProjectId = projInput.ProjectId,
|
||||||
GenderType = projInput.GenderType,
|
GenderType = projInput.GenderType,
|
||||||
Price = (int)(projInput.Fee * 100),
|
Price = AmountHelper.YuanToFen(projInput.Fee),
|
||||||
CreatedTime = DateTime.Now
|
CreatedTime = DateTime.Now
|
||||||
};
|
};
|
||||||
await _eventProjectRepository.InsertAsync(eventProject);
|
await _eventProjectRepository.InsertAsync(eventProject);
|
||||||
@ -938,7 +939,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
EventId = input.EventId,
|
EventId = input.EventId,
|
||||||
ProjectId = projInput.ProjectId,
|
ProjectId = projInput.ProjectId,
|
||||||
GenderType = projInput.GenderType,
|
GenderType = projInput.GenderType,
|
||||||
Price = (int)(projInput.Fee * 100),
|
Price = AmountHelper.YuanToFen(projInput.Fee),
|
||||||
CreatedTime = DateTime.Now
|
CreatedTime = DateTime.Now
|
||||||
};
|
};
|
||||||
await _eventProjectRepository.InsertAsync(eventProject);
|
await _eventProjectRepository.InsertAsync(eventProject);
|
||||||
@ -1033,7 +1034,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 格式验证:最多保留两位小数
|
// 格式验证:最多保留两位小数
|
||||||
if (Math.Round(input.FeeConfig.Price, 2) != input.FeeConfig.Price)
|
if (!AmountHelper.IsValidAmount(input.FeeConfig.Price))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("价格格式不正确,最多保留两位小数");
|
throw new InvalidOperationException("价格格式不正确,最多保留两位小数");
|
||||||
}
|
}
|
||||||
@ -1059,7 +1060,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 格式验证:最多保留两位小数
|
// 格式验证:最多保留两位小数
|
||||||
if (Math.Round(feeInput.Fee, 2) != feeInput.Fee)
|
if (!AmountHelper.IsValidAmount(feeInput.Fee))
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("项目单价格式不正确,最多保留两位小数");
|
throw new InvalidOperationException("项目单价格式不正确,最多保留两位小数");
|
||||||
}
|
}
|
||||||
@ -1067,7 +1068,7 @@ public class EventService : BaseService<EventInfo>, ITransient
|
|||||||
var proj = eventProjects.FirstOrDefault(p => p.Id == feeInput.ProjectId);
|
var proj = eventProjects.FirstOrDefault(p => p.Id == feeInput.ProjectId);
|
||||||
if (proj != null)
|
if (proj != null)
|
||||||
{
|
{
|
||||||
proj.Price = (int)(feeInput.Fee * 100);
|
proj.Price = AmountHelper.YuanToFen(feeInput.Fee);
|
||||||
proj.UpdatedTime = DateTime.Now;
|
proj.UpdatedTime = DateTime.Now;
|
||||||
await _eventProjectRepository.UpdateAsync(proj);
|
await _eventProjectRepository.UpdateAsync(proj);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using YD_Event.Application.Event.Dtos;
|
using YD_Event.Application.Event.Dtos;
|
||||||
using YD_Event.Core.Entities;
|
using YD_Event.Core.Entities;
|
||||||
using YD_Event.Core.Enum;
|
using YD_Event.Core.Enum;
|
||||||
|
using YD_Event.Core.Utils;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using YD_Event.Application.Auth;
|
using YD_Event.Application.Auth;
|
||||||
@ -519,8 +520,8 @@ public class UserEventRecordService : IDynamicApiController, ITransient
|
|||||||
{
|
{
|
||||||
OutTradeNo = registration.OrderNo,
|
OutTradeNo = registration.OrderNo,
|
||||||
OutRefundNo = outRefundNo,
|
OutRefundNo = outRefundNo,
|
||||||
RefundAmount = (int)(registration.TotalAmount * 100), // 转分
|
RefundAmount = AmountHelper.YuanToFen(registration.TotalAmount), // 转分
|
||||||
TotalAmount = (int)(registration.TotalAmount * 100),
|
TotalAmount = AmountHelper.YuanToFen(registration.TotalAmount),
|
||||||
Reason = input.CancelReason
|
Reason = input.CancelReason
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -7,6 +7,7 @@ using YD_Event.Application.Project;
|
|||||||
using YD_Event.Core.Entities;
|
using YD_Event.Core.Entities;
|
||||||
using YD_Event.Core.Enum;
|
using YD_Event.Core.Enum;
|
||||||
using YD_Event.Core.Domain.Pricing;
|
using YD_Event.Core.Domain.Pricing;
|
||||||
|
using YD_Event.Core.Utils;
|
||||||
using SqlSugar;
|
using SqlSugar;
|
||||||
using Mapster;
|
using Mapster;
|
||||||
using YD_Event.Application.Auth;
|
using YD_Event.Application.Auth;
|
||||||
@ -234,25 +235,16 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
|
|||||||
.Where(t => !t.DeleteMark && t.Status == AvailabilityStatus.Enable)
|
.Where(t => !t.DeleteMark && t.Status == AvailabilityStatus.Enable)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var completionStats = await _projectService.GetProjectStatisticsBatchAsync(
|
|
||||||
projectIds,
|
|
||||||
ProjectService.ProjectStatisticType.CompletionCount);
|
|
||||||
|
|
||||||
output.Projects = eventProjects.Select(ep =>
|
output.Projects = eventProjects.Select(ep =>
|
||||||
{
|
{
|
||||||
var p = baseProjects.FirstOrDefault(b => b.Id == ep.ProjectId);
|
var p = baseProjects.FirstOrDefault(b => b.Id == ep.ProjectId);
|
||||||
var completionCount = 0;
|
|
||||||
if (p != null && completionStats.TryGetValue(p.Id, out var count))
|
|
||||||
{
|
|
||||||
completionCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dto = new UserEventProjectListOutput
|
var dto = new UserEventProjectListOutput
|
||||||
{
|
{
|
||||||
Id = ep.Id,
|
Id = ep.Id,
|
||||||
Name = p?.Name ?? "",
|
Name = p?.Name ?? "",
|
||||||
Fee = ep.Price / 100m,
|
Fee = ep.Price / 100m,
|
||||||
CompletionCount = completionCount
|
CompletionCount = p?.CompletionCount ?? 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (p != null)
|
if (p != null)
|
||||||
@ -330,18 +322,9 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
|
|||||||
.Where(t => !t.DeleteMark && t.Status == AvailabilityStatus.Enable)
|
.Where(t => !t.DeleteMark && t.Status == AvailabilityStatus.Enable)
|
||||||
.ToListAsync();
|
.ToListAsync();
|
||||||
|
|
||||||
var completionStats = await _projectService.GetProjectStatisticsBatchAsync(
|
|
||||||
projectIds,
|
|
||||||
ProjectService.ProjectStatisticType.CompletionCount);
|
|
||||||
|
|
||||||
return eventProjects.Select(ep =>
|
return eventProjects.Select(ep =>
|
||||||
{
|
{
|
||||||
var p = baseProjects.FirstOrDefault(b => b.Id == ep.ProjectId);
|
var p = baseProjects.FirstOrDefault(b => b.Id == ep.ProjectId);
|
||||||
var completionCount = 0;
|
|
||||||
if (p != null && completionStats.TryGetValue(p.Id, out var count))
|
|
||||||
{
|
|
||||||
completionCount = count;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dto = new UserEventProjectListOutput
|
var dto = new UserEventProjectListOutput
|
||||||
{
|
{
|
||||||
@ -349,7 +332,7 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
|
|||||||
Name = p?.Name ?? "",
|
Name = p?.Name ?? "",
|
||||||
Fee = ep.Price / 100m,
|
Fee = ep.Price / 100m,
|
||||||
TeamSize = 1,
|
TeamSize = 1,
|
||||||
CompletionCount = completionCount
|
CompletionCount = p?.CompletionCount ?? 0
|
||||||
};
|
};
|
||||||
|
|
||||||
if (p != null)
|
if (p != null)
|
||||||
@ -939,7 +922,7 @@ public class UserEventRegistrationService : IDynamicApiController, ITransient
|
|||||||
AppId = configOptions.AppId,
|
AppId = configOptions.AppId,
|
||||||
Description = $"{eventInfo.EventName} - 报名费",
|
Description = $"{eventInfo.EventName} - 报名费",
|
||||||
NotifyUrl = configOptions.NotifyUrl,
|
NotifyUrl = configOptions.NotifyUrl,
|
||||||
TotalAmount = (int)(finalAmount * 100), // 单位为分,使用优惠后的金额
|
TotalAmount = AmountHelper.YuanToFen(finalAmount), // 单位为分,使用优惠后的金额
|
||||||
ExpireTime = DateTimeOffset.Now.AddMinutes(5)
|
ExpireTime = DateTimeOffset.Now.AddMinutes(5)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
18
YD_Event.Application/Order/Dtos/UpdatePaymentStatusInput.cs
Normal file
18
YD_Event.Application/Order/Dtos/UpdatePaymentStatusInput.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
using System.ComponentModel.DataAnnotations;
|
||||||
|
|
||||||
|
namespace YD_Event.Application.Order.Dtos;
|
||||||
|
|
||||||
|
public class UpdatePaymentStatusInput
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 订单ID列表(批量更新)
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public List<long> Ids { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 支付状态:0-未支付,1-已支付,3-已退款
|
||||||
|
/// </summary>
|
||||||
|
[Required]
|
||||||
|
public int PaymentStatus { get; set; }
|
||||||
|
}
|
||||||
@ -136,7 +136,7 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
Id = _idGenerator.NextId(),
|
Id = _idGenerator.NextId(),
|
||||||
OrderNo = entity.OrderNo,
|
OrderNo = entity.OrderNo,
|
||||||
OrderType = 1, // 收入
|
OrderType = 1, // 收入
|
||||||
PaymentMethod = 1, // 默认线上支付
|
PaymentMethod = 2, // 默认线上支付 (1=线下, 2=线上)
|
||||||
RegistrationMode = 2, // 后台管理页面只做团队报名
|
RegistrationMode = 2, // 后台管理页面只做团队报名
|
||||||
MerchantId = eventInfo?.MerchantId, // 设置商户ID(支付商户)
|
MerchantId = eventInfo?.MerchantId, // 设置商户ID(支付商户)
|
||||||
MerchantCertificationId = certification?.Id, // 设置单位认证ID
|
MerchantCertificationId = certification?.Id, // 设置单位认证ID
|
||||||
@ -202,7 +202,7 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
EventName = e.EventName,
|
EventName = e.EventName,
|
||||||
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
||||||
RelatedOrderNo = ro.OrderNo,
|
RelatedOrderNo = ro.OrderNo,
|
||||||
PaymentMethod = o.PaymentMethod == 1 ? "线上" : "线下",
|
PaymentMethod = o.PaymentMethod == 2 ? "线上" : "线下",
|
||||||
PaymentProofUrl = o.PaymentProofUrl,
|
PaymentProofUrl = o.PaymentProofUrl,
|
||||||
RegistrationMode = o.RegistrationMode == 1 ? "个人" : "团队",
|
RegistrationMode = o.RegistrationMode == 1 ? "个人" : "团队",
|
||||||
ContactName = r.ContactName,
|
ContactName = r.ContactName,
|
||||||
@ -282,30 +282,60 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新订单状态
|
/// 批量更新订单支付状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DisplayName("更新订单状态")]
|
[DisplayName("批量更新订单支付状态")]
|
||||||
[HttpPut("status")]
|
[HttpPut("payment-status")]
|
||||||
public async Task UpdateStatus(UpdateOrderStatusInput input)
|
public async Task UpdatePaymentStatus(UpdatePaymentStatusInput input)
|
||||||
{
|
{
|
||||||
var order = await _orderRepository.GetByIdAsync(input.Id);
|
if (input.Ids == null || !input.Ids.Any())
|
||||||
if (order == null) throw Oops.Bah("订单不存在");
|
|
||||||
|
|
||||||
// 更新订单
|
|
||||||
order.OrderStatus = input.PaymentStatus == 1 ? OrderStatus.Paid : OrderStatus.Pending; // 假设1表示支付成功
|
|
||||||
if (input.PaymentStatus == 1)
|
|
||||||
{
|
{
|
||||||
order.PaymentTime = DateTime.Now;
|
throw Oops.Bah("请选择要更新的订单");
|
||||||
}
|
}
|
||||||
await _orderRepository.UpdateAsync(order);
|
|
||||||
|
|
||||||
// 同步更新报名记录
|
var orders = await _orderRepository.AsQueryable()
|
||||||
var reg = await _registrationRepository.GetByIdAsync(order.RegistrationId);
|
.Where(o => input.Ids.Contains(o.Id))
|
||||||
if (reg != null)
|
.ToListAsync();
|
||||||
|
|
||||||
|
if (!orders.Any())
|
||||||
{
|
{
|
||||||
reg.PaymentStatus = input.PaymentStatus;
|
throw Oops.Bah("未找到订单");
|
||||||
reg.PaymentTime = order.PaymentTime;
|
}
|
||||||
await _registrationRepository.UpdateAsync(reg);
|
|
||||||
|
var currentUserId = _authManager.UserId;
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
|
foreach (var order in orders)
|
||||||
|
{
|
||||||
|
// 更新订单的 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -338,6 +368,9 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
|
|
||||||
if (order.OrderStatus != OrderStatus.Paid) throw Oops.Bah("订单未支付,无法退款");
|
if (order.OrderStatus != OrderStatus.Paid) throw Oops.Bah("订单未支付,无法退款");
|
||||||
|
|
||||||
|
var currentUserId = _authManager.UserId;
|
||||||
|
var now = DateTime.Now;
|
||||||
|
|
||||||
// 1. 创建退款订单
|
// 1. 创建退款订单
|
||||||
var refundOrder = new YD_Event.Core.Entities.Order
|
var refundOrder = new YD_Event.Core.Entities.Order
|
||||||
{
|
{
|
||||||
@ -354,30 +387,66 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
OriginalAmount = order.ActualAmount,
|
OriginalAmount = order.ActualAmount,
|
||||||
ActualAmount = input.RefundAmount, // 退款金额
|
ActualAmount = input.RefundAmount, // 退款金额
|
||||||
OrderStatus = OrderStatus.Refunded, // 退款成功
|
OrderStatus = OrderStatus.Refunded, // 退款成功
|
||||||
PaymentTime = DateTime.Now,
|
PaymentTime = now,
|
||||||
Remark = input.Reason,
|
Remark = input.Reason,
|
||||||
CreatedTime = DateTime.Now,
|
CreatedTime = now,
|
||||||
CreatedUserId = _idGenerator.NewLong() // 系统或当前用户
|
CreatedUserId = currentUserId
|
||||||
};
|
};
|
||||||
await _orderRepository.InsertAsync(refundOrder);
|
await _orderRepository.InsertAsync(refundOrder);
|
||||||
|
|
||||||
// 2. 更新报名状态
|
// 2. 更新原订单状态
|
||||||
|
order.OrderStatus = OrderStatus.Refunded;
|
||||||
|
order.UpdatedTime = now;
|
||||||
|
order.UpdatedUserId = currentUserId;
|
||||||
|
await _orderRepository.UpdateAsync(order);
|
||||||
|
|
||||||
|
// 3. 更新报名状态
|
||||||
var reg = await _registrationRepository.GetByIdAsync(order.RegistrationId);
|
var reg = await _registrationRepository.GetByIdAsync(order.RegistrationId);
|
||||||
if (reg != null)
|
if (reg != null)
|
||||||
{
|
{
|
||||||
reg.PaymentStatus = 3; // 已退款
|
reg.PaymentStatus = 3; // 已退款
|
||||||
|
reg.UpdatedTime = now;
|
||||||
|
reg.UpdatedUserId = currentUserId;
|
||||||
await _registrationRepository.UpdateAsync(reg);
|
await _registrationRepository.UpdateAsync(reg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 4. 更新支付记录(如果有)
|
||||||
|
var payment = await _paymentRepository.GetFirstAsync(p => p.RegistrationId == order.RegistrationId);
|
||||||
|
if (payment != null)
|
||||||
|
{
|
||||||
|
payment.RefundStatus = 1; // 已退款
|
||||||
|
payment.UpdatedTime = now;
|
||||||
|
payment.UpdatedUserId = currentUserId;
|
||||||
|
await _paymentRepository.UpdateAsync(payment);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除订单
|
/// 删除订单(软删除)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DisplayName("删除订单")]
|
[DisplayName("删除订单")]
|
||||||
[HttpPost("delete")]
|
[HttpPost("delete")]
|
||||||
public async Task Delete([FromBody] List<long> ids)
|
public async Task Delete([FromBody] List<long> ids)
|
||||||
{
|
{
|
||||||
await _orderRepository.DeleteAsync(x => ids.Contains(x.Id));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -415,7 +484,7 @@ public class OrderService : BaseService<YD_Event.Core.Entities.Order>, ITransien
|
|||||||
EventName = e.EventName,
|
EventName = e.EventName,
|
||||||
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
OrderType = o.OrderType == 1 ? "收入" : "退款",
|
||||||
RelatedOrderNo = ro.OrderNo,
|
RelatedOrderNo = ro.OrderNo,
|
||||||
PaymentMethod = o.PaymentMethod == 1 ? "线上" : "线下",
|
PaymentMethod = o.PaymentMethod == 2 ? "线上" : "线下",
|
||||||
RegistrationMode = o.RegistrationMode == 1 ? "个人" : "团队",
|
RegistrationMode = o.RegistrationMode == 1 ? "个人" : "团队",
|
||||||
ContactName = r.ContactName,
|
ContactName = r.ContactName,
|
||||||
ContactPhone = r.ContactPhone,
|
ContactPhone = r.ContactPhone,
|
||||||
|
|||||||
@ -70,19 +70,6 @@ public class ProjectService : BaseService<YD_Event.Core.Entities.Project>, ITran
|
|||||||
})
|
})
|
||||||
.ToPagedListAsync(input);
|
.ToPagedListAsync(input);
|
||||||
|
|
||||||
var projectIds = result.Rows.Select(x => x.Id).ToList();
|
|
||||||
if (projectIds.Any())
|
|
||||||
{
|
|
||||||
var stats = await GetProjectStatisticsBatchAsync(projectIds, ProjectStatisticType.CompletionCount);
|
|
||||||
foreach (var item in result.Rows)
|
|
||||||
{
|
|
||||||
if (stats.TryGetValue(item.Id, out var count))
|
|
||||||
{
|
|
||||||
item.CompletionCount = count;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,8 +168,6 @@ public class ProjectService : BaseService<YD_Event.Core.Entities.Project>, ITran
|
|||||||
|
|
||||||
var groups = await _groupRepository.GetListAsync(x => x.ProjectId == id);
|
var groups = await _groupRepository.GetListAsync(x => x.ProjectId == id);
|
||||||
dto.Groups = groups.Adapt<List<ProjectGroupDto>>();
|
dto.Groups = groups.Adapt<List<ProjectGroupDto>>();
|
||||||
|
|
||||||
dto.CompletionCount = await GetProjectStatisticAsync(id, ProjectStatisticType.CompletionCount);
|
|
||||||
|
|
||||||
return dto;
|
return dto;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,10 +8,13 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Blog\**" />
|
<Compile Remove="Blog\**" />
|
||||||
<Compile Remove="Config\**" />
|
<Compile Remove="Config\**" />
|
||||||
|
<Compile Remove="File\**" />
|
||||||
<EmbeddedResource Remove="Blog\**" />
|
<EmbeddedResource Remove="Blog\**" />
|
||||||
<EmbeddedResource Remove="Config\**" />
|
<EmbeddedResource Remove="Config\**" />
|
||||||
|
<EmbeddedResource Remove="File\**" />
|
||||||
<None Remove="Blog\**" />
|
<None Remove="Blog\**" />
|
||||||
<None Remove="Config\**" />
|
<None Remove="Config\**" />
|
||||||
|
<None Remove="File\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Remove="applicationsettings.json" />
|
<None Remove="applicationsettings.json" />
|
||||||
@ -25,7 +28,6 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Folder Include="Auth\Dtos\" />
|
<Folder Include="Auth\Dtos\" />
|
||||||
<Folder Include="Certificate\Dtos\" />
|
<Folder Include="Certificate\Dtos\" />
|
||||||
<Folder Include="File\Dtos\" />
|
|
||||||
<Folder Include="Logging\Dtos\" />
|
<Folder Include="Logging\Dtos\" />
|
||||||
<Folder Include="Menu\Dtos\" />
|
<Folder Include="Menu\Dtos\" />
|
||||||
<Folder Include="Organization\Dtos\" />
|
<Folder Include="Organization\Dtos\" />
|
||||||
|
|||||||
79
YD_Event.Core/Utils/AmountHelper.cs
Normal file
79
YD_Event.Core/Utils/AmountHelper.cs
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
namespace YD_Event.Core.Utils;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 金额处理工具类
|
||||||
|
/// 统一处理货币单位转换、精度控制和四舍五入规则
|
||||||
|
/// </summary>
|
||||||
|
public static class AmountHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 金额精度小数位数
|
||||||
|
/// </summary>
|
||||||
|
private const int DecimalPlaces = 2;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将元转换为分
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额(元)</param>
|
||||||
|
/// <returns>金额(分)</returns>
|
||||||
|
public static int YuanToFen(decimal amount)
|
||||||
|
{
|
||||||
|
// 使用四舍五入到最近的整数,使用银行家舍入法(四舍六入五成双)
|
||||||
|
// 这里先确保精度,然后乘以100,再四舍五入
|
||||||
|
var roundedAmount = Math.Round(amount, DecimalPlaces, MidpointRounding.AwayFromZero);
|
||||||
|
return (int)Math.Round(roundedAmount * 100, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将分转换为元
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额(分)</param>
|
||||||
|
/// <returns>金额(元)</returns>
|
||||||
|
public static decimal FenToYuan(int amount)
|
||||||
|
{
|
||||||
|
return amount / 100m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将分转换为元(可为空)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额(分)</param>
|
||||||
|
/// <returns>金额(元)</returns>
|
||||||
|
public static decimal? FenToYuan(int? amount)
|
||||||
|
{
|
||||||
|
if (!amount.HasValue) return null;
|
||||||
|
return amount.Value / 100m;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 四舍五入金额到指定小数位
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额</param>
|
||||||
|
/// <param name="decimalPlaces">小数位数,默认2位</param>
|
||||||
|
/// <returns>四舍五入后的金额</returns>
|
||||||
|
public static decimal Round(decimal amount, int decimalPlaces = DecimalPlaces)
|
||||||
|
{
|
||||||
|
return Math.Round(amount, decimalPlaces, MidpointRounding.AwayFromZero);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证金额格式是否正确(最多保留2位小数)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额</param>
|
||||||
|
/// <returns>是否有效</returns>
|
||||||
|
public static bool IsValidAmount(decimal amount)
|
||||||
|
{
|
||||||
|
var rounded = Math.Round(amount, DecimalPlaces, MidpointRounding.AwayFromZero);
|
||||||
|
return rounded == amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 验证金额是否大于0且格式正确
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="amount">金额</param>
|
||||||
|
/// <returns>是否有效</returns>
|
||||||
|
public static bool IsValidPositiveAmount(decimal amount)
|
||||||
|
{
|
||||||
|
return amount > 0 && IsValidAmount(amount);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5811,6 +5811,60 @@
|
|||||||
子选项
|
子选项
|
||||||
</summary>
|
</summary>
|
||||||
</member>
|
</member>
|
||||||
|
<member name="T:YD_Event.Core.Utils.AmountHelper">
|
||||||
|
<summary>
|
||||||
|
金额处理工具类
|
||||||
|
统一处理货币单位转换、精度控制和四舍五入规则
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="F:YD_Event.Core.Utils.AmountHelper.DecimalPlaces">
|
||||||
|
<summary>
|
||||||
|
金额精度小数位数
|
||||||
|
</summary>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.YuanToFen(System.Decimal)">
|
||||||
|
<summary>
|
||||||
|
将元转换为分
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额(元)</param>
|
||||||
|
<returns>金额(分)</returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.FenToYuan(System.Int32)">
|
||||||
|
<summary>
|
||||||
|
将分转换为元
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额(分)</param>
|
||||||
|
<returns>金额(元)</returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.FenToYuan(System.Nullable{System.Int32})">
|
||||||
|
<summary>
|
||||||
|
将分转换为元(可为空)
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额(分)</param>
|
||||||
|
<returns>金额(元)</returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.Round(System.Decimal,System.Int32)">
|
||||||
|
<summary>
|
||||||
|
四舍五入金额到指定小数位
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额</param>
|
||||||
|
<param name="decimalPlaces">小数位数,默认2位</param>
|
||||||
|
<returns>四舍五入后的金额</returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.IsValidAmount(System.Decimal)">
|
||||||
|
<summary>
|
||||||
|
验证金额格式是否正确(最多保留2位小数)
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额</param>
|
||||||
|
<returns>是否有效</returns>
|
||||||
|
</member>
|
||||||
|
<member name="M:YD_Event.Core.Utils.AmountHelper.IsValidPositiveAmount(System.Decimal)">
|
||||||
|
<summary>
|
||||||
|
验证金额是否大于0且格式正确
|
||||||
|
</summary>
|
||||||
|
<param name="amount">金额</param>
|
||||||
|
<returns>是否有效</returns>
|
||||||
|
</member>
|
||||||
<member name="F:Easy.Core.EnumExtension.EnumDic">
|
<member name="F:Easy.Core.EnumExtension.EnumDic">
|
||||||
<summary>
|
<summary>
|
||||||
缓存
|
缓存
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user