mikilin-go是核查框架,该框架是java版本的 核查框架Mikilin 的go版本实现。目前该项目已经作为公司级框架的validate模块了isc-gobase
go get github.com/simonalong/mikilin-go
这里举个例子,快速使用
package main
import (
"github.com/simonalong/mikilin-go"
log "github.com/sirupsen/logrus"
)
type ValueBaseEntityOne struct {
// 对属性修饰
Name string `match:"value=zhou"`
Age int
}
func main() {
var value ValueBaseEntityOne
var result bool
var err string
value = ValueBaseEntityOne{Name: "chen"}
// 核查
result, err = mikilin.Check(value)
if !result {
// 属性 Name 的值 chen 不在只可用列表 [zhou] 中
log.Errorf(err)
}
}
说明:
- 这里提供方法Check,用于核查属性,Check方法也支持核查指定属性,默认核查所有属性
- 提供标签match,标签内容中提供匹配器:value,该匹配器表示匹配的具体的一些值
提示:
value除了匹配一个值,还可以修饰多个值,更多的功能,以及更多匹配器见更多功能
type ValueBaseEntity struct {
Name string `match:"value={zhou, 宋江}"`
Age int `match:"value={12, 13}"`
}
这里将核查部分分为匹配和处理两部分,匹配可以有多种的匹配器,核查的逻辑是只要有任何一个匹配器匹配上则认为匹配上,处理模块用于对匹配上的结果进行处理,比如返回指定的异常,或者匹配后接受还是拒绝对应的值,或者匹配后将某个值更改掉(待支持)
- value:匹配指定的值
- isBlank:值是否为空字符
- isUnBlank:值是否为非空字符
- range:匹配数值的范围(最大值和最小值,用法是数学表达式):数值(整数和浮点数)的大小、字符串的长度、时间的范围、时间的移动
- model:匹配指定的类型:
- id_card:身份证
- phone: 手机号
- fixed_phone:固定电话
- mail: 邮件地址
- ip: ip地址
- condition:修饰的属性的表达式的匹配,提供#current和#root占位符,用于获取相邻属性的值
- regex:匹配正则表达式
- customize:匹配自定义的回调函数
- errMsg: 自定义的异常
- accept: 匹配后接受还是拒绝
- disable: 是否启用匹配,默认启用
匹配器可以有多个一起修饰,只要匹配上一个,则认为匹配上
匹配指定的一些值,可以修饰一个,也可以修饰多个值,可以修饰字符,也可修饰整数(int、int8、int16、int32、int64)、无符号整数(uint、uint8、uint16、uint32、uint64)、浮点数(float32、float64)、bool类型和string类型。
提示:
- 中间逗号也可以为中文,为了防止某些手误写错为中文字符
// 修饰一个值
type ValueBaseEntityOne struct {
Name string `match:"value=zhou"`
Age int `match:"value=12"`
}
// 修饰一个值
type ValueBaseEntity struct {
Name string `match:"value={zhou, 宋江}"`
Age int `match:"value={12, 13}"`
}
如果有自定义类型嵌套,则可以使用标签check
,用于解析复杂结构
type ValueInnerEntity struct {
InnerName string `match:"value={inner_zhou, inner_宋江}"`
InnerAge int `match:"value={2212, 2213}"`
}
type ValueStructEntity struct {
Name string `match:"value={zhou, 宋江}"`
Age int `match:"value={12, 13}"`
Inner ValueInnerEntity `match:"check"`
}
修饰的结构可以有如下
- 自定义结构
- 数组/分片:对应类型只有为复杂结构才会核查
- map:其中的key和value类型只有是复杂结构才会核查
匹配string类型的值是否为空字符,false:字符不为空则匹配上,true:字符为空则匹配上
// 默认为true
type IsBlankEntity2 struct {
Name string `match:"isBlank"`
Age int
}
// 同上
type IsBlankEntity3 struct {
Name string `match:"isBlank=true"`
Age int
}
type IsBlankEntity1 struct {
Name string `match:"isBlank=false"`
Age int
}
匹配string类型的值是否为非空字符,true:字符非空则匹配上,false:字符为空则匹配上
// 默认为true
type IsBlankEntity2 struct {
Name string `match:"isBlank"`
Age int
}
// 同上
type IsBlankEntity3 struct {
Name string `match:"isBlank=true"`
Age int
}
type IsBlankEntity1 struct {
Name string `match:"isBlank=false"`
Age int
}
匹配类型的指定范围,方式使用数学表达式"["、"]"、"("、")",使用数学表达式的开闭符号
- [:表示大于等于
- ]:表示小于等于
- (:表示大于
- ):表示小于
比如:
- [1, 10):表示大于等于1而且小于10
- (1, 10):表示大于1而且小于10
- (1,):表示大于1
- (,10]:表示小于等于10,也可以[,10]
修饰的类型有
- 整数:比较大小,int、int8、int16、int32、int64
- 无符号整数:比较大小,uint、uint8、uint16、uint32、uint64
- 浮点数:比较大小,float32、float64
- 分片:匹配分片的长度
- 时间类型(time.Time):时间的范围,时间格式支持如下
- yyyy
- yyyy-MM
- yyyy-MM-dd
- yyyy-MM-dd HH
- yyyy-MM-dd HH:mm
- yyyy-MM-dd HH:mm:ss
- yyyy-MM-dd HH:mm:ss.SSS
- now:表示当前时间
- 变量时间:除了使用数学表达式外,还支持past、future这两个关键字,用于表示过去和未来的时间
- 时间计算:用于计算当前的时间向前或者向后推几个小时或者几分钟这种;(-1M, ):表示最近一个月的时间;(-1M3d, ):表示最近一个月零3天的时间,表示大于时间向前推一个月零三天的时间
- -/+:表示往前推还是往后推
- y:年
- M:月
- d:日
- h:小时
- m:分钟
- s:秒
// 整数类型1
type RangeIntEntity1 struct {
Age int `match:"range=[1, 2]"`
}
// 整数类型2
type RangeIntEntity2 struct {
Age int `match:"range=[3,]"`
}
// 整数类型3
type RangeIntEntity3 struct {
Age int `match:"range=[3,)"`
}
// 浮点数类型
type RangeFloatEntity struct {
Money float32 `match:"range=[10.37, 20.31]"`
}
// 字符类型
type RangeStringEntity struct {
Name string `match:"range=[2, 12]"`
}
// 分片类型
type RangeSliceEntity struct {
Age []int `match:"range=[2, 6]"`
}
// 时间类型1
type RangeTimeEntity1 struct {
CreateTime time.Time `match:"range=[2019-07-13 12:00:23.321, 2019-08-23 12:00:23.321]"`
}
// 时间类型2
type RangeTimeEntity2 struct {
CreateTime time.Time `match:"range=[2019-07-13 12:00:23.321, ]"`
}
// 时间类型3
type RangeTimeEntity3 struct {
CreateTime time.Time `match:"range=(, 2019-07-23 12:00:23.321]"`
}
// 时间类型4
type RangeTimeEntity4 struct {
CreateTime time.Time `match:"range=[2019-07-23 12:00:23.321, now)"`
}
// 时间类型4
type RangeTimeEntity5 struct {
CreateTime time.Time `match:"range=past"`
}
// 时间类型4
type RangeTimeEntity6 struct {
CreateTime time.Time `match:"range=future"`
}
// 时间计算:年
type RangeTimeCalEntity1 struct {
CreateTime time.Time `match:"range=(-1y, )"`
}
// 时间计算:月
type RangeTimeCalEntity2 struct {
CreateTime time.Time `match:"range=(-1M, )"`
}
// 时间计算:月日
// 顺序不能乱:yMdhms,中间可以为空,比如:-1y3d
type RangeTimeCalEntity2And1 struct {
CreateTime time.Time `match:"range=(-1M3d, )"`
}
// 时间计算:日
type RangeTimeCalEntity3 struct {
CreateTime time.Time `match:"range=(-3d, )"`
}
// 时间计算:时
type RangeTimeCalEntity4 struct {
CreateTime time.Time `match:"range=(-4h, )"`
}
// 时间计算:分
type RangeTimeCalEntity5 struct {
CreateTime time.Time `match:"range=(-12m, )"`
}
// 时间计算:秒
type RangeTimeCalEntity6 struct {
CreateTime time.Time `match:"range=(-120s, )"`
}
// 时间计算:正负号
type RangeTimeCalEntity7 struct {
CreateTime time.Time `match:"range=(2h, )"`
}
类型匹配器:指定的几种内置类型进行匹配
- id_card:身份证
- phone: 手机号
- fixed_phone:固定电话
- mail: 邮件地址
- ip: ip地址
type ValueModelIdCardEntity struct {
Data string `match:"model=id_card"`
}
type ValueModelPhone struct {
Data string `match:"model=phone"`
}
type ValueModelFixedPhoneEntity struct {
Data string `match:"model=fixed_phone"`
}
type ValueModelEmailEntity struct {
Data string `match:"model=mail"`
}
type ValueModelIpAddressEntity struct {
Data string `match:"model=ip"`
}
表达式匹配器:用于数学计算表达式进行计算,表达式是返回bool类型的表达式。提供两个占位符
- #current:当前修饰的值
- #root:当前属性所在的对象,比如:#root.Age,表示当前对象中的其他属性Age的值
// 测试基本表达式
type ValueConditionEntity1 struct {
Data1 int `match:"condition=#current + #root.Data2 > 100"`
Data2 int `match:"condition=#current < 20"`
Data3 int `match:"condition=(++#current) >31"`
}
// 测试表达式
type ValueConditionEntity2 struct {
Age int `match:"condition=#root.Judge"`
Judge bool
}
正则表达式匹配器:用于匹配自定义的正则表达式
type ValueRegexEntity struct {
Name string `match:"regex=^zhou.*zhen$"`
Age int `match:"regex=^\\d+$"`
}
该匹配器可以用于自定义扩展,比如实际业务场景,某个字段在数据库中存在,这种情况就需要用户自定义扩展
比如:
package fun
type CustomizeEntity1 struct {
// fun.Judge1是对应的函数
Name string `match:"customize=judge1Name"`
}
func JudgeString1(name string) bool {
if name == "zhou" || name == "宋江" {
return true
}
return false
}
// 由于go反射功能没那么强,因此需要用户自己先将函数和name进行注册
func init() {
mikilin.RegisterCustomize("judge1Name", JudgeString1)
}
其中自定义的函数有相关的要求,参数可以为一个,也可以为两个,返回
参数:
- 一个值:为修饰的属性的值
- 两个值:第一个值为属性所在的对象的值,第二个值为属性的值
返回值:
- 一个值:则为bool类型(表示是否匹配上)
- 两个值:第一个为bool类型(表示是否匹配上),第二个为string类型(匹配或者没有匹配上的自定义错误)
package fun
import (
"fmt"
"github.com/simonalong/mikilin-go"
)
type CustomizeEntity2 struct {
Name string `match:"customize=judge2Name"`
}
type CustomizeEntity3 struct {
Name string `match:"customize=judge3Name"`
Age int
}
func JudgeString2(name string) (bool, string) {
if name == "zhou" || name == "宋江" {
return true, ""
}
return false, "没有命中可用的值'zhou'和'宋江'"
}
func JudgeString3(customize CustomizeEntity3, name string) (bool, string) {
if name == "zhou" || name == "宋江" {
if customize.Age > 12 {
return true, ""
} else {
return false, "用户[" + name + "]" + "没有满足年龄age > 12," + "当前年龄为:" + fmt.Sprintf("%v", customize.Age)
}
} else {
return false, "没有命中可用的值'zhou'和'宋江'"
}
}
func init() {
mikilin.RegisterCustomize("judge2Name", JudgeString2)
mikilin.RegisterCustomize("judge3Name", JudgeString3)
}
当匹配后如何处理,这里分为了如下几种处理
匹配后是接收还是拒绝,目前业内的处理方式都是匹配后接收,在概念上其实叫白名单,对于黑名单的处理,业内是没有,而我们这里用accept实现白名单和黑名单的概念
type AcceptEntity1 struct {
// 表示只要匹配上name为zhou的,则拒绝
Name string `match:"value=zhou" accept:"false"`
Age int
}
type AcceptEntity2 struct {
// 表示name为空,则拒绝,表示需要非空才行,以下同`match:"isUnBlank"`
Name string `match:"isBlank" accept:"false"`
Age int
}
// 只要任何一个匹配上,则接受
type AcceptEntity3 struct {
Name string `match:"isBlank=true value=zhou" accept:"true"`
Age int
}
匹配后返回自定义的异常,提供了两个占位符#current表示修饰的当前属性的值,#root当前属性所在的结构的值,#root.Age表示当前结构中的属性Age对应的值
type ErrMsgEntity1 struct {
Name string `match:"value=zhou" errMsg:"对应的值不合法"`
Age int
}
type ErrMsgEntity2 struct {
Name string `match:"value=zhou"`
Age int `match:"condition=#current > 10" errMsg:"当前的值不合法,应该大于10,当前值为#current,对应的名字为#root.Name"`
}
表示是否启用整个核查
type DisableEntity1 struct {
Name string `match:"value=zhou" disable:"true"`
Age int
}