admin/src/Oms.Application/Shared/CommissionBalanceMutator.cs
zzh 89233bc3da feat: add customer marketing module (M1-M6 backend)
Implement referral, points, coupons, commission, member levels, and order integration with migration SQL and background jobs.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-23 16:42:08 +08:00

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("佣金流水号生成失败");
}
}