feat: migrate snowflake ids and refresh migrations

This commit is contained in:
2025-12-02 09:04:37 +08:00
parent 462e15abbb
commit 148475fa43
174 changed files with 8020 additions and 34278 deletions

View File

@@ -10,17 +10,17 @@ public interface IDictionaryAppService
{
Task<DictionaryGroupDto> CreateGroupAsync(CreateDictionaryGroupRequest request, CancellationToken cancellationToken = default);
Task<DictionaryGroupDto> UpdateGroupAsync(Guid groupId, UpdateDictionaryGroupRequest request, CancellationToken cancellationToken = default);
Task<DictionaryGroupDto> UpdateGroupAsync(long groupId, UpdateDictionaryGroupRequest request, CancellationToken cancellationToken = default);
Task DeleteGroupAsync(Guid groupId, CancellationToken cancellationToken = default);
Task DeleteGroupAsync(long groupId, CancellationToken cancellationToken = default);
Task<IReadOnlyList<DictionaryGroupDto>> SearchGroupsAsync(DictionaryGroupQuery request, CancellationToken cancellationToken = default);
Task<DictionaryItemDto> CreateItemAsync(CreateDictionaryItemRequest request, CancellationToken cancellationToken = default);
Task<DictionaryItemDto> UpdateItemAsync(Guid itemId, UpdateDictionaryItemRequest request, CancellationToken cancellationToken = default);
Task<DictionaryItemDto> UpdateItemAsync(long itemId, UpdateDictionaryItemRequest request, CancellationToken cancellationToken = default);
Task DeleteItemAsync(Guid itemId, CancellationToken cancellationToken = default);
Task DeleteItemAsync(long itemId, CancellationToken cancellationToken = default);
Task<IReadOnlyDictionary<string, IReadOnlyList<DictionaryItemDto>>> GetCachedItemsAsync(DictionaryBatchQueryRequest request, CancellationToken cancellationToken = default);
}

View File

@@ -13,15 +13,15 @@ public interface IDictionaryCache
/// <summary>
/// 获取缓存。
/// </summary>
Task<IReadOnlyList<DictionaryItemDto>?> GetAsync(Guid tenantId, string code, CancellationToken cancellationToken = default);
Task<IReadOnlyList<DictionaryItemDto>?> GetAsync(long tenantId, string code, CancellationToken cancellationToken = default);
/// <summary>
/// 写入缓存。
/// </summary>
Task SetAsync(Guid tenantId, string code, IReadOnlyList<DictionaryItemDto> items, CancellationToken cancellationToken = default);
Task SetAsync(long tenantId, string code, IReadOnlyList<DictionaryItemDto> items, CancellationToken cancellationToken = default);
/// <summary>
/// 移除缓存。
/// </summary>
Task RemoveAsync(Guid tenantId, string code, CancellationToken cancellationToken = default);
Task RemoveAsync(long tenantId, string code, CancellationToken cancellationToken = default);
}

View File

@@ -1,3 +1,5 @@
using System.Text.Json.Serialization;
using TakeoutSaaS.Shared.Abstractions.Serialization;
using System.ComponentModel.DataAnnotations;
namespace TakeoutSaaS.Application.Dictionary.Contracts;
@@ -11,7 +13,8 @@ public sealed class CreateDictionaryItemRequest
/// 所属分组 ID。
/// </summary>
[Required]
public Guid GroupId { get; set; }
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long GroupId { get; set; }
/// <summary>
/// 字典项键。

View File

@@ -1,5 +1,8 @@
using TakeoutSaaS.Domain.Dictionary.Enums;
using System.Text.Json.Serialization;
using TakeoutSaaS.Shared.Abstractions.Serialization;
namespace TakeoutSaaS.Application.Dictionary.Models;
/// <summary>
@@ -7,7 +10,8 @@ namespace TakeoutSaaS.Application.Dictionary.Models;
/// </summary>
public sealed class DictionaryGroupDto
{
public Guid Id { get; init; }
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long Id { get; init; }
public string Code { get; init; } = string.Empty;

View File

@@ -1,3 +1,6 @@
using System.Text.Json.Serialization;
using TakeoutSaaS.Shared.Abstractions.Serialization;
namespace TakeoutSaaS.Application.Dictionary.Models;
/// <summary>
@@ -5,9 +8,11 @@ namespace TakeoutSaaS.Application.Dictionary.Models;
/// </summary>
public sealed class DictionaryItemDto
{
public Guid Id { get; init; }
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long Id { get; init; }
public Guid GroupId { get; init; }
[JsonConverter(typeof(SnowflakeIdJsonConverter))]
public long GroupId { get; init; }
public string Key { get; init; } = string.Empty;

View File

@@ -47,7 +47,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
var group = new DictionaryGroup
{
Id = Guid.NewGuid(),
Id = 0,
TenantId = targetTenant,
Code = normalizedCode,
Name = request.Name.Trim(),
@@ -62,7 +62,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return MapGroup(group, includeItems: false);
}
public async Task<DictionaryGroupDto> UpdateGroupAsync(Guid groupId, UpdateDictionaryGroupRequest request, CancellationToken cancellationToken = default)
public async Task<DictionaryGroupDto> UpdateGroupAsync(long groupId, UpdateDictionaryGroupRequest request, CancellationToken cancellationToken = default)
{
var group = await RequireGroupAsync(groupId, cancellationToken);
EnsureScopePermission(group.Scope);
@@ -77,7 +77,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return MapGroup(group, includeItems: false);
}
public async Task DeleteGroupAsync(Guid groupId, CancellationToken cancellationToken = default)
public async Task DeleteGroupAsync(long groupId, CancellationToken cancellationToken = default)
{
var group = await RequireGroupAsync(groupId, cancellationToken);
EnsureScopePermission(group.Scope);
@@ -120,7 +120,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
var item = new DictionaryItem
{
Id = Guid.NewGuid(),
Id = 0,
TenantId = group.TenantId,
GroupId = group.Id,
Key = request.Key.Trim(),
@@ -138,7 +138,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return MapItem(item);
}
public async Task<DictionaryItemDto> UpdateItemAsync(Guid itemId, UpdateDictionaryItemRequest request, CancellationToken cancellationToken = default)
public async Task<DictionaryItemDto> UpdateItemAsync(long itemId, UpdateDictionaryItemRequest request, CancellationToken cancellationToken = default)
{
var item = await RequireItemAsync(itemId, cancellationToken);
var group = await RequireGroupAsync(item.GroupId, cancellationToken);
@@ -156,7 +156,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return MapItem(item);
}
public async Task DeleteItemAsync(Guid itemId, CancellationToken cancellationToken = default)
public async Task DeleteItemAsync(long itemId, CancellationToken cancellationToken = default)
{
var item = await RequireItemAsync(itemId, cancellationToken);
var group = await RequireGroupAsync(item.GroupId, cancellationToken);
@@ -186,8 +186,8 @@ public sealed class DictionaryAppService : IDictionaryAppService
foreach (var code in normalizedCodes)
{
var systemItems = await GetOrLoadCacheAsync(Guid.Empty, code, cancellationToken);
if (tenantId == Guid.Empty)
var systemItems = await GetOrLoadCacheAsync(0, code, cancellationToken);
if (tenantId == 0)
{
result[code] = systemItems;
continue;
@@ -200,7 +200,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return result;
}
private async Task<DictionaryGroup> RequireGroupAsync(Guid groupId, CancellationToken cancellationToken)
private async Task<DictionaryGroup> RequireGroupAsync(long groupId, CancellationToken cancellationToken)
{
var group = await _repository.FindGroupByIdAsync(groupId, cancellationToken);
if (group == null)
@@ -211,7 +211,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
return group;
}
private async Task<DictionaryItem> RequireItemAsync(Guid itemId, CancellationToken cancellationToken)
private async Task<DictionaryItem> RequireItemAsync(long itemId, CancellationToken cancellationToken)
{
var item = await _repository.FindItemByIdAsync(itemId, cancellationToken);
if (item == null)
@@ -222,16 +222,16 @@ public sealed class DictionaryAppService : IDictionaryAppService
return item;
}
private Guid ResolveTargetTenant(DictionaryScope scope)
private long ResolveTargetTenant(DictionaryScope scope)
{
var tenantId = _tenantProvider.GetCurrentTenantId();
if (scope == DictionaryScope.System)
{
EnsurePlatformTenant(tenantId);
return Guid.Empty;
return 0;
}
if (tenantId == Guid.Empty)
if (tenantId == 0)
{
throw new BusinessException(ErrorCodes.BadRequest, "业务参数需指定租户");
}
@@ -241,28 +241,28 @@ public sealed class DictionaryAppService : IDictionaryAppService
private static string NormalizeCode(string code) => code.Trim().ToLowerInvariant();
private static DictionaryScope ResolveScopeForQuery(DictionaryScope? requestedScope, Guid tenantId)
private static DictionaryScope ResolveScopeForQuery(DictionaryScope? requestedScope, long tenantId)
{
if (requestedScope.HasValue)
{
return requestedScope.Value;
}
return tenantId == Guid.Empty ? DictionaryScope.System : DictionaryScope.Business;
return tenantId == 0 ? DictionaryScope.System : DictionaryScope.Business;
}
private void EnsureScopePermission(DictionaryScope scope)
{
var tenantId = _tenantProvider.GetCurrentTenantId();
if (scope == DictionaryScope.System && tenantId != Guid.Empty)
if (scope == DictionaryScope.System && tenantId != 0)
{
throw new BusinessException(ErrorCodes.Forbidden, "仅平台管理员可操作系统字典");
}
}
private void EnsurePlatformTenant(Guid tenantId)
private void EnsurePlatformTenant(long tenantId)
{
if (tenantId != Guid.Empty)
if (tenantId != 0)
{
throw new BusinessException(ErrorCodes.Forbidden, "仅平台管理员可操作系统字典");
}
@@ -279,7 +279,7 @@ public sealed class DictionaryAppService : IDictionaryAppService
// 系统参数更新需要逐租户重新合并,由调用方在下一次请求时重新加载
}
private async Task<IReadOnlyList<DictionaryItemDto>> GetOrLoadCacheAsync(Guid tenantId, string code, CancellationToken cancellationToken)
private async Task<IReadOnlyList<DictionaryItemDto>> GetOrLoadCacheAsync(long tenantId, string code, CancellationToken cancellationToken)
{
var cached = await _cache.GetAsync(tenantId, code, cancellationToken);
if (cached != null)