refactor: 日志库拆分与清理用户审计
This commit is contained in:
@@ -11,6 +11,7 @@ using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Domain.Tenants.Services;
|
||||
using TakeoutSaaS.Infrastructure.App.Options;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Services;
|
||||
@@ -34,6 +35,7 @@ public static class AppServiceCollectionExtensions
|
||||
{
|
||||
services.AddDatabaseInfrastructure(configuration);
|
||||
services.AddPostgresDbContext<TakeoutAppDbContext>(DatabaseConstants.AppDataSource);
|
||||
services.AddPostgresDbContext<TakeoutLogsDbContext>(DatabaseConstants.LogsDataSource);
|
||||
|
||||
services.AddScoped<IMerchantRepository, EfMerchantRepository>();
|
||||
services.AddScoped<IMerchantCategoryRepository, EfMerchantCategoryRepository>();
|
||||
|
||||
@@ -87,18 +87,10 @@ public sealed class TakeoutAppDbContext(
|
||||
/// </summary>
|
||||
public DbSet<TenantVerificationProfile> TenantVerificationProfiles => Set<TenantVerificationProfile>();
|
||||
/// <summary>
|
||||
/// 租户审计日志。
|
||||
/// </summary>
|
||||
public DbSet<TenantAuditLog> TenantAuditLogs => Set<TenantAuditLog>();
|
||||
/// <summary>
|
||||
/// 租户审核领取记录。
|
||||
/// </summary>
|
||||
public DbSet<TenantReviewClaim> TenantReviewClaims => Set<TenantReviewClaim>();
|
||||
/// <summary>
|
||||
/// 运营操作日志。
|
||||
/// </summary>
|
||||
public DbSet<OperationLog> OperationLogs => Set<OperationLog>();
|
||||
/// <summary>
|
||||
/// 配额包定义。
|
||||
/// </summary>
|
||||
public DbSet<QuotaPackage> QuotaPackages => Set<QuotaPackage>();
|
||||
@@ -123,10 +115,6 @@ public sealed class TakeoutAppDbContext(
|
||||
/// </summary>
|
||||
public DbSet<MerchantStaff> MerchantStaff => Set<MerchantStaff>();
|
||||
/// <summary>
|
||||
/// 商户审计日志。
|
||||
/// </summary>
|
||||
public DbSet<MerchantAuditLog> MerchantAuditLogs => Set<MerchantAuditLog>();
|
||||
/// <summary>
|
||||
/// 商户分类。
|
||||
/// </summary>
|
||||
public DbSet<MerchantCategory> MerchantCategories => Set<MerchantCategory>();
|
||||
@@ -307,10 +295,6 @@ public sealed class TakeoutAppDbContext(
|
||||
/// </summary>
|
||||
public DbSet<MemberPointLedger> MemberPointLedgers => Set<MemberPointLedger>();
|
||||
/// <summary>
|
||||
/// 成长值日志。
|
||||
/// </summary>
|
||||
public DbSet<MemberGrowthLog> MemberGrowthLogs => Set<MemberGrowthLog>();
|
||||
/// <summary>
|
||||
/// 会话记录。
|
||||
/// </summary>
|
||||
public DbSet<ChatSession> ChatSessions => Set<ChatSession>();
|
||||
@@ -401,15 +385,12 @@ public sealed class TakeoutAppDbContext(
|
||||
ConfigureTenantAnnouncement(modelBuilder.Entity<TenantAnnouncement>());
|
||||
ConfigureTenantAnnouncementRead(modelBuilder.Entity<TenantAnnouncementRead>());
|
||||
ConfigureTenantVerificationProfile(modelBuilder.Entity<TenantVerificationProfile>());
|
||||
ConfigureTenantAuditLog(modelBuilder.Entity<TenantAuditLog>());
|
||||
ConfigureTenantReviewClaim(modelBuilder.Entity<TenantReviewClaim>());
|
||||
ConfigureOperationLog(modelBuilder.Entity<OperationLog>());
|
||||
ConfigureQuotaPackage(modelBuilder.Entity<QuotaPackage>());
|
||||
ConfigureTenantQuotaPackagePurchase(modelBuilder.Entity<TenantQuotaPackagePurchase>());
|
||||
ConfigureMerchantDocument(modelBuilder.Entity<MerchantDocument>());
|
||||
ConfigureMerchantContract(modelBuilder.Entity<MerchantContract>());
|
||||
ConfigureMerchantStaff(modelBuilder.Entity<MerchantStaff>());
|
||||
ConfigureMerchantAuditLog(modelBuilder.Entity<MerchantAuditLog>());
|
||||
ConfigureMerchantCategory(modelBuilder.Entity<MerchantCategory>());
|
||||
ConfigureStoreBusinessHour(modelBuilder.Entity<StoreBusinessHour>());
|
||||
ConfigureStoreHoliday(modelBuilder.Entity<StoreHoliday>());
|
||||
@@ -454,7 +435,6 @@ public sealed class TakeoutAppDbContext(
|
||||
ConfigureMemberProfile(modelBuilder.Entity<MemberProfile>());
|
||||
ConfigureMemberTier(modelBuilder.Entity<MemberTier>());
|
||||
ConfigureMemberPointLedger(modelBuilder.Entity<MemberPointLedger>());
|
||||
ConfigureMemberGrowthLog(modelBuilder.Entity<MemberGrowthLog>());
|
||||
ConfigureChatSession(modelBuilder.Entity<ChatSession>());
|
||||
ConfigureChatMessage(modelBuilder.Entity<ChatMessage>());
|
||||
ConfigureSupportTicket(modelBuilder.Entity<SupportTicket>());
|
||||
@@ -512,17 +492,6 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasIndex(x => x.TenantId).IsUnique();
|
||||
}
|
||||
|
||||
private static void ConfigureTenantAuditLog(EntityTypeBuilder<TenantAuditLog> builder)
|
||||
{
|
||||
builder.ToTable("tenant_audit_logs");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.TenantId).IsRequired();
|
||||
builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
|
||||
builder.Property(x => x.Description).HasMaxLength(1024);
|
||||
builder.Property(x => x.OperatorName).HasMaxLength(64);
|
||||
builder.HasIndex(x => x.TenantId);
|
||||
}
|
||||
|
||||
private static void ConfigureTenantReviewClaim(EntityTypeBuilder<TenantReviewClaim> builder)
|
||||
{
|
||||
builder.ToTable("tenant_review_claims");
|
||||
@@ -537,21 +506,6 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasIndex(x => x.TenantId).IsUnique().HasFilter("\"ReleasedAt\" IS NULL AND \"DeletedAt\" IS NULL");
|
||||
}
|
||||
|
||||
private static void ConfigureOperationLog(EntityTypeBuilder<OperationLog> builder)
|
||||
{
|
||||
builder.ToTable("operation_logs");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.OperationType).HasMaxLength(64).IsRequired();
|
||||
builder.Property(x => x.TargetType).HasMaxLength(64).IsRequired();
|
||||
builder.Property(x => x.TargetIds).HasColumnType("text");
|
||||
builder.Property(x => x.OperatorId).HasMaxLength(64);
|
||||
builder.Property(x => x.OperatorName).HasMaxLength(128);
|
||||
builder.Property(x => x.Parameters).HasColumnType("text");
|
||||
builder.Property(x => x.Result).HasColumnType("text");
|
||||
builder.Property(x => x.Success).IsRequired();
|
||||
builder.HasIndex(x => new { x.OperationType, x.CreatedAt });
|
||||
builder.HasIndex(x => x.CreatedAt);
|
||||
}
|
||||
|
||||
private static void ConfigureTenantSubscriptionHistory(EntityTypeBuilder<TenantSubscriptionHistory> builder)
|
||||
{
|
||||
@@ -885,17 +839,6 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasIndex(x => new { x.TenantId, x.MerchantId, x.Phone });
|
||||
}
|
||||
|
||||
private static void ConfigureMerchantAuditLog(EntityTypeBuilder<MerchantAuditLog> builder)
|
||||
{
|
||||
builder.ToTable("merchant_audit_logs");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.MerchantId).IsRequired();
|
||||
builder.Property(x => x.Title).HasMaxLength(128).IsRequired();
|
||||
builder.Property(x => x.Description).HasMaxLength(1024);
|
||||
builder.Property(x => x.OperatorName).HasMaxLength(64);
|
||||
builder.HasIndex(x => new { x.TenantId, x.MerchantId });
|
||||
}
|
||||
|
||||
private static void ConfigureMerchantCategory(EntityTypeBuilder<MerchantCategory> builder)
|
||||
{
|
||||
builder.ToTable("merchant_categories");
|
||||
@@ -1297,15 +1240,6 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.HasIndex(x => new { x.TenantId, x.MemberId, x.OccurredAt });
|
||||
}
|
||||
|
||||
private static void ConfigureMemberGrowthLog(EntityTypeBuilder<MemberGrowthLog> builder)
|
||||
{
|
||||
builder.ToTable("member_growth_logs");
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.MemberId).IsRequired();
|
||||
builder.Property(x => x.Notes).HasMaxLength(256);
|
||||
builder.HasIndex(x => new { x.TenantId, x.MemberId, x.OccurredAt });
|
||||
}
|
||||
|
||||
private static void ConfigureChatSession(EntityTypeBuilder<ChatSession> builder)
|
||||
{
|
||||
builder.ToTable("chat_sessions");
|
||||
|
||||
@@ -3,6 +3,7 @@ using TakeoutSaaS.Domain.Merchants.Entities;
|
||||
using TakeoutSaaS.Domain.Merchants.Enums;
|
||||
using TakeoutSaaS.Domain.Merchants.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
|
||||
@@ -12,7 +13,7 @@ namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
/// <remarks>
|
||||
/// 初始化仓储。
|
||||
/// </remarks>
|
||||
public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchantRepository
|
||||
public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLogsDbContext logsContext) : IMerchantRepository
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<Merchant?> FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
@@ -151,9 +152,13 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchan
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
// 1. 保存业务库变更
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -196,13 +201,13 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context) : IMerchan
|
||||
/// <inheritdoc />
|
||||
public Task AddAuditLogAsync(MerchantAuditLog log, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.MerchantAuditLogs.AddAsync(log, cancellationToken).AsTask();
|
||||
return logsContext.MerchantAuditLogs.AddAsync(log, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<MerchantAuditLog>> GetAuditLogsAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.MerchantAuditLogs
|
||||
return await logsContext.MerchantAuditLogs
|
||||
.AsNoTracking()
|
||||
.Where(x => x.TenantId == tenantId && x.MerchantId == merchantId)
|
||||
.OrderByDescending(x => x.CreatedAt)
|
||||
|
||||
@@ -3,13 +3,14 @@ using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 订阅管理仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : ISubscriptionRepository
|
||||
public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext, TakeoutLogsDbContext logsContext) : ISubscriptionRepository
|
||||
{
|
||||
#region 订阅查询
|
||||
|
||||
@@ -393,7 +394,7 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
|
||||
/// <inheritdoc />
|
||||
public Task AddOperationLogAsync(OperationLog log, CancellationToken cancellationToken = default)
|
||||
{
|
||||
dbContext.Set<OperationLog>().Add(log);
|
||||
logsContext.OperationLogs.Add(log);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
@@ -402,6 +403,10 @@ public sealed class EfSubscriptionRepository(TakeoutAppDbContext dbContext) : IS
|
||||
/// <inheritdoc />
|
||||
public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
// 1. 保存业务库变更
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ using TakeoutSaaS.Domain.Tenants.Entities;
|
||||
using TakeoutSaaS.Domain.Tenants.Enums;
|
||||
using TakeoutSaaS.Domain.Tenants.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.App.Persistence;
|
||||
using TakeoutSaaS.Infrastructure.Logs.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.App.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 租户聚合的 EF Core 仓储实现。
|
||||
/// </summary>
|
||||
public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRepository
|
||||
public sealed class EfTenantRepository(TakeoutAppDbContext context, TakeoutLogsDbContext logsContext) : ITenantRepository
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<Tenant?> FindByIdAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||
@@ -277,16 +278,21 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRep
|
||||
{
|
||||
try
|
||||
{
|
||||
// 1. 写入领取记录
|
||||
await context.TenantReviewClaims.AddAsync(claim, cancellationToken);
|
||||
await context.TenantAuditLogs.AddAsync(auditLog, cancellationToken);
|
||||
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 写入审计日志
|
||||
await logsContext.TenantAuditLogs.AddAsync(auditLog, cancellationToken);
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
return true;
|
||||
}
|
||||
catch (DbUpdateException ex) when (ex.InnerException is PostgresException pg && pg.SqlState == PostgresErrorCodes.UniqueViolation)
|
||||
{
|
||||
// 1. 释放实体跟踪避免重复写入
|
||||
context.Entry(claim).State = EntityState.Detached;
|
||||
context.Entry(auditLog).State = EntityState.Detached;
|
||||
|
||||
// 2. (空行后) 返回抢占失败
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -372,13 +378,13 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRep
|
||||
/// <inheritdoc />
|
||||
public Task AddAuditLogAsync(TenantAuditLog log, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.TenantAuditLogs.AddAsync(log, cancellationToken).AsTask();
|
||||
return logsContext.TenantAuditLogs.AddAsync(log, cancellationToken).AsTask();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<TenantAuditLog>> GetAuditLogsAsync(long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return await context.TenantAuditLogs
|
||||
return await logsContext.TenantAuditLogs
|
||||
.IgnoreQueryFilters()
|
||||
.AsNoTracking()
|
||||
.Where(x => x.DeletedAt == null && x.TenantId == tenantId)
|
||||
@@ -387,8 +393,12 @@ public sealed class EfTenantRepository(TakeoutAppDbContext context) : ITenantRep
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
public async Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.SaveChangesAsync(cancellationToken);
|
||||
// 1. 保存业务库变更
|
||||
await context.SaveChangesAsync(cancellationToken);
|
||||
|
||||
// 2. (空行后) 保存日志库变更
|
||||
await logsContext.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user