当前位置: 首页 > article >正文

Golang Model 字段自动化校验设计

背景

在我们日常开发中,不可避免的总要去进行各种参数校验,但是如果在某个场景中,要校验的字段非常多,并且在其中还有耦合关系,那么我们手写校验逻辑就变得非常的低效且难以维护。本篇文档就基于 DDD 领域模型设计的思想下,提供自动化的校验模型字段。

常见的字段校验方式

数据校验在业务逻辑代码中有着至关重要的作用,关系到整个后续业务是否可以正常运行。对参数的校验根据其具体业务逻辑与场景,可以分为字段校验、依赖校验、功能校验与逻辑校验四个部分。

字段校验

字段校验是最常见的校验类型。例如:商品名称不能超过多少个字符,商品状态必须是有效等。

func (e *Shop) ValidateShopName() error {
  	if e.Name != nil && e.Name == "" {
    		return errors.New("商品名称不能为空。")
 		}
  
  	if e.Name != nil && utf8.RuneCountInString(e.Name) > constant.MaxShopNameLength {
    		return errors.Errorf("商品名称长度为 %d, 不能超过 %d ", utf8.RuneCountInString(e.Name), constant.MaxShopNameLength)
  	}
  
  	return nil
}

依赖校验

依赖校验,顾名思义是在业务逻辑中依赖了其他模块。例如,在创建商品信息时,要校验一下商品依赖的商家或供应商等信息是否合法。

func (e *Shop) ValidateMerchant() error {
		// 在此方法中可能需要进行外部调用或者查询 DB 的操作。
  	if e.HasInvalidMerchant() {
    		return errors.New("商家信息存在异常")
  	}
  
  	return nil
}

功能校验

功能校验例如用户是否有权限发布商品、商品信息是否与其他商品存在冲突等。

func (e *Shop) ValidateUserPermission() error {
  	if e.UserCreateShopWithoutPermission() {
    		return errors.New("用户无权限创建商品")
  	}
  
  	return nil
}

逻辑校验

逻辑校验主要是一些具体的业务逻辑。例如在下架商品时,校验是否有新用户下单等。

func (e *Shop) ValidateCloseShop() error{
  	if e.InvalidShopStatus() {
    		return errors.New("商品已下架")
  	}
  	if e.ExistShopTicket() {
    		return errors.New("有正在进行的订单信息,无法下架")
  	}
  
  	return nil
}

上面我们列出来常见的四种校验方式,当我们在一个复杂且庞大的业务场景需要把各种各样的校验放在一起去校验时,我们不得不编写一个庞大的校验函数,把这些单点的校验函数聚合起来,更有甚者都没有进行子逻辑校验的函数区分,就是第一个大函数,把各种各样的校验逻辑代码写到一个函数中,那么长此以往,校验逻辑就会非常复杂,无法迭代。

func (e *Shop) ValidateCreateShop() error {
  	if err = e.ValidateShopName(); err != nil {
    		return err
  	}
  	if err = e.ValidateDescrption(); err != nil {
    		return err
  	}
  	if err = e.ValidateImage(); err != nil {
    		return err
  	}
  	if err = e.ValidateMerchant(); err != nil {
    		return err
  	}
  	if err = e.ValidateUserPermission(); err != nil {
    		return err
  	}
  	if err = e.ValidateCloseShop(); err != nil {
    		return err
  	}
  
  	return nil
}

自动化校验

image-20250215164708771



type Validator struct {
    FieldNames			[]string		// 需要更新的字段
    ValidateNames		[]string		// 需要校验的字段列表
    ValidateFuncList	[]Func() error	// 校验函数列表
}

func (v *Validator) Validate() error {
    for _, validate := range v.validateFuncList {
        if err := validate(); err != nil {
            return err
        }
    }
    return nil
}

// GetFields2ValidateFuncMap 各个字段的校验函数在这里扩展,在调用 register 函数时,会自动注册
func (a *Aggregate) GetFields2ValidateFuncMap() map[string]func() error {
    return map[string]func() error {
        constant.ShopForCreate:		a.Shop.ValidateCreateShop,
        constant.ShopForUpdate: 	a.Shop.ValidateUpdateShop,
        constant.ShopCanStart:  	a.Shop.CanStart,
        // ... 等等各种校验都可以在这里定义一个聚合函数列表
    }
}


func DTOToAgg(dto *DTO.Shop) (*shop.Aggregate, error) {
    baseShop := base.NewBaseShop()
    // 先把传参 model 转化成领域数据
    if err = copier.Copy(baseShop, dto); err != nil {
        return nil, errors.Wrap(err, err.Error())
    }
    
    // New 一个聚合类
    shopAgg := shop.NewShopAggregate(baseShop)
    
    // 获取本次传给领域对象的字段,以及加载要校验的字段
    setFields := GetSetOptionalFields(*dto)
    var validateName []string
    for _, field := range setFields {
        validateName = append(validateName, field)
    }
    
    shopAgg.SetUpdateFields(setFields)
    // 注册 validate 函数
    shopAgg.RegisterValidator(validateName)
    
    return shopAgg, nil   
}

// 执行校验函数
func (v *Validator) ValidateMultipleFields(ctx context.Context) error {
	for _, validate := range v.validateFuncList {
		if err := validate(); err != nil {
			return err
		}
	}
	return
}
image-20250215165659841

简单来描述自动校验分为以下几个步骤:

  1. 在接收传参的转换函数中,先把本次请求传入的字段拿到,并且注册这些字段对应的校验函数。
  2. 进入到业务逻辑处理的函数中,再次增加一些当前业务场景需要的特殊校验函数。
  3. 依次执行校验函数,观察是否有报错。

http://www.kler.cn/a/547853.html

相关文章:

  • AI agent 未来好的趋势:AI医疗影像、智能客服、个性化推荐
  • Python深度学习代做目标检测NLP计算机视觉强化学习
  • IntelliJ IDEA集成本地化部署的DeepSeek
  • 【VUE】前端工程化与前端工程化与webpack
  • 机器学习所需数学知识详细版02【】
  • 收银系统源码开发指南:PHP + Flutter + Uniapp 全栈方案
  • 【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用
  • halcon激光三角测量(十七)calibrate_sheet_of_light_3d_calib_object
  • 基于AIOHTTP、Websocket和Vue3一步步实现web部署平台,无延迟控制台输出,接近原生SSH连接
  • lean4安装
  • 把 CSV 文件摄入到 Elasticsearch 中 - CSVES
  • Pythong 解决Pycharm 运行太慢
  • C#(Winform)通过添加AForge添加并使用系统摄像机
  • 提取MV视频中的音频到mp3
  • 【Golang学习之旅】Go 语言微服务架构实践(gRPC、Kafka、Docker、K8s)
  • 国内 网络安全沙箱
  • 基于Odoo的数据中台建设:助力企业数据驱动决策
  • C#打印设计器
  • 机器学习:二分类和多分类
  • 解锁 DeepSeek 超强能力:蓝耘智算平台搭建实战秘籍