feat: Add tiered packaging fee support for stores

Introduces tiered packaging fee configuration for stores by adding OrderPackagingFeeMode and PackagingFeeTiers fields to StoreFee. Updates DTOs, validators, handlers, and mapping logic to support both fixed and tiered packaging fee modes. Adds StoreFeeTierHelper for tier normalization and serialization, and includes a database migration to persist the new fields.
This commit is contained in:
2026-01-26 09:26:49 +08:00
parent 923856e286
commit 725f89ae24
18 changed files with 604 additions and 53 deletions

View File

@@ -21,7 +21,7 @@ public sealed class UpdateStoreFeeCommandValidator : AbstractValidator<UpdateSto
RuleFor(x => x.FixedPackagingFee)
.NotNull()
.When(x => x.PackagingFeeMode == PackagingFeeMode.Fixed)
.When(x => x.PackagingFeeMode == PackagingFeeMode.Fixed && x.OrderPackagingFeeMode == OrderPackagingFeeMode.Fixed)
.WithMessage("总计打包费模式下必须填写固定打包费");
RuleFor(x => x.FixedPackagingFee)
@@ -31,5 +31,57 @@ public sealed class UpdateStoreFeeCommandValidator : AbstractValidator<UpdateSto
RuleFor(x => x.FixedPackagingFee)
.Must(fee => !fee.HasValue || fee.Value >= 0)
.WithMessage("固定打包费不能为负数");
RuleFor(x => x)
.Custom((command, context) =>
{
if (command.PackagingFeeMode != PackagingFeeMode.Fixed || command.OrderPackagingFeeMode != OrderPackagingFeeMode.Tiered)
{
return;
}
if (command.PackagingFeeTiers is null || command.PackagingFeeTiers.Count == 0)
{
context.AddFailure("阶梯价模式必须配置至少 1 个区间");
return;
}
if (command.PackagingFeeTiers.Count > 10)
{
context.AddFailure("阶梯价最多支持 10 个区间");
return;
}
var expectedMin = 0m;
for (var index = 0; index < command.PackagingFeeTiers.Count; index++)
{
var tier = command.PackagingFeeTiers[index];
if (tier.Fee < 0 || tier.Fee > 99.99m)
{
context.AddFailure($"第 {index + 1} 个阶梯打包费需在 0~99.99 之间");
return;
}
if (tier.MaxPrice.HasValue && tier.MaxPrice.Value <= expectedMin)
{
context.AddFailure($"第 {index + 1} 个阶梯上限必须大于 {expectedMin:0.##}");
return;
}
if (!tier.MaxPrice.HasValue && index != command.PackagingFeeTiers.Count - 1)
{
context.AddFailure("仅允许最后一个阶梯的上限为空");
return;
}
if (tier.MaxPrice.HasValue && tier.MaxPrice.Value > 99999.99m)
{
context.AddFailure($"第 {index + 1} 个阶梯上限不能超过 99999.99");
return;
}
expectedMin = tier.MaxPrice ?? expectedMin;
}
});
}
}