Files
tiku-backend.net/Tiku.Infrastructure/Commerce/Reconciliation/CommerceAdminService.Reconciliation.cs
xiong c497a3ca8d
Some checks failed
ci / release-gate (push) Has been cancelled
清理代码
2026-08-03 12:31:39 +08:00

120 lines
5.2 KiB
C#

using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using Tiku.Application.Commerce;
using Tiku.Domain.Commerce;
namespace Tiku.Infrastructure.Commerce;
internal sealed partial class CommerceAdminService
{
public async Task<TenantReconciliationBatchList> GetReconciliationBatchesAsync(
CommerceAdminActor actor,
CommerceAdminQuery query,
CancellationToken cancellationToken = default)
{
await AssertAdminAsync(actor, cancellationToken);
var batches = dbContext.CommerceReconciliationBatches.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId);
if (!string.IsNullOrWhiteSpace(query.Provider))
{
var provider = NormalizeProvider(query.Provider);
batches = batches.Where(item => item.Provider == provider);
}
if (!string.IsNullOrWhiteSpace(query.Status))
batches = batches.Where(item => item.Status == ParseReconciliationBatchStatus(query.Status));
var items = await batches.OrderByDescending(item => item.CreatedAt)
.Take(Math.Clamp(query.Limit ?? 50, 1, 200))
.ToArrayAsync(cancellationToken);
return new TenantReconciliationBatchList(items);
}
public async Task<CommerceReconciliationBatch> CreateReconciliationBatchAsync(
CommerceAdminActor actor,
CreateReconciliationBatchCommand command,
CancellationToken cancellationToken = default)
{
await AssertAdminAsync(actor, cancellationToken);
var batch = new CommerceReconciliationBatch
{
TenantId = actor.TenantId,
CreatedBy = actor.UserId,
Provider = NormalizeProvider(command.Provider),
BillDate = command.BillDate,
BillType = command.BillType,
Source = command.Source,
SourceName = command.SourceName?.Trim(),
SourceHash = command.SourceHash.Trim(),
Status = ReconciliationBatchStatus.Pending,
Metadata = JsonObjectOrDefault(command.Metadata)
};
dbContext.CommerceReconciliationBatches.Add(batch);
await AddAuditAsync(actor, "commerce.reconciliation_batch.created", "commerce_reconciliation_batches", batch.Id,
new { batch.Provider, batch.BillDate }, cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken);
return batch;
}
public async Task<TenantReconciliationIssueList> GetReconciliationIssuesAsync(
CommerceAdminActor actor,
CommerceAdminQuery query,
CancellationToken cancellationToken = default)
{
await AssertAdminAsync(actor, cancellationToken);
var issues = dbContext.CommerceReconciliationIssues.AsNoTracking()
.Where(item => item.TenantId == actor.TenantId);
if (!string.IsNullOrWhiteSpace(query.Provider))
{
var provider = NormalizeProvider(query.Provider);
issues = issues.Where(item => item.Provider == provider);
}
if (!string.IsNullOrWhiteSpace(query.Status))
issues = issues.Where(item => item.Status == ParseReconciliationIssueStatus(query.Status));
var items = await issues.OrderByDescending(item => item.CreatedAt)
.Take(Math.Clamp(query.Limit ?? 50, 1, 200))
.ToArrayAsync(cancellationToken);
return new TenantReconciliationIssueList(items);
}
public async Task<CommerceReconciliationIssue> UpdateReconciliationIssueAsync(
CommerceAdminActor actor,
UpdateReconciliationIssueCommand command,
CancellationToken cancellationToken = default)
{
await AssertAdminAsync(actor, cancellationToken);
var issue = await dbContext.CommerceReconciliationIssues.SingleOrDefaultAsync(
item => item.TenantId == actor.TenantId && item.Id == command.IssueId,
cancellationToken) ??
throw new CommerceException("Reconciliation issue was not found.",
"reconciliation_issue_not_found");
var fromStatus = issue.Status;
issue.Status = command.Status;
issue.ResolutionType = command.ResolutionType;
issue.ResolutionNote = command.Note?.Trim();
issue.AssignedTo = command.AssignedTo ?? issue.AssignedTo;
if (command.Status is ReconciliationIssueStatus.Resolved or ReconciliationIssueStatus.Ignored)
{
issue.ResolvedBy = actor.UserId;
issue.ResolvedAt = DateTimeOffset.UtcNow;
}
dbContext.CommerceReconciliationIssueEvents.Add(new CommerceReconciliationIssueEvent
{
TenantId = actor.TenantId,
IssueId = issue.Id,
FromStatus = fromStatus,
ToStatus = command.Status,
EventType = "status_changed",
ActorUserId = actor.UserId,
Note = command.Note,
Details = JsonSerializer.SerializeToElement(new { command.ResolutionType, command.AssignedTo })
});
await AddAuditAsync(actor, "commerce.reconciliation_issue.status_changed", "commerce_reconciliation_issues",
issue.Id, new { issue.IssueNo, From = fromStatus, To = command.Status }, cancellationToken);
await dbContext.SaveChangesAsync(cancellationToken);
return issue;
}
}