Golang Model 字段自动化校验设计方案

 更新时间:2025年02月17日 11:06:44   作者:Gerald Kwok  
在我们日常开发中,不可避免的总要去进行各种参数校验,但是如果在某个场景中,要校验的字段非常多,并且在其中还有耦合关系,那么我们手写校验逻辑就变得非常的低效且难以维护,本篇文档就基于 DDD 领域模型设计的思想下,提供自动化的校验模型字段,感兴趣的朋友一起看看吧

背景

在我们日常开发中,不可避免的总要去进行各种参数校验,但是如果在某个场景中,要校验的字段非常多,并且在其中还有耦合关系,那么我们手写校验逻辑就变得非常的低效且难以维护。本篇文档就基于 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
}

自动化校验

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
}

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

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

到此这篇关于Golang Model 字段自动化校验设计的文章就介绍到这了,更多相关Golang Model 字段校验内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Go语言编程实现支持六种级别的日志库 

    Go语言编程实现支持六种级别的日志库 

    这篇文章主要为大家介绍了使用Golang编写一个支持六种级别的日志库示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Golang当中的定时器实例详解

    Golang当中的定时器实例详解

    这篇文章主要给大家介绍了关于Golang当中定时器的相关资料,定时器的实现大家应该都遇到过,最近在学习golang,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • Golang httptest包测试使用教程

    Golang httptest包测试使用教程

    这篇文章主要介绍了Golang httptest包测试使用,httptest包的理念是,非常容易模拟http服务,也就是说模拟响应写(response writer),提供给http处理器(handle),让我们测试http服务端和客户端很容易
    2023-03-03
  • Go 语言数组和切片的区别详解

    Go 语言数组和切片的区别详解

    本文主要介绍了Go 语言数组和切片的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-04-04
  • Golang分布式应用定时任务示例详解

    Golang分布式应用定时任务示例详解

    这篇文章主要为大家介绍了Golang分布式应用定时任务示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Go到底能不能实现安全的双检锁(推荐)

    Go到底能不能实现安全的双检锁(推荐)

    这篇文章主要介绍了Go到底能不能实现安全的双检锁,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-05-05
  • go 语言爬虫库goquery的具体使用

    go 语言爬虫库goquery的具体使用

    GoQuery是专为Go语言设计的一个强大的HTML解析和查询库,本文主要介绍了go语言爬虫库goquery的具体使用,具有一定的参考价值,感兴趣的可以了解一下
    2024-01-01
  • golang 格式化输入输出操作

    golang 格式化输入输出操作

    这篇文章主要介绍了golang 格式化输入输出操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 深入了解Golang中的数据类型

    深入了解Golang中的数据类型

    在计算机编程中,数据类型是非常重要的一个概念。这篇文章将详细介绍 Golang中的数据类型,包括基本类型、复合类型、引用类型以及自定义类型,希望对大家有所帮助
    2023-04-04
  • 一文带你深入了解Go语言中切片的奥秘

    一文带你深入了解Go语言中切片的奥秘

    切片是数组的一个引用,因此切片是引用类型。但自身是结构体,值拷贝传递。本文将通过示例带大家一起探索一下Go语言中切片的奥秘,感兴趣的可以了解一下
    2022-11-11

最新评论