Implement referral, points, coupons, commission, member levels, and order integration with migration SQL and background jobs. Co-authored-by: Cursor <cursoragent@cursor.com>
133 lines
4.3 KiB
C#
133 lines
4.3 KiB
C#
using Fake.DependencyInjection;
|
|
using Fake.Application;
|
|
using Oms.Domain.CustomersAggregate.Consts;
|
|
using Oms.Domain.CustomersAggregate.Entities;
|
|
using Oms.Domain.CustomersAggregate.Repositories;
|
|
|
|
namespace Oms.Application.Shared;
|
|
|
|
/// <summary>
|
|
/// 佣金账户变动与流水。
|
|
/// </summary>
|
|
public class CommissionBalanceMutator : IScopedDependency
|
|
{
|
|
private readonly ICustomerRepository _customerRepository;
|
|
private readonly ICustomerAccountFlowRepository _flowRepository;
|
|
private readonly CustomerBalanceMutator _balanceMutator;
|
|
|
|
public CommissionBalanceMutator(
|
|
ICustomerRepository customerRepository,
|
|
ICustomerAccountFlowRepository flowRepository,
|
|
CustomerBalanceMutator balanceMutator)
|
|
{
|
|
_customerRepository = customerRepository;
|
|
_flowRepository = flowRepository;
|
|
_balanceMutator = balanceMutator;
|
|
}
|
|
|
|
public async Task CreditCommissionAsync(
|
|
Customer customer,
|
|
decimal amount,
|
|
int detailId,
|
|
string? orderNo,
|
|
string? remark,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await ApplyCommissionAsync(customer, amount, CustomerAccountFlowBizTypes.ReferralCommission, detailId, orderNo, remark, cancellationToken);
|
|
}
|
|
|
|
public async Task DebitForTransferToBalanceAsync(
|
|
Customer customer,
|
|
decimal amount,
|
|
int withdrawRequestId,
|
|
string serialNumber,
|
|
string? remark,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await ApplyCommissionAsync(customer, -amount, CustomerAccountFlowBizTypes.CommissionToBalance, withdrawRequestId, serialNumber, remark, cancellationToken);
|
|
await _balanceMutator.CreditRechargeAsync(customer, amount, withdrawRequestId, serialNumber, remark ?? "佣金转余额", cancellationToken);
|
|
}
|
|
|
|
public async Task DebitForWithdrawAsync(
|
|
Customer customer,
|
|
decimal amount,
|
|
int withdrawRequestId,
|
|
string serialNumber,
|
|
string? remark,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await ApplyCommissionAsync(customer, -amount, CustomerAccountFlowBizTypes.CommissionWithdraw, withdrawRequestId, serialNumber, remark, cancellationToken);
|
|
}
|
|
|
|
public async Task AdjustCommissionAsync(
|
|
Customer customer,
|
|
decimal signedAmount,
|
|
int? detailId,
|
|
string? remark,
|
|
CancellationToken cancellationToken = default)
|
|
{
|
|
await ApplyCommissionAsync(
|
|
customer,
|
|
signedAmount,
|
|
CustomerAccountFlowBizTypes.MarketingAdjust,
|
|
detailId,
|
|
null,
|
|
remark,
|
|
cancellationToken);
|
|
}
|
|
|
|
private async Task ApplyCommissionAsync(
|
|
Customer customer,
|
|
decimal signedAmount,
|
|
string bizType,
|
|
int? bizId,
|
|
string? bizNo,
|
|
string? remark,
|
|
CancellationToken cancellationToken)
|
|
{
|
|
if (signedAmount == 0)
|
|
{
|
|
throw new BusinessException("佣金变动金额不能为 0");
|
|
}
|
|
|
|
var before = customer.CommissionBalance;
|
|
var after = before + signedAmount;
|
|
if (after < 0)
|
|
{
|
|
throw new BusinessException("佣金余额不足");
|
|
}
|
|
|
|
customer.CommissionBalance = after;
|
|
await _customerRepository.UpdateAsync(customer, cancellationToken);
|
|
|
|
var flow = new CustomerAccountFlow
|
|
{
|
|
CustomerId = customer.Id,
|
|
SerialNumber = await GenerateSerialNumberAsync(cancellationToken),
|
|
BizType = bizType,
|
|
BizId = bizId,
|
|
BizNo = bizNo,
|
|
Amount = signedAmount,
|
|
BalanceBefore = customer.Balance,
|
|
BalanceAfter = customer.Balance,
|
|
Currency = "CNY",
|
|
Remark = remark
|
|
};
|
|
await _flowRepository.InsertAsync(flow, cancellationToken);
|
|
}
|
|
|
|
private async Task<string> GenerateSerialNumberAsync(CancellationToken cancellationToken)
|
|
{
|
|
for (var i = 0; i < 5; i++)
|
|
{
|
|
var serialNumber = $"CMF{DateTime.Now:yyyyMMddHHmmss}{Random.Shared.Next(100000, 999999)}";
|
|
if (!await _flowRepository.ExistsSerialNumberAsync(serialNumber, cancellationToken))
|
|
{
|
|
return serialNumber;
|
|
}
|
|
}
|
|
|
|
throw new BusinessException("佣金流水号生成失败");
|
|
}
|
|
}
|