fix(pickup): prevent null rowversion on settings and slots save
All checks were successful
Build and Deploy TenantApi / build-and-deploy (push) Successful in 43s

This commit is contained in:
2026-02-19 17:57:52 +08:00
parent 53f7c54c82
commit 7ecf069efd
3 changed files with 32 additions and 8 deletions

View File

@@ -1,4 +1,5 @@
using System.Text.Json;
using System.Security.Cryptography;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
@@ -92,6 +93,7 @@ public sealed class StorePickupController(
setting.AllowDaysAhead = Math.Clamp(request.BasicSettings.BookingDays, 1, 30);
setting.MaxQuantityPerOrder = request.BasicSettings.MaxItemsPerOrder;
setting.Mode = request.Mode is null ? setting.Mode : StoreApiHelpers.ToPickupMode(request.Mode);
setting.RowVersion = CreateRowVersion();
await dbContext.SaveChangesAsync(cancellationToken);
return ApiResponse<object>.Ok(null);
@@ -110,6 +112,7 @@ public sealed class StorePickupController(
var setting = await EnsurePickupSettingAsync(tenantId, parsedStoreId, cancellationToken);
setting.Mode = request.Mode is null ? setting.Mode : StoreApiHelpers.ToPickupMode(request.Mode);
setting.RowVersion = CreateRowVersion();
var existingSlots = await dbContext.StorePickupSlots
.Where(x => x.TenantId == tenantId && x.StoreId == parsedStoreId)
@@ -137,7 +140,8 @@ public sealed class StorePickupController(
Capacity = capacity,
ReservedCount = Math.Clamp(slot.ReservedCount, 0, capacity),
Weekdays = StoreApiHelpers.SerializeWeekdays(slot.DayOfWeeks),
IsEnabled = slot.Enabled
IsEnabled = slot.Enabled,
RowVersion = CreateRowVersion()
});
}
@@ -163,6 +167,7 @@ public sealed class StorePickupController(
var setting = await EnsurePickupSettingAsync(tenantId, parsedStoreId, cancellationToken);
setting.Mode = request.Mode is null ? setting.Mode : StoreApiHelpers.ToPickupMode(request.Mode);
setting.RowVersion = CreateRowVersion();
var normalizedRule = NormalizeFineRule(request.FineRule);
setting.FineRuleJson = JsonSerializer.Serialize(normalizedRule, StoreApiHelpers.JsonOptions);
@@ -218,7 +223,8 @@ public sealed class StorePickupController(
{
targetSetting = new StorePickupSetting
{
StoreId = targetStoreId
StoreId = targetStoreId,
RowVersion = CreateRowVersion()
};
await dbContext.StorePickupSettings.AddAsync(targetSetting, cancellationToken);
}
@@ -229,6 +235,7 @@ public sealed class StorePickupController(
targetSetting.MaxQuantityPerOrder = sourceSetting?.MaxQuantityPerOrder ?? 20;
targetSetting.Mode = sourceSetting?.Mode ?? StorePickupMode.Big;
targetSetting.FineRuleJson = sourceSetting?.FineRuleJson;
targetSetting.RowVersion = CreateRowVersion();
}
var targetSlots = await dbContext.StorePickupSlots
@@ -247,7 +254,8 @@ public sealed class StorePickupController(
Capacity = slot.Capacity,
ReservedCount = slot.ReservedCount,
Weekdays = slot.Weekdays,
IsEnabled = slot.IsEnabled
IsEnabled = slot.IsEnabled,
RowVersion = CreateRowVersion()
}))
.ToList();
@@ -275,12 +283,18 @@ public sealed class StorePickupController(
setting = new StorePickupSetting
{
StoreId = storeId
StoreId = storeId,
RowVersion = CreateRowVersion()
};
await dbContext.StorePickupSettings.AddAsync(setting, cancellationToken);
return setting;
}
private static byte[] CreateRowVersion()
{
return RandomNumberGenerator.GetBytes(16);
}
private static PickupFineRuleDto ParseFineRule(string? raw)
{
if (!string.IsNullOrWhiteSpace(raw))

View File

@@ -1093,7 +1093,9 @@ public sealed class TakeoutAppDbContext(
builder.Property(x => x.Mode).HasConversion<int>();
builder.Property(x => x.FineRuleJson).HasColumnType("text");
builder.Property(x => x.RowVersion)
.IsConcurrencyToken();
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
@@ -1106,7 +1108,9 @@ public sealed class TakeoutAppDbContext(
builder.Property(x => x.Weekdays).HasMaxLength(32).IsRequired();
builder.Property(x => x.CutoffMinutes).HasDefaultValue(30);
builder.Property(x => x.RowVersion)
.IsConcurrencyToken();
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.Name });
}

View File

@@ -283,7 +283,10 @@ public sealed class TakeoutTenantAppDbContext(
builder.Property(x => x.AllowDaysAhead).HasDefaultValue(3);
builder.Property(x => x.DefaultCutoffMinutes).HasDefaultValue(30);
builder.Property(x => x.MaxQuantityPerOrder);
builder.Property(x => x.RowVersion).IsRowVersion();
builder.Property(x => x.RowVersion)
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId }).IsUnique();
}
@@ -300,7 +303,10 @@ public sealed class TakeoutTenantAppDbContext(
builder.Property(x => x.ReservedCount).HasDefaultValue(0);
builder.Property(x => x.Weekdays).HasMaxLength(32).HasDefaultValue("1,2,3,4,5,6,7");
builder.Property(x => x.IsEnabled).HasDefaultValue(true);
builder.Property(x => x.RowVersion).IsRowVersion();
builder.Property(x => x.RowVersion)
.IsRequired()
.IsConcurrencyToken()
.ValueGeneratedNever();
builder.HasIndex(x => new { x.TenantId, x.StoreId, x.StartTime, x.EndTime }).IsUnique();
}
}