基于Fluent Validation实现剧院票务系统购票折扣校验的问题
解决方案
Fluent Validation 是专门用于输入合法性校验的框架,设计定位不承载业务逻辑赋值职责,因此无法在校验规则中直接修改总金额字段的数值。你可以按以下方案实现需求:
具体实现步骤
- 保留现有必填字段校验逻辑,确保出生日期等核心字段的合法性校验正常生效
- 在校验通过后的业务逻辑层,单独计算年龄、判断折扣条件并给总金额赋值
- 如果需要防止前端篡改总金额,可以新增Fluent Validation规则校验总金额是否符合折扣规则
代码示例
1. 扩展校验类(可选,用于校验总金额合法性)
using FluentValidation; using System; namespace SaleTheaterTickets.Models.ViewModelValidators { public class GeneratedTicketViewModelValidator : AbstractValidator<GeneratedTicketViewModel> { // 这里替换成你的实际单张票原价,也可以从ViewModel的票种/座位价格字段读取 private const decimal OriginalPrice = 100m; public GeneratedTicketViewModelValidator() { // 你原有其他校验规则保留不变 RuleFor(x => x.CustomerName) .NotEmpty().WithMessage("Digite o nome do cliente"); RuleFor(x => x.BirthDate) .NotEmpty().WithMessage("Digite a data de nascimento") .Must(ValidDate).WithMessage("Data de nascimento tem que ser menor que hoje"); RuleFor(x => x.FormOfPayment) .NotEmpty().WithMessage("Selecione a forma de pagamento"); RuleFor(x => x.Seat) .NotEmpty().WithMessage("Escolha a poltrona") .NotNull().WithMessage("Poltrona inválida"); RuleFor(x => x.NeedyChild) .NotEmpty().WithMessage("Responda a pergunta"); // 新增总金额校验规则 RuleFor(x => x.TotalAmount) .NotEmpty().WithMessage("总金额不能为空") .Must((model, total) => BeValidTotalAmount(model.BirthDate, total)) .WithMessage("总金额不符合折扣规则"); } private static bool ValidDate(DateTime date) { return date < DateTime.Now.Date; } // 校验总金额是否符合折扣规则 private static bool BeValidTotalAmount(DateTime birthDate, decimal total) { var age = CalculateAge(birthDate); decimal expectedTotal = age is >= 2 and <= 12 or >= 60 ? OriginalPrice * 0.5m : OriginalPrice; // 处理浮点精度误差 return Math.Abs(total - expectedTotal) < 0.01m; } // 准确计算年龄的公共方法 private static int CalculateAge(DateTime birthDate) { var today = DateTime.Today; int age = today.Year - birthDate.Year; // 当年生日未过的话年龄减1 if (birthDate.Date > today.AddYears(-age)) { age--; } return age; } } }
2. 业务层折扣计算逻辑
// 业务处理示例 var validator = new GeneratedTicketViewModelValidator(); var validationResult = validator.Validate(viewModel); if (validationResult.IsValid) { // 校验通过后计算折扣并赋值 var age = CalculateAge(viewModel.BirthDate); if(age is >= 2 and <= 12 or >= 60) { viewModel.TotalAmount = OriginalPrice * 0.5m; } else { viewModel.TotalAmount = OriginalPrice; } // 后续保存订单等业务逻辑 } // 年龄计算方法,也可以封装成公共工具类方法复用 private static int CalculateAge(DateTime birthDate) { var today = DateTime.Today; int age = today.Year - birthDate.Year; if (birthDate.Date > today.AddYears(-age)) { age--; } return age; }
注意事项
- 计算年龄时不要直接用年份相减,要判断当年生日是否已过,避免年龄计算错误
- 原价可以根据实际业务场景从票种配置、座位价格等字段读取,替换示例中的固定常量即可
- 如果不需要校验前端提交的总金额,可以不用新增校验类里的总金额校验规则,只保留业务层的折扣计算逻辑即可
内容的提问来源于stack exchange,提问作者Karolina
相关产品推荐
相关产品推荐

