You need to enable JavaScript to run this app. 基于Fluent Validation实现剧院票务系统购票折扣校验的问题
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.20 04:27:03