feat: 租户审核领单与强制接管

This commit is contained in:
2025-12-15 10:40:50 +08:00
parent f54d4cf405
commit 2339775fcb
21 changed files with 7519 additions and 2 deletions

View File

@@ -81,6 +81,10 @@ public sealed class TakeoutAppDbContext(
/// </summary>
public DbSet<TenantAuditLog> TenantAuditLogs => Set<TenantAuditLog>();
/// <summary>
/// 租户审核领取记录。
/// </summary>
public DbSet<TenantReviewClaim> TenantReviewClaims => Set<TenantReviewClaim>();
/// <summary>
/// 商户实体。
/// </summary>
public DbSet<Merchant> Merchants => Set<Merchant>();
@@ -374,6 +378,7 @@ public sealed class TakeoutAppDbContext(
ConfigureTenantAnnouncementRead(modelBuilder.Entity<TenantAnnouncementRead>());
ConfigureTenantVerificationProfile(modelBuilder.Entity<TenantVerificationProfile>());
ConfigureTenantAuditLog(modelBuilder.Entity<TenantAuditLog>());
ConfigureTenantReviewClaim(modelBuilder.Entity<TenantReviewClaim>());
ConfigureMerchantDocument(modelBuilder.Entity<MerchantDocument>());
ConfigureMerchantContract(modelBuilder.Entity<MerchantContract>());
ConfigureMerchantStaff(modelBuilder.Entity<MerchantStaff>());
@@ -491,6 +496,20 @@ public sealed class TakeoutAppDbContext(
builder.HasIndex(x => x.TenantId);
}
private static void ConfigureTenantReviewClaim(EntityTypeBuilder<TenantReviewClaim> builder)
{
builder.ToTable("tenant_review_claims");
builder.HasKey(x => x.Id);
builder.Property(x => x.TenantId).IsRequired();
builder.Property(x => x.ClaimedBy).IsRequired();
builder.Property(x => x.ClaimedByName).HasMaxLength(64).IsRequired();
builder.Property(x => x.ClaimedAt).IsRequired();
builder.Property(x => x.ReleasedAt);
builder.HasIndex(x => x.TenantId);
builder.HasIndex(x => x.ClaimedBy);
builder.HasIndex(x => x.TenantId).IsUnique().HasFilter("\"ReleasedAt\" IS NULL AND \"DeletedAt\" IS NULL");
}
private static void ConfigureTenantSubscriptionHistory(EntityTypeBuilder<TenantSubscriptionHistory> builder)
{
builder.ToTable("tenant_subscription_histories");

View File

@@ -1,4 +1,5 @@
using Microsoft.EntityFrameworkCore;
using Npgsql;
using TakeoutSaaS.Domain.Tenants.Entities;
using TakeoutSaaS.Domain.Tenants.Enums;
using TakeoutSaaS.Domain.Tenants.Repositories;
@@ -214,6 +215,54 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRep
context.Entry(existing).CurrentValues.SetValues(profile);
}
/// <inheritdoc />
public Task<TenantReviewClaim?> GetActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default)
{
return context.TenantReviewClaims
.AsNoTracking()
.Where(x => x.TenantId == tenantId && x.ReleasedAt == null)
.OrderByDescending(x => x.ClaimedAt)
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public Task<TenantReviewClaim?> FindActiveReviewClaimAsync(long tenantId, CancellationToken cancellationToken = default)
{
return context.TenantReviewClaims
.Where(x => x.TenantId == tenantId && x.ReleasedAt == null)
.OrderByDescending(x => x.ClaimedAt)
.FirstOrDefaultAsync(cancellationToken);
}
/// <inheritdoc />
public async Task<bool> TryAddReviewClaimAsync(
TenantReviewClaim claim,
TenantAuditLog auditLog,
CancellationToken cancellationToken = default)
{
try
{
await context.TenantReviewClaims.AddAsync(claim, cancellationToken);
await context.TenantAuditLogs.AddAsync(auditLog, cancellationToken);
await context.SaveChangesAsync(cancellationToken);
return true;
}
catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
{
context.Entry(claim).State = EntityState.Detached;
context.Entry(auditLog).State = EntityState.Detached;
return false;
}
}
/// <inheritdoc />
public Task UpdateReviewClaimAsync(TenantReviewClaim claim, CancellationToken cancellationToken = default)
{
context.TenantReviewClaims.Update(claim);
return Task.CompletedTask;
}
/// <inheritdoc />
public Task<TenantSubscription?> GetActiveSubscriptionAsync(long tenantId, CancellationToken cancellationToken = default)
{