feat: 提交后端其余改动
This commit is contained in:
@@ -39,6 +39,7 @@ public static class DictionaryServiceCollectionExtensions
|
||||
services.AddScoped<IDictionaryGroupRepository, DictionaryGroupRepository>();
|
||||
services.AddScoped<IDictionaryItemRepository, DictionaryItemRepository>();
|
||||
services.AddScoped<ITenantDictionaryOverrideRepository, TenantDictionaryOverrideRepository>();
|
||||
services.AddScoped<IDictionaryLabelOverrideRepository, DictionaryLabelOverrideRepository>();
|
||||
services.AddScoped<IDictionaryImportLogRepository, DictionaryImportLogRepository>();
|
||||
services.AddScoped<ICacheInvalidationLogRepository, CacheInvalidationLogRepository>();
|
||||
services.AddScoped<ISystemParameterRepository, EfSystemParameterRepository>();
|
||||
|
||||
@@ -7,6 +7,7 @@ using TakeoutSaaS.Infrastructure.Common.Persistence;
|
||||
using TakeoutSaaS.Shared.Abstractions.Ids;
|
||||
using TakeoutSaaS.Shared.Abstractions.Security;
|
||||
using TakeoutSaaS.Shared.Abstractions.Tenancy;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Dictionary.Persistence;
|
||||
|
||||
@@ -17,8 +18,9 @@ public sealed class DictionaryDbContext(
|
||||
DbContextOptions<DictionaryDbContext> options,
|
||||
ITenantProvider tenantProvider,
|
||||
ICurrentUserAccessor? currentUserAccessor = null,
|
||||
IIdGenerator? idGenerator = null)
|
||||
: TenantAwareDbContext(options, tenantProvider, currentUserAccessor, idGenerator)
|
||||
IIdGenerator? idGenerator = null,
|
||||
IHttpContextAccessor? httpContextAccessor = null)
|
||||
: TenantAwareDbContext(options, tenantProvider, currentUserAccessor, idGenerator, httpContextAccessor)
|
||||
{
|
||||
/// <summary>
|
||||
/// 字典分组集合。
|
||||
@@ -45,6 +47,11 @@ public sealed class DictionaryDbContext(
|
||||
/// </summary>
|
||||
public DbSet<CacheInvalidationLog> CacheInvalidationLogs => Set<CacheInvalidationLog>();
|
||||
|
||||
/// <summary>
|
||||
/// 字典标签覆盖集合。
|
||||
/// </summary>
|
||||
public DbSet<DictionaryLabelOverride> DictionaryLabelOverrides => Set<DictionaryLabelOverride>();
|
||||
|
||||
/// <summary>
|
||||
/// 系统参数集合。
|
||||
/// </summary>
|
||||
@@ -62,6 +69,7 @@ public sealed class DictionaryDbContext(
|
||||
ConfigureGroup(modelBuilder.Entity<DictionaryGroup>(), isSqlite);
|
||||
ConfigureItem(modelBuilder.Entity<DictionaryItem>(), isSqlite);
|
||||
ConfigureOverride(modelBuilder.Entity<TenantDictionaryOverride>());
|
||||
ConfigureLabelOverride(modelBuilder.Entity<DictionaryLabelOverride>());
|
||||
ConfigureImportLog(modelBuilder.Entity<DictionaryImportLog>());
|
||||
ConfigureCacheInvalidationLog(modelBuilder.Entity<CacheInvalidationLog>());
|
||||
ConfigureSystemParameter(modelBuilder.Entity<SystemParameter>());
|
||||
@@ -228,4 +236,34 @@ public sealed class DictionaryDbContext(
|
||||
builder.HasIndex(x => x.TenantId);
|
||||
builder.HasIndex(x => new { x.TenantId, x.Key }).IsUnique();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置字典标签覆盖。
|
||||
/// </summary>
|
||||
/// <param name="builder">实体构建器。</param>
|
||||
private static void ConfigureLabelOverride(EntityTypeBuilder<DictionaryLabelOverride> builder)
|
||||
{
|
||||
builder.ToTable("dictionary_label_overrides", t => t.HasComment("字典标签覆盖配置。"));
|
||||
builder.HasKey(x => x.Id);
|
||||
builder.Property(x => x.Id).HasComment("实体唯一标识。");
|
||||
builder.Property(x => x.TenantId).IsRequired().HasComment("所属租户 ID(覆盖目标租户)。");
|
||||
builder.Property(x => x.DictionaryItemId).IsRequired().HasComment("被覆盖的字典项 ID。");
|
||||
builder.Property(x => x.OriginalValue).HasColumnType("jsonb").IsRequired().HasComment("原始显示值(JSON 格式,多语言)。");
|
||||
builder.Property(x => x.OverrideValue).HasColumnType("jsonb").IsRequired().HasComment("覆盖后的显示值(JSON 格式,多语言)。");
|
||||
builder.Property(x => x.OverrideType).HasConversion<int>().IsRequired().HasComment("覆盖类型。");
|
||||
builder.Property(x => x.Reason).HasMaxLength(512).HasComment("覆盖原因/备注。");
|
||||
ConfigureAuditableEntity(builder);
|
||||
ConfigureSoftDeleteEntity(builder);
|
||||
|
||||
builder.HasOne(x => x.DictionaryItem)
|
||||
.WithMany()
|
||||
.HasForeignKey(x => x.DictionaryItemId)
|
||||
.OnDelete(DeleteBehavior.Cascade);
|
||||
|
||||
builder.HasIndex(x => x.TenantId);
|
||||
builder.HasIndex(x => new { x.TenantId, x.DictionaryItemId })
|
||||
.IsUnique()
|
||||
.HasFilter("\"DeletedAt\" IS NULL");
|
||||
builder.HasIndex(x => new { x.TenantId, x.OverrideType });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -155,9 +155,15 @@ public sealed class DictionaryGroupRepository(DictionaryDbContext context) : IDi
|
||||
if (!string.IsNullOrWhiteSpace(keyword))
|
||||
{
|
||||
var trimmed = keyword.Trim();
|
||||
query = query.Where(group =>
|
||||
EF.Property<string>(group, "Code").Contains(trimmed) ||
|
||||
group.Name.Contains(trimmed));
|
||||
if (DictionaryCode.IsValid(trimmed))
|
||||
{
|
||||
var code = new DictionaryCode(trimmed);
|
||||
query = query.Where(group => group.Code == code || group.Name.Contains(trimmed));
|
||||
}
|
||||
else
|
||||
{
|
||||
query = query.Where(group => group.Name.Contains(trimmed));
|
||||
}
|
||||
}
|
||||
|
||||
if (isEnabled.HasValue)
|
||||
|
||||
@@ -0,0 +1,115 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using TakeoutSaaS.Domain.Dictionary.Entities;
|
||||
using TakeoutSaaS.Domain.Dictionary.Enums;
|
||||
using TakeoutSaaS.Domain.Dictionary.Repositories;
|
||||
using TakeoutSaaS.Infrastructure.Dictionary.Persistence;
|
||||
|
||||
namespace TakeoutSaaS.Infrastructure.Dictionary.Repositories;
|
||||
|
||||
/// <summary>
|
||||
/// 字典标签覆盖仓储实现。
|
||||
/// </summary>
|
||||
public sealed class DictionaryLabelOverrideRepository(DictionaryDbContext context) : IDictionaryLabelOverrideRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据 ID 获取覆盖配置。
|
||||
/// </summary>
|
||||
public Task<DictionaryLabelOverride?> GetByIdAsync(long id, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.DictionaryLabelOverrides
|
||||
.IgnoreQueryFilters()
|
||||
.Include(x => x.DictionaryItem)
|
||||
.FirstOrDefaultAsync(x => x.Id == id && x.DeletedAt == null, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定字典项的覆盖配置。
|
||||
/// </summary>
|
||||
public Task<DictionaryLabelOverride?> GetByItemIdAsync(long tenantId, long dictionaryItemId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.DictionaryLabelOverrides
|
||||
.IgnoreQueryFilters()
|
||||
.FirstOrDefaultAsync(x =>
|
||||
x.TenantId == tenantId &&
|
||||
x.DictionaryItemId == dictionaryItemId &&
|
||||
x.DeletedAt == null,
|
||||
cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取租户的所有覆盖配置。
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<DictionaryLabelOverride>> ListByTenantAsync(
|
||||
long tenantId,
|
||||
OverrideType? overrideType = null,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var query = context.DictionaryLabelOverrides
|
||||
.AsNoTracking()
|
||||
.IgnoreQueryFilters()
|
||||
.Include(x => x.DictionaryItem)
|
||||
.Where(x => x.TenantId == tenantId && x.DeletedAt == null);
|
||||
|
||||
if (overrideType.HasValue)
|
||||
{
|
||||
query = query.Where(x => x.OverrideType == overrideType.Value);
|
||||
}
|
||||
|
||||
return await query.OrderByDescending(x => x.CreatedAt).ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量获取多个字典项的覆盖配置。
|
||||
/// </summary>
|
||||
public async Task<IReadOnlyList<DictionaryLabelOverride>> GetByItemIdsAsync(
|
||||
long tenantId,
|
||||
IEnumerable<long> dictionaryItemIds,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
var ids = dictionaryItemIds.ToArray();
|
||||
if (ids.Length == 0) return Array.Empty<DictionaryLabelOverride>();
|
||||
|
||||
return await context.DictionaryLabelOverrides
|
||||
.AsNoTracking()
|
||||
.IgnoreQueryFilters()
|
||||
.Where(x =>
|
||||
x.TenantId == tenantId &&
|
||||
ids.Contains(x.DictionaryItemId) &&
|
||||
x.DeletedAt == null)
|
||||
.ToListAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 新增覆盖配置。
|
||||
/// </summary>
|
||||
public Task AddAsync(DictionaryLabelOverride entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
context.DictionaryLabelOverrides.Add(entity);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新覆盖配置。
|
||||
/// </summary>
|
||||
public Task UpdateAsync(DictionaryLabelOverride entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
context.DictionaryLabelOverrides.Update(entity);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 删除覆盖配置。
|
||||
/// </summary>
|
||||
public Task DeleteAsync(DictionaryLabelOverride entity, CancellationToken cancellationToken = default)
|
||||
{
|
||||
entity.DeletedAt = DateTime.UtcNow;
|
||||
context.DictionaryLabelOverrides.Update(entity);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 持久化更改。
|
||||
/// </summary>
|
||||
public Task SaveChangesAsync(CancellationToken cancellationToken = default)
|
||||
=> context.SaveChangesAsync(cancellationToken);
|
||||
}
|
||||
Reference in New Issue
Block a user