feat: add process expired points command

This commit is contained in:
Sam 2026-02-09 19:00:07 +08:00
parent 4745c91915
commit ce6b80e334
4 changed files with 63 additions and 1 deletions

View File

@ -0,0 +1,5 @@
using MediatR;
namespace Fengling.Member.Application.Commands.Points;
public record ProcessExpiredPointsCommand(int BatchSize = 1000) : IRequest<int>;

View File

@ -0,0 +1,49 @@
using MediatR;
using Fengling.Member.Domain.Aggregates.PointsModel;
using Fengling.Member.Infrastructure;
using Microsoft.EntityFrameworkCore;
namespace Fengling.Member.Application.Commands.Points;
public class ProcessExpiredPointsCommandHandler : IRequestHandler<ProcessExpiredPointsCommand, int>
{
private readonly ApplicationDbContext _context;
public ProcessExpiredPointsCommandHandler(ApplicationDbContext context)
{
_context = context;
}
public async Task<int> Handle(ProcessExpiredPointsCommand request, CancellationToken cancellationToken)
{
var now = DateTime.UtcNow;
var expiredTransactions = await _context.PointsTransactions
.Where(x => x.ExpireAt <= now
&& x.ExpireAt > DateTime.MinValue
&& x.Points > 0)
.OrderBy(x => x.ExpireAt)
.Take(request.BatchSize)
.ToListAsync(cancellationToken);
foreach (var transaction in expiredTransactions)
{
var deduction = PointsTransaction.Create(
transaction.PointsAccountId,
transaction.MemberId,
-transaction.Points,
"Expired",
$"来源交易: {transaction.Id}",
PointsTransactionType.Expired,
$"积分过期扣除 - 来源交易: {transaction.Id}");
deduction.SetExpireAt(transaction.ExpireAt);
_context.PointsTransactions.Add(deduction);
}
var affected = await _context.SaveChangesAsync(cancellationToken);
return affected / 2;
}
}

View File

@ -10,11 +10,17 @@ public class PointsTransaction : Entity<long>
public PointsTransactionType TransactionTypeCategory { get; private set; }
public string? Remark { get; private set; }
public DateTime CreatedAt { get; private set; } = DateTime.UtcNow;
public DateTime ExpireAt { get; private set; } = DateTime.MinValue;
private PointsTransaction()
{
}
public void SetExpireAt(DateTime expireAt)
{
ExpireAt = expireAt;
}
public static PointsTransaction Create(
long pointsAccountId,
long memberId,
@ -41,5 +47,6 @@ public class PointsTransaction : Entity<long>
public enum PointsTransactionType
{
Earn = 1,
Deduct = 2
Deduct = 2,
Expired = 3
}

View File

@ -14,6 +14,7 @@ public partial class ApplicationDbContext(DbContextOptions<ApplicationDbContext>
public DbSet<Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount> PointsAccounts => Set<Fengling.Member.Domain.Aggregates.PointsModel.PointsAccount>();
public DbSet<Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule> PointsRules => Set<Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRule>();
public DbSet<Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition> PointsRuleConditions => Set<Fengling.Member.Domain.Aggregates.PointsRuleModel.PointsRuleCondition>();
public DbSet<Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction> PointsTransactions => Set<Fengling.Member.Domain.Aggregates.PointsModel.PointsTransaction>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{