/mikilin-go

go的核查框架

Primary LanguageGo

mikilin-go

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)
    }
}

说明:

  1. 这里提供方法Check,用于核查属性,Check方法也支持核查指定属性,默认核查所有属性
  2. 提供标签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: 是否启用匹配,默认启用

匹配模块

匹配器可以有多个一起修饰,只要匹配上一个,则认为匹配上

1. 值匹配器:value

匹配指定的一些值,可以修饰一个,也可以修饰多个值,可以修饰字符,也可修饰整数(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类型只有是复杂结构才会核查

2. 空值匹配器:isBlank

匹配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
}

3. 非空匹配器:isUnBlank

匹配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
}

4. 范围匹配器:range

匹配类型的指定范围,方式使用数学表达式"["、"]"、"("、")",使用数学表达式的开闭符号

  • [:表示大于等于
  • ]:表示小于等于
  • (:表示大于
  • ):表示小于

比如:

  • [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, )"`
}

5. 类型匹配器:model

类型匹配器:指定的几种内置类型进行匹配

  • 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"`
}

6. 表达式匹配器:condition

表达式匹配器:用于数学计算表达式进行计算,表达式是返回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
}

7. 正则表达式匹配器:regex

正则表达式匹配器:用于匹配自定义的正则表达式

type ValueRegexEntity struct {
    Name string `match:"regex=^zhou.*zhen$"`
    Age  int    `match:"regex=^\\d+$"`
}

8. 自定义回调匹配器:customize

该匹配器可以用于自定义扩展,比如实际业务场景,某个字段在数据库中存在,这种情况就需要用户自定义扩展

比如:

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)
}

处理模块

当匹配后如何处理,这里分为了如下几种处理

1. 匹配上接受/拒绝:accept

匹配后是接收还是拒绝,目前业内的处理方式都是匹配后接收,在概念上其实叫白名单,对于黑名单的处理,业内是没有,而我们这里用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
}

2. 自定义异常:errMsg

匹配后返回自定义的异常,提供了两个占位符#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"`
}

3. 启用:disable

表示是否启用整个核查

type DisableEntity1 struct {
    Name string `match:"value=zhou" disable:"true"`
    Age  int
}