fix: 修复商户中心查询 RowVersion 列不存在问题
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 38s
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 38s
This commit is contained in:
@@ -44,7 +44,7 @@ public sealed record UpdateMerchantCommand : IRequest<UpdateMerchantResultDto?>
|
||||
public string? ContactEmail { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 并发控制版本。
|
||||
/// 并发控制版本(兼容字段,当前由数据库 xmin 托管)。
|
||||
/// </summary>
|
||||
public byte[] RowVersion { get; init; } = Array.Empty<byte>();
|
||||
public byte[]? RowVersion { get; init; }
|
||||
}
|
||||
|
||||
@@ -29,16 +29,11 @@ public sealed class UpdateMerchantCommandHandler(
|
||||
/// <inheritdoc />
|
||||
public async Task<UpdateMerchantResultDto?> Handle(UpdateMerchantCommand request, CancellationToken cancellationToken)
|
||||
{
|
||||
if (request.RowVersion == null || request.RowVersion.Length == 0)
|
||||
{
|
||||
throw new BusinessException(ErrorCodes.ValidationFailed, "RowVersion 不能为空");
|
||||
}
|
||||
|
||||
// 1. 获取操作者权限
|
||||
var currentTenantId = tenantProvider.GetCurrentTenantId();
|
||||
|
||||
// 2. 读取商户信息
|
||||
var merchant = await merchantRepository.FindByIdAsync(request.MerchantId, currentTenantId, cancellationToken);
|
||||
var merchant = await merchantRepository.GetForUpdateAsync(request.MerchantId, currentTenantId, cancellationToken);
|
||||
|
||||
if (merchant == null)
|
||||
{
|
||||
@@ -73,7 +68,6 @@ public sealed class UpdateMerchantCommandHandler(
|
||||
merchant.Address = registeredAddress;
|
||||
merchant.ContactPhone = contactPhone;
|
||||
merchant.ContactEmail = contactEmail;
|
||||
merchant.RowVersion = request.RowVersion;
|
||||
|
||||
var requiresReview = merchant.Status == MerchantStatus.Approved && criticalChanged;
|
||||
if (requiresReview)
|
||||
|
||||
@@ -21,6 +21,5 @@ public sealed class UpdateMerchantCommandValidator : AbstractValidator<UpdateMer
|
||||
RuleFor(x => x.ContactPhone).NotEmpty().MaximumLength(32);
|
||||
RuleFor(x => x.ContactEmail).EmailAddress().MaximumLength(128)
|
||||
.When(x => !string.IsNullOrWhiteSpace(x.ContactEmail));
|
||||
RuleFor(x => x.RowVersion).NotEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,15 @@ public interface IMerchantRepository
|
||||
/// <returns>商户实体或 null。</returns>
|
||||
Task<Merchant?> FindByIdAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 依据标识获取商户(用于更新,返回可跟踪实体)。
|
||||
/// </summary>
|
||||
/// <param name="merchantId">商户 ID。</param>
|
||||
/// <param name="tenantId">租户 ID。</param>
|
||||
/// <param name="cancellationToken">取消标记。</param>
|
||||
/// <returns>商户实体或 null。</returns>
|
||||
Task<Merchant?> GetForUpdateAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// 按状态筛选商户列表。
|
||||
/// </summary>
|
||||
|
||||
@@ -536,10 +536,12 @@ public sealed class TakeoutAppDbContext(
|
||||
builder.Property(x => x.IsFrozen).HasDefaultValue(false);
|
||||
builder.Property(x => x.FrozenReason).HasMaxLength(500);
|
||||
builder.Property(x => x.ClaimedByName).HasMaxLength(100);
|
||||
builder.Property(x => x.RowVersion)
|
||||
.IsRowVersion()
|
||||
.IsConcurrencyToken()
|
||||
.HasColumnType("bytea");
|
||||
builder.Ignore(x => x.RowVersion);
|
||||
builder.Property<uint>("xmin")
|
||||
.HasColumnName("xmin")
|
||||
.HasColumnType("xid")
|
||||
.ValueGeneratedOnAddOrUpdate()
|
||||
.IsConcurrencyToken();
|
||||
builder.HasIndex(x => x.TenantId);
|
||||
builder.HasIndex(x => new { x.TenantId, x.Status });
|
||||
builder.HasIndex(x => x.ClaimedBy);
|
||||
|
||||
@@ -25,6 +25,14 @@ public sealed class EfMerchantRepository(TakeoutAppDbContext context, TakeoutLog
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<Merchant?> GetForUpdateAsync(long merchantId, long tenantId, CancellationToken cancellationToken = default)
|
||||
{
|
||||
return context.Merchants
|
||||
.Where(x => x.TenantId == tenantId && x.Id == merchantId)
|
||||
.FirstOrDefaultAsync(cancellationToken);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<IReadOnlyList<Merchant>> SearchAsync(long tenantId, MerchantStatus? status, CancellationToken cancellationToken = default)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user