仅本页所有页面
由 GitBook 提供支持
1 / 38

Go微服务框架

Loading...

Loading...

Loading...

快速入门

Loading...

Loading...

Loading...

核心架构

Loading...

Loading...

Loading...

Loading...

web开发

Loading...

Loading...

Loading...

Loading...

Loading...

插件

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

数据库相关

Loading...

Loading...

grpc相关

Loading...

Loading...

Loading...

Loading...

Loading...

一款好用的go微服务框架

在web框架gin的基础上做的2次集成,同时支持grpc开发,支持服务发现

安装:go get -u github.com/wike2019/wike_go@v1.0.9

功能包含

控制器、依赖注入、中间件、表达式、任务组件,redis缓存,服务发现,限流,熔断等

本框架的目的

尽管现在基于 go 语言开发的框架处于一个百家争鸣的时代, 但我还打算做一款go微服务框架,通过详细的文档,简洁的API快速迭代开发,简单易用,功能齐全是我们的初衷

为wike_go做贡

项目地址

pull request 的处理过程对于新特性和 bug 是不一样的。在你发起一个新特性的 pull request 之前,你应该先创建一个带有 [Proposal] 标题的 issue。这个proposal 应当描述这个新特性,以及实现方法。提议将会被审查,有可能会被采纳,也有可能会被拒绝。当一个提议被采纳,将会创建一个实现新特性的 pull request。没有遵循上述指南的 pull request 将会被立即关闭。

为 bug 创建的 Pull requests 不需要创建建议 issue。如果你有解决 bug 的办法,请详细描述你的解决方案。

后期完善:

web客户端功能

  • https://github.com/wike2019/wike_go

    路由

    基本定义

    基于gin的分组,实现了分组路由和控制器路由

    package main
    
    import (
      "github.com/wike2019/wike_go/src/Web"
    )
    
    type IndexController struct {}
    
    func NewIndexController() *IndexController {
    	return &IndexController{}
    }
    
    //执行函数
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	return "this is 首页"
    }
    //实现接口
    func(this *IndexController) Name () string   {
    	return "IndexController"
    }
    
    func(this *IndexController) Build(goft *Web.Goft){
      //注册路由
    	goft.Handle("GET","/helloworld",this.Index)
    }
    
    func main()  {
    	signalChan := make(chan os.Signal, 1)
    	app:= Web.New(). //初始化脚手架
    		Mount("",NewIndexController()).  //挂载控制器
    	go func() {
    	   app.Launch()
       }()
      signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    	//关闭工作
    	<-signalChan
    }

    此时访问地址为 http://127.0.0.1:8180/helloworld

    此时访问为 http://127.0.0.1:8180/api/helloworld

    package main
    
    import (
      "github.com/wike2019/wike_go/src/Web"
    )
    
    type IndexController struct {}
    
    func NewIndexController() *IndexController {
    	return &IndexController{}
    }
    
    //执行函数
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	return "this is 首页"
    }
    //实现接口
    func(this *IndexController) Name () string   {
    	return "IndexController"
    }
    
    func(this *IndexController) Build(goft *Web.Goft){
      //注册路由
    	goft.Handle("GET","/helloworld",this.Index)
    }
    
    func main()  {
    	signalChan := make(chan os.Signal, 1)
    	app:= Web.New(). //初始化脚手架
    		Mount("/api",NewIndexController()).  //挂载控制器
    	go func() {
    	   app.Launch()
       }()
      signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    	//关闭工作
    	<-signalChan
    }

    selector选择器

    基本定义

    实现了常见选择算法,包括平滑加权轮训,轮训,随机 ,iphash等选择算法。简单易懂的接口,轻松就能实现服务的选择

    选择算法目前支持

    • LoadBalance.RoundRobinByWeight 平滑加权

    • LoadBalance.SelectByIPHash IPhash

    • LoadBalance.SelectByRand 随机选择

    • LoadBalance.RoundRobin 轮询不加权

    • LoadBalance.SelectByWeightRand 加权轮询

    案例

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E9%80%89%E6%8B%A9%E5%99%A8

    elasticsearch

    敬请期待

    更新记录

    v1.0.9 - 2020-12-27

    改变

    • 修改grpc客户端服务发现问题

    v1.0.8 - 2020-12-27

    改变

    • 添加各种案例

    v1.0.7 - 2020-12-27

    修复

    • 中间件的类型bug

    v1.0.6 - 2020-12-27

    改变

    • grpc的中间件底层修改

    v1.0.5 - 2020-12-27

    修复

    • 服务发现更新节点可能出现节点数量bug

    v1.0.4 - 2020-12-27

    修复

    • 改版selector算法

    v1.0.3 - 2020-12-27

    修复

    • 第一个可用版本上线

    改变

    • 修改核心包名,对外的包全部以大写字母开头

    v1.0.2 - 2020-12-26

    添加

    • 添加grpc功能

    v1.0.1 - 2020-12-26

    添加

    • 添加到代码参考,同时开源项目。不太会发布所以此版本不可用

    最简单的启动代码

    package main
    
    import (
      "github.com/wike2019/wike_go/src/Web"
    )
    
    type IndexController struct {}
    
    func NewIndexController() *IndexController {
    	return &IndexController{}
    }
    
    //执行函数
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	return "this is 首页"
    }
    //实现接口
    func(this *IndexController) Name () string   {
    	return "IndexController"
    }
    
    func(this *IndexController) Build(goft *Web.Goft){
      //注册路由
    	goft.Handle("GET","/",this.Index)
    }
    
    func main()  {
    	signalChan := make(chan os.Signal, 1)
    	app:= Web.New(). //初始化脚手架
    		Mount("",NewIndexController()).  //挂载控制器
    	go func() {
    	   app.Launch()
       }()
      signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    	//关闭工作
    	<-signalChan
    }

    所需环境

    gin 1.15+

    golang 1.12+

    支持windows7、mac、Linux

    支持go module的方式处理依赖

    你所需要的组件 例如redis etcd mysql 等

    开始

    基本定义

    web开发是基于gin的二次封装,更容易的使用,添加更多实用功能

    启动网站

    package main
    
    import (
      "github.com/wike2019/wike_go/src/Web"
    )
    
    type IndexController struct {}
    
    func NewIndexController() *IndexController {
    	return &IndexController{}
    }
    
    //执行函数
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	return "this is 首页"
    }
    //实现接口
    func(this *IndexController) Name () string   {
    	return "IndexController"
    }
    
    func(this *IndexController) Build(goft *Web.Goft){
      //注册路由
    	goft.Handle("GET","/",this.Index)
    }
    
    
    func main()  {
    	signalChan := make(chan os.Signal, 1)
    	app:= Web.New(). //初始化脚手架
    		Mount("",NewIndexController()).  //挂载控制器
    	go func() {
    	   app.Launch()
       }()
      signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
    	//关闭工作
    	<-signalChan
    }

    WEB完整功能案例

    配置

    基本定义

    系统配置是基于yaml实现的基本配置加载,不是必选,主要解决的是多节点部署,根据不同的配置文件加载不同配置

    目前支持的核心配置

    • etcd

    • redis

    • mysql

    • web端口

    配置文件放在根目录 必须是application.yaml

    任务组件

    基本定义

    任务组件主要实现了 定时任务,和异步任务,协程数控制,等功能

    1、定时任务,可以定时执行某些操作

    2、异步任务,可以异步执行操作,并且限制任务数量防止内存溢出

    3、限制协程数方法

    4、限制函数执行时间

    定时任务

    定时任务一般用于定时操作,例如数据清理,数据上报备份等,支持到秒。

    案例

    异步任务

    用于一些异步操作,例如日志记录,发送邮件,发送短信等

    案例

    协程数控制

    封装了一个简易限制函数,用于限制协程的数量

    案例

    限时函数

    封装了一个简易的限时函数,用于控制函数的执行时间

    案例

    版本管理

    关于版本

    版本计划

    版本

    状态

    积极支持截止时间

    发布或预计发布时间

    1.0

    Beta 版试用中

    版本说明

    • 积极支持将包含常规迭代周期的 BUG 修复、安全问题修复、功能迭代和功能新增;

    版本规则

    wike_go采用 x.y.z 的版本号规则来命名各个版本,如 1.0.3 版本,1 即为 x,2 即为 y,3 即为 z,您可以根据该版本规则来制定您对框架的更新计划。

    • x 表示一个重大版本,当 Hyperf 的核心进行大量的重构变动时,或当存在大量的破坏性 API 变更时,会作为一个 x 版本发布,x 版本变更通常来说是无法与之前的 x 版本兼容,但也不一定代表完全无法兼容,具体根据对应版本的升级指南来进行甄别。

    • y 表示一个主要功能迭代版本,当一些公开的 API 进行了破坏性的变更后,包括公开 API 的变更和删除,导致前置版本可能无法兼容的时候,会以 y 版本来进行发布。

    • z 表示一个完全兼容的修复版本,当对各个组件的已有功能进行 BUG 修复或安全修复时,会选择以一个 z 版本来发布,当一个 BUG 导致了某个功能完全无法使用时,亦可能在 z 版本内修复这个 BUG 时对 API 进行破坏性变更,但由于功能此前已经完全无法使用故此类变更不会以 y 版本来发布,除了 BUG 修复,z 版本也可能会包括一些新增的功能或组件,这些功能和组件均不会影响此前的代码使用。

    2022-12-31

    2020-12-31

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/web%E7%9B%B8%E5%85%B3
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E4%BB%BB%E5%8A%A1%E7%BB%84%E4%BB%B6/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E4%BB%BB%E5%8A%A1%E7%BB%84%E4%BB%B6/%E5%BC%82%E6%AD%A5%E4%BB%BB%E5%8A%A1
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E4%BB%BB%E5%8A%A1%E7%BB%84%E4%BB%B6/%E7%BA%BF%E7%A8%8B%E6%95%B0%E6%8E%A7%E5%88%B6
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E4%BB%BB%E5%8A%A1%E7%BB%84%E4%BB%B6/%E9%99%90%E6%97%B6%E5%87%BD%E6%95%B0
    server:
      port: 8180
      mysql: "root:root@tcp(192.168.3.2:3306)/test?charset=utf8mb4&parseTime=True&loc=Local"
      redis: "192.168.3.2:6379"
      etcd: "192.168.3.2:2379"

    etcd

    基本定义

    基于etcd的原生操作,我们在其基础上做了二次封装。

    1、实现了基本操作,取值,存值,设置租约

    2、实现了服务发现与注册功能

    3、实现了锁功能

    基本使用

    封装了etcd常用操作

    案例

    服务发现与注册

    封装了服务发现与注册

    Seletor 函数参数说明,一个服务列表,第二个选择算法 第三个客户端ip

    • 服务列表通过LoadService获得

    • 选择算法目前支持

      • LoadBalance.RoundRobinByWeight 平滑加权

      • LoadBalance.SelectByIPHash

    案例

    锁

    案例

    IPhash
  • LoadBalance.SelectByRand 随机选择

  • LoadBalance.RoundRobin 轮询不加权

  • LoadBalance.SelectByWeightRand 加权轮询

  • https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/etcd/%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/etcd/%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E5%8F%91%E7%8E%B0
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/etcd/%E9%94%81

    依赖注入

    基本定义

    依赖注入使用的是一款第三方开发的检验Ioc容器,我们在其基础上做了二次封装。

    1、所有依赖注入都是“单例模式”

    2、需要注入的对象必须是指针struct

    使用的方式很类似java的configuration的写法

    使用方法一

    注入配置

    说明

    Config方法会使用反射,执行传入的对象的每个方法,如果返回的是一个指针对象则将该对象 注册到Ioc容器中

    案例

    使用方法二

    注入Bean容器

    说明

    Beans方法会将该结构体注册到Ioc容器中,并且可以支持表达式操作

    案例

    Redis

    基本使用

    封装了redis操作,目前只支持string类型,hash,list以后会逐渐完善

    案例

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/redis/%E5%9F%BA%E6%9C%AC%E6%93%8D%E4%BD%9C

    缓存使用

    1. 封装了redis缓存操作,通过DbGetter来决定缓存什么,使代码高度解耦

    2. 可以设置校验策略(内置正则校验策略)

    自定义策略需要实现如下接口

    内置正则策略

    案例

    上下文

    基本定义

    gin封装的上下文对象。支持所有*gin.Context操作

    最新api请参考,gin官网 https://github.com/gin-gonic/gin

    操作常用

    获取query参数

    ctx.Query("lastname") 相当于 ctx.Request.URL.Query().Get("lastname")

    ctx.DefaultQuery("firstname", "Guest") 当firstname不存在时返回Guest,一般多用于分页 默认第一页

    获取post参数

    获取上传文件

    单文件上传

    SaveUploadedFile 将文件移动到指定目标

    多文件上传

    将参数绑定到指定对象

    获取cookie

    写入cookie

    控制器

    基本定义

    文件名譬如是:IndexController 。建议和struct名称一模一样。一个文件只能放一个struct

    控制器名称建议 是大写首字母+Controller

    注意:

    1、控制器必须有个Name方法,且必须返回string,没有参数

    2、必须是指针结构体

    3、必须有个build方法,用来注册路由

    加密

    基本定义

    封装了go内置加密算法,将复杂的细节封装,提供友好的调用接口。

    案例

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Ioc%E4%BD%BF%E7%94%A8/config%E9%85%8D%E7%BD%AE
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Ioc%E4%BD%BF%E7%94%A8/beans%E5%AE%B9%E5%99%A8

    助手函数

    基本定义

    一些辅助函数,配合项目开发使用

    ExistDir 判断目录是否存在

    Map2Struct 将map映射成结构体

    CreateCA 生成https证书,包括自签CA证书 客户端相关证书 服务端相关证书

    熔断

    敬请期待

    type CachePolicy interface {
    	Before(key string ) //之前执行
    	IfNil(key string,v interface{}) //当空值是操作
    	SetOperation(opt *RedisStringOperation) //设置处理器 目前支持string
    }
    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/redis/%E7%BC%93%E5%AD%98%E6%93%8D%E4%BD%9C
    常用写法

    返回string

    返回json

    抛出错误

    sql相关

    根据sql返回数组对象

    根据sql返回单个对象

    根据sql返回数据添加key 返回数据为 { "result": { "name" : "wike" , "id" : 1 } }

    根据sql返回数据将数据转换为map后二次加工后返回

    func(this *IndexController) Index(ctx *gin.Context) string   {
       return "111"
    }
    type Person struct {
    	ID string `uri:"id" binding:"required,uuid"`
    	Name string `uri:"name" binding:"required"`
    }
    
    func(this *IndexController) Index (ctx *gin.Context) Web.Json {
       return &Person{ID:"1111",Name:"wike"}
    }

    AesEncryptCBC加密

    Md5加密

    Sha256加密

    生成非对称加密秘钥

    使用非对称加密

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E5%8A%A0%E5%AF%86
    //缓存穿透 策略
    type CrossPolicy struct {
    	KeyRegx string  //检查key的正则
    	Expire time.Duration  //可以配置查找失败过期时间
    	opt *RedisStringOperation
    }
    
    func NewCrossPolicy(keyRegx string,expire time.Duration) *CrossPolicy {
    	return &CrossPolicy{KeyRegx: keyRegx,Expire:expire}
    }
    
    func (this *CrossPolicy) Before(key string )  {
    		if !regexp.MustCompile(this.KeyRegx).MatchString(key){
    			panic("error cache key")
    		}
    }
    func(this *CrossPolicy) IfNil(key string,v interface{})  {
    	 	this.opt.Set(key,v,WithExpire(this.Expire)).Unwrap()
    
    }
    func(this *CrossPolicy) SetOperation(opt *RedisStringOperation){
    	this.opt=opt
    }
    
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	ctx.DefaultQuery("firstname", "Guest") 
    	ctx.Query("lastname") 
    	return "this is 首页"
    }
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	message := ctx.PostForm("message")
    	nick := ctx.DefaultPostForm("nick", "anonymous")
    	return "this is 首页"
    }
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	  file, _ := ctx.FormFile("file")
    		log.Println(file.Filename)
    		dest:="./"+file.Filename
    		ctx.SaveUploadedFile(file, dst)
    	  return "this is 首页"
    }
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	form, _ := ctx.MultipartForm()
    		files := form.File["upload[]"]
    		for _, file := range files {
    			log.Println(file.Filename)
    			dest:="./"+file.Filename
    			ctx.SaveUploadedFile(file, dst)
    		}
    	return "this is 首页"
    }
    type Person struct {
    	Name    string `form:"name"`
    	Address string `form:"address"`
    }
    
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	if ctx.ShouldBind(&person) == nil {
    		log.Println("====== Only Bind By Query String ======")
    		log.Println(person.Name)
    		log.Println(person.Address)
    	}
    	return "this is 首页"
    }
    
    type Person struct {
    	ID string `uri:"id" binding:"required,uuid"`
    	Name string `uri:"name" binding:"required"`
    }
    
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	if err := ctx.ShouldBindUri(&person); err != nil {
    			return  "参数错误"
    		}
    	return "this is 首页"
    }
    
    type testHeader struct {
    	Rate   int    `header:"Rate"`
    	Domain string `header:"Domain"`
    }
    
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	if err := ctx.ShouldBindHeader(&testHeader); err != nil {
    			return  "heander头错误"
    		}
    	return "this is 首页"
    }
    
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	cookie, err := ctx.Cookie("gin_cookie")
    	return cookie
    }
    func(this *IndexController) Index(ctx *gin.Context) string   {
    	ctx.SetCookie("gin_cookie", "test", 3600, "/", "localhost", false, true)
    	return "this is 首页"
    }
    func(this *IndexController) Index(ctx *gin.Context) Web.Json {
       return gin.H{"resut":"test"}
    }
    func(this *IndexController) Index (ctx *gin.Context) Web.Json {
       u:=&User{}
       fmt.Println(this.Db)
       Web.Error(fmt.Errorf("抛出一个错误"),"这个是返回的提示消息")
    
    }
    func(this *IndexController) Index (ctx *gin.Context) Web.Json {
      Web.Throw("错误消息",500,ctx)
    }
    func(this *IndexController) Index (ctx *gin.Context) sql.Query   {
       return  sql.SimpleQuery("select * from users where user_id > ?").WithArgs(1)
    }
    func(this *IndexController) Index (ctx *gin.Context) sql.Query   {
       return  sql.SimpleQuery("select * from users where user_id = ?").WithArgs(1).WithFirst()
    }
    func(this *IndexController) Index (ctx *gin.Context) sql.Query   {
       return  sql.SimpleQuery("select * from users where user_id = ?").WithArgs(1).WithFirst().WithKey("result")
    }
    func(this *IndexController) Index  (ctx *gin.Context) Web.Json {
       //u:=&User{}
       ret:=  sql.SimpleQuery("select * from users where user_id = ?").WithArgs(1).WithFirst().Get()
       m := ret.(map[string]interface{})
       m["additive"]="添加一些数据"
       return  m
    }
    package main
    
    import (
       "encoding/base64"
       "encoding/hex"
       "fmt"
       "github.com/wike2019/wike_go/src/util/Crypto"
    )
    
    func main()  {
    
       crypto := Crypto.New()
       origData := []byte("Hello World 11111233") // 待加密的数据
       fmt.Println("原文:", string(origData))
       decrypted := crypto.AesDecryptCBC(encrypted)
       fmt.Println("解密结果:", string(decrypted))
    
    
    
    }
    
    package main
    
    import (
       "encoding/base64"
       "encoding/hex"
       "fmt"
       "github.com/wike2019/wike_go/src/util/Crypto"
    )
    
    func main()  {
    
        crypto := Crypto.New()
       	fmt.Println(crypto.Md5("wike is ok"))
    
    
    }
    package main
    
    import (
       "encoding/base64"
       "encoding/hex"
       "fmt"
       "github.com/wike2019/wike_go/src/util/Crypto"
    )
    
    func main()  {
    
        crypto := Crypto.New()
       	fmt.Println(crypto.Sha256("wike is ok"))
    
    
    }
    package main
    
    import (
       "encoding/base64"
       "encoding/hex"
       "fmt"
       "github.com/wike2019/wike_go/src/util/Crypto"
    )
    
    func main()  {
        crypto := Crypto.New()
        crypto.RSAGenKey(1024,"./public/key")
    
    }
    package main
    
    import (
       "encoding/base64"
       "encoding/hex"
       "fmt"
       "github.com/wike2019/wike_go/src/util/Crypto"
    )
    
    func main()  {
        crypto := Crypto.New()
        data,_:=crypto.EncyptogRSA(origData,"./public/key/publicKey.pem")
    	  fmt.Println("加密之后的数据为:",string(data))
    	  data,_=crypto.DecrptogRSA(data,"./public/key/privateKey.pem")
    	  fmt.Println("解密之后的数据为:",string(data))
    
    }

    中间件

    基本定义

    gin里面本身就有中间件,脚手架里对中间件做了简单的封装

    接口定义

    OnRequest: 执行控制器方法前,修改如头信息、判断参数等等

    验证器

    基本定义

    基于第三方库实现的验证器,自定义消息提示,和gin自带的参数绑定完美结合。

    添加自定义验证器

    压缩

    基本定义

    封装了go内置压缩算法,将复杂的细节封装,提供友好的调用接口。

    案例

    限流

    敬请期待

    OnResponse:执行控制器方法后。可以修改返回值内容

    创建全局中间件

    注意点:两个方法必须都有返回值。

    注册全局中间件

    注册路由中间件

    type Fairing interface {
        OnRequest(*gin.Context) error
        OnResponse(result interface{}) (interface{}, error)
    }
    验证结构体

    自定义消息

    自定义消息规则 vmsg:"验证tag=自定义消息提示"

    案例

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E9%AA%8C%E8%AF%81%E5%99%A8

    Validate.New().AddValiDate("NewEmail", func(fl validator.FieldLevel) bool {
       return  true
    })

    Archive压缩

    Compress压缩

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/%E5%8A%A0%E5%AF%86
    package main
    
    import (
    	"fmt"
    	"github.com/wike2019/wike_go/src/util/Archive"
    )
    
    func main()  {
        instance:=Archive.New() //创建对象
        
        instance.TarFile("./用于压缩测试/my.txt","./txt.tar")
        
        instance.UnTarFile("./txt.tar","./用于压缩解压测试")
        
        instance.TarDir("./用于压缩测试","./go.tar")
        
        instance.UnTarDir("./go.tar","./压缩解压测试tmp")
        
        instance.Zip("./用于压缩测试","./压缩解压测试tmp.zip")
        
        instance.UnZip("./压缩解压测试tmp.zip","./压缩解压测试zip")
    
    }
    type TokenCheck struct {}
    func NewTokenCheck() *TokenCheck {
       return &TokenCheck{}
    }
    func(this *TokenCheck) OnRequest(ctx *gin.Context) error{
          return nil
    }
    func(this *TokenCheck) OnResponse(result interface{}) (interface{}, error){
       return result,nil  
    }
    type AddVersion struct {
    
    }
    func NewAddVersion() *AddVersion {
       return &AddVersion{}
    }
    func(this *AddVersion) OnRequest(ctx *gin.Context) error{
       return nil
    }
    func(this *AddVersion) OnResponse(result interface{}) (interface{}, error){
       if m,ok:=result.(gin.H);ok{
          m["version"]="0.3.0"
          return m,nil
       }
       return result,nil
    }
    func main()  {
       Web.New().
          Attach(NewTokenCheck(),NewAddVersion()).
          Mount("v1",NewIndexController()).
          Launch()
    }
    type CheckVersion struct {
    
    }
    
    func NewCheckVersion() *CheckVersion {
       return &CheckVersion {}
    }
    func(this *CheckVersion) OnRequest(ctx *gin.Context) error{
       if ctx.Query("wike")==""{
          Web.Throw("wike requred",503,ctx)
       }
       return nil
    }
    func(this *CheckVersion) OnResponse(result interface{}) (interface{}, error){
       return result,nil
    }
    type CheckName struct {
    
    }
    
    func NewCheckName() *CheckName {
       return &CheckName {}
    }
    func(this *CheckName ) OnRequest(ctx *gin.Context) error{
       if ctx.Query("wike")==""{
          Web.Throw("wikerequred",503,ctx)
       }
       return nil
    }
    func(this *CheckName ) OnResponse(result interface{}) (interface{}, error){
       return result,nil
    }
    func(this *IndexController) Build(goft *Web.Goft){
       goft.HandleWithFairing("GET","/index",this.Index, NewCheckName(),NewCheckVersion()).
    user:=&User{Name:"不合法的名字"}
    err:=Validate.New().Validate.Struct(user)
    
    fmt.Println(Validate.New().Msg(user,err))
    type User struct {
       Name string `form:"name" binding:"required,CheckName" json:"name" gorm:"column:user_name" name:"user_name" vmsg:"required=用户名必填,CheckName=我是提示信息"`
    }
    package main
    
    import (
    	"fmt"
    	"github.com/wike2019/wike_go/src/util/Compress"
    )
    
    func main()  {
    		compress:=Compress.New()
    		
    		compress.Gzip("./用于压缩测试","./test.gzip")
    		compress.UnGzip("test.gzip","./用于压缩测试gzip")
    		
    		data:=compress.Zlib([]byte("key"))
    		fmt.Println(data)
    		data,_=compress.UnZlib(data)
    		fmt.Println(string(data))
    }

    jwtToken

    基本定义

    JwtToken解决的是用户信息认证,早期我们使用cookie配合session,但随着分布式系统和微服务系统的流行,token验证越来越流行。

    温馨提示

    type UserClaim struct {
       Age float64  //这里是坑 jwt转换过后 Int被转换成float64 所以 int改成float64
       Uname string
       jwt.StandardClaims
    }

    案例

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/JwtToken

    管道操作(多路复用)

    敬请期待

    链路追踪

    敬请期待

    证书

    基本定义

    grpc的核心是远程调用,一般都是内网使用,但是使用证书更加安全,所以本框架grpc强制要求使用双向验证证书,同时验证CA证书。证书可以自己购买,也可以使用本框架生成。为了方便我们提供了一个助手函数用于生成自签证书

    快速创建

    Help.CreateCA([]string{"wike.com","localhost",, "192.168.3.3","192.168.3.2"},"./keys")

    第一个参数是一个字符串切片,可以填多个值,值内容是您的ip和host 可以多个,第二个是你证书需要保存的目录

    中间件

    基本定义

    和web中间件概念一致,grpc中间件分为普通中间件针对普通服务,和流中间件针对流服务,我们对其进行了封装

    接口

    mysql

    基本定义

    Mysql操作,我们一律使用Gorm做为默认ORM,我们的依赖注入完全无缝兼容gorm。在Web案例中我们有访问过数据库

    Gorm特性

    proto

    基本定义

    谷歌的proto,不了解的可以谷歌一波,

    注意事项

    第一点 proto不能是最新版,要使用老版本因为要兼容etcd,这个是个bug,只能降级。

    客户端

    基本定义

    grpc的核心是远程调用,grpc客户端调用。

    注意

    请先启动服务端

    服务器端

    基本定义

    grpc的核心是远程调用,grpc服务端实现。

    案例

    第二点 请装 protoc-go-inject-tag 用于注入tag 很有用

    第三点 文件存放位置,所以proto文件统一一个目录,这样好管理

    第四点 模型定义集中,我们案例是全部定义到Models.proto

    案例

    普通调用

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E6%99%AE%E9%80%9A%E8%B0%83%E7%94%A8

    服务端流调用

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%B5%81

    客户端流调用

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E5%AE%A2%E6%88%B7%E7%AB%AF%E6%B5%81

    双向流调用

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E5%8F%8C%E5%90%91%E6%B5%81

    服务发现调用

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E6%9C%8D%E5%8A%A1%E5%8F%91%E7%8E%B0%E8%B0%83%E7%94%A8

    https://github.com/wike2019/wike_go/tree/main/%E6%A1%88%E4%BE%8B/Grpc%E6%A1%88%E4%BE%8B/%E6%9C%8D%E5%8A%A1%E7%AB%AF
    普通中间件类型

    流中间件类型

    案例

    普通中间件

    流中间件

    type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error)
    type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error
    func RecoveryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp interface{}, err error) {
       defer func() {
          if e := recover(); e != nil {
             debug.PrintStack()
             err = status.Errorf(codes.Internal, "Panic err: %v", e)
          }
       }()
    
       return handler(ctx, req)
    }
    func StreamLoggingInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error{
       log.Printf("gRPC method: %s, %v", info.FullMethod)
       err := handler(srv, stream)
       log.Printf("gRPC method: %s", info.FullMethod)
       return err
    }
    最新api请参考,gorm官网

    本教程只供参考

    • 全功能 ORM

    • 关联 (Has One,Has Many,Belongs To,Many To Many,多态,单表继承)

    • Create,Save,Update,Delete,Find 中钩子方法

    • 支持 Preload、Joins 的预加载

    • 事务,嵌套事务,Save Point,Rollback To Saved Point

    • Context,预编译模式,DryRun 模式

    • 批量插入,FindInBatches,Find/Create with Map,使用 SQL 表达式、Context Valuer 进行 CRUD

    • SQL 构建器,Upsert,数据库锁,Optimizer/Index/Comment Hint,命名参数,子查询

    • 复合主键,索引,约束

    • Auto Migration

    • 自定义 Logger

    • 灵活的可扩展插件 API:Database Resolver(多数据库,读写分离)、Prometheus…

    • 每个特性都经过了测试的重重考验

    • 开发者友好

    模型定义

    模型定义

    模型是标准的 struct,由 Go 的基本数据类型、实现了 Scanner 和 Valuer 接口的自定义类型及其指针或别名组成

    例如:

    type User struct {

    gorm.Model

    Name string

    Age sql.NullInt64

    Birthday time.Time

    Email string

    gorm:"type:varchar(100);unique_index"

    Role string gorm:"size:255" //设置字段的大小为255个字节

    MemberNumber string gorm:"unique;not null"

    约定

    GORM 倾向于约定,而不是配置。默认情况下,GORM 使用 ID 作为主键,使用结构体名的 蛇形复数 作为表名,字段名的 蛇形 作为列名,并使用 CreatedAt、UpdatedAt 字段追踪创建、更新时间

    遵循 GORM 已有的约定,可以减少您的配置和代码量。如果约定不符合您的需求,GORM 允许您自定义配置它们

    gorm.Model

    GORM 定义一个 gorm.Model 结构体,其包括字段 ID、CreatedAt、UpdatedAt、DeletedAt

    您可以将它嵌入到您的结构体中,以包含这几个字段,详情请参考 嵌入结构体

    高级选项

    字段级权限控制

    可导出的字段在使用 GORM 进行 CRUD 时拥有全部的权限,此外,GORM 允许您用标签控制字段级别的权限。这样您就可以让一个字段的权限是只读、只写、只创建、只更新或者被忽略

    注意: 使用 GORM Migrator 创建表时,不会创建被忽略的字段

    创建/更新时间追踪(纳秒、毫秒、秒、Time)

    GORM 约定使用 CreatedAt、UpdatedAt 追踪创建/更新时间。如果您定义了这种字段,GORM 在创建、更新时会自动填充 当前时间

    要使用不同名称的字段,您可以配置 autoCreateTim、autoUpdateTim 标签

    如果您想要保存 UNIX(毫/纳)秒时间戳,而不是 time,您只需简单地将 time.Time 修改为 int 即可

    嵌入结构体

    对于匿名字段,GORM 会将其字段包含在父结构体中,例如:

    对于正常的结构体字段,你也可以通过标签 embedded 将其嵌入,例如:

    并且,您可以使用标签 embeddedPrefix 来为 db 中的字段名添加前缀,例如:

    字段标签

    声明 model 时,tag 是可选的,GORM 支持以下 tag: tag 名大小写不敏感,但建议使用 camelCase 风格

    标签名

    说明

    column

    指定 db 列名

    type

    列数据类型,推荐使用兼容性好的通用类型,例如:所有数据库都支持 bool、int、uint、float、string、time、bytes 并且可以和其他标签一起使用,例如:not null、size, autoIncrement… 像 varbinary(8) 这样指定数据库数据类型也是支持的。在使用指定数据库数据类型时,它需要是完整的数据库数据类型,如:MEDIUMINT UNSIGNED not NULL AUTO_INSTREMENT

    size

    指定列大小,例如:size:256

    primaryKey

    指定列为主键

    unique

    指定列为唯一

    创建

    创建记录

    用指定的字段创建记录

    创建记录并更新给出的字段。

    创建记录并更新未给出的字段。

    批量插入

    要有效地插入大量记录,请将一个 slice 传递给 Create 方法。 将切片数据传递给 Create 方法,GORM 将生成一个单一的 SQL 语句来插入所有数据,并回填主键的值,钩子方法也会被调用。

    使用 CreateInBatches 创建时,你还可以指定创建的数量,例如:

    Upsert 和 Create With Associations 也支持批量插入

    说明 初始化GORM时使用CreateBatchSize选项,所有的INSERT在创建record & associations时都会尊重这个选项

    创建钩子

    GORM允许用户定义的钩子被实现为beforeave, beforereate, AfterSave, AfterCreate。这些钩子方法将在创建记录时被调用,有关生命周期的详细信息请参考钩子

    如果你想跳过钩子方法,你可以使用SkipHooks会话模式,例如

    根据 Map 创建

    GORM支持创建 map[string]interface{}和[]map[string]interface{}{},例如:

    注意当创建from map时,钩子不会被调用,关联不会被保存,主键值也不会被填回

    使用 SQL 表达式、Context Valuer 创建记录

    ORM允许使用SQL表达式插入数据,有两种方法可以实现这个目标,create from map[string]interface{}或自定义数据类型,例如:

    高级选项

    关联创建

    当创建一些带有关联的数据时,如果它的关联值不是零值,那么这些关联将被插入,并且它的hook方法将被调用

    你可以用Select跳过保存关联,省略,例如:

    默认值

    你可以为带有default标签的字段定义默认值,例如:

    然后,当为零值字段插入数据库时,将使用默认值

    注意任何像0,",false这样的零值不会被保存到数据库中,对于那些定义了默认值的字段,你可能想要使用指针类型或Scanner/Valuer来避免这种情况,例如:

    注意:你必须为在数据库中有默认值或虚拟/生成值的字段设置默认标记,如果你想在迁移时跳过默认值定义,你可以使用default:(-),例如:

    当使用虚拟/生成的值时,您可能需要禁用其创建/更新权限,检查字段级权限

    Upsert 及冲突

    GORM为不同的数据库提供兼容的Upsert支持

    在高级查询中还检出firststorinit和firststorcreate

    查看原始SQL和SQL Builder以获取更多细节

    查询

    检索单个对象

    GORM 提供了 First、Take、Last 方法,以便从数据库中检索单个对象。当查询数据库时它添加了 LIMIT 1 条件,且没有找到记录时,它会返回 ErrRecordNotFound 错误

    如果你想避免ErrRecordNotFound错误,你可以使用Find,比如db.Limit(1).Find(&user)

    First, Last方法将根据主键找到第一个/最后一个记录顺序,它只在使用struct查询或提供模型值时有效,如果没有为当前模型定义主键,将根据第一个字段排序,例如:

    根据主键检索

    通过使用内联条件,可以使用主键检索对象。使用字符串时要格外小心,以避免SQL注入,详细信息请参阅安全部分

    检索全部对象

    条件

    String 条件

    Struct & Map 条件

    注意:当使用struct查询时,GORM只会查询非零字段,这意味着如果你的字段的值是0,",false或其他零值,它不会被用于构建查询条件,例如

    您可以使用map来构建查询条件,例如:

    内联条件

    工作原理类似于Where。

    Not 条件

    建造NOT条件 工作原理类似于Where。

    Or 条件

    还可以在高级查询中检查组条件,它可以用于编写复杂的SQL

    选择特定字段

    指定要从数据库检索的字段,默认情况下,选择所有字段

    也可以查看智能选择字段

    Order

    从数据库中检索记录时指定顺序

    Limit & Offset

    Limit指定要检索的记录的最大数量Offset指定在开始返回记录之前要跳过的记录的数量

    关于如何制作分页器的签出分页

    Group & Having

    Distinct

    从模型中选择不同的值

    Distinct 也可以使用Pluck Count

    Joins

    指定连接条件

    Joins 预加载

    你可以在单个SQL中使用连接急切加载关联,例如:

    有关详细信息,请参阅预加载(即时加载)

    Scan

    扫描结果到一个结构的工作类似于查找

    高级查询

    智能选择字段

    GORM 允许通过 Select 方法选择特定的字段,如果您在应用程序中经常使用此功能,你也可以定义一个较小的结构体,以实现调用 API 时自动选择特定的字段,例如:

    说明QueryFields模式将按所有字段的名称选择当前模型

    Locking (FOR UPDATE)

    GORM支持不同类型的锁,例如:

    更多细节请参考原始SQL和SQL Builder

    子查询

    子查询可以嵌套在查询中,当使用* GORM . db对象作为参数时,GORM可以生成子查询

    From 子查询

    GORM允许在方法表的FROM子句中使用子查询,例如:

    Group 条件

    更容易编写带有组条件的复杂SQL查询

    命名参数

    GORM支持sql命名参数。NamedArg或map[string]接口{}{},例如:

    查看Raw SQL和SQL Builder了解更多细节

    Find 至 map

    GORM允许扫描结果映射[string]interface{}或[]map[string]interface{},不要忘记指定模型或表,例如:

    FirstOrInit

    获取第一个匹配的记录或用给定的条件初始化一个新的实例(仅适用于struct或map条件)

    用更多的属性初始化struct如果没有找到记录,这些Attrs将不会被用于构建SQL查询

    为struct分配属性,无论是否找到,这些属性都不会用于构建SQL查询,最终数据也不会保存到数据库中

    FirstOrCreate

    获取第一个匹配的记录或创建一个给定条件的新记录(仅适用于struct, map条件)

    创建具有更多属性的结构,如果没有找到记录,这些属性将不会被用于构建SQL查询

    为记录分配属性,不管是否找到记录,并将它们保存回数据库

    优化器、索引提示

    优化器提示用于控制查询优化器选择某个查询执行计划,GORM 通过 gorm.io/hints 提供支持,例如:

    索引提示允许向数据库传递索引提示,以防查询规划器搞混。

    请参考优化Optimizer Hints/Index/Comment了解更多细节

    迭代

    GORM支持遍历行

    FindInBatches

    批量查询和处理记录

    查询钩子

    GORM允许hook在查询后被调用,当查询一个记录时,它将被调用,参考hook获取详细信息

    Pluck

    从数据库查询单个列并扫描到一个切片,如果要查询多个列,则使用Select和scan代替

    Scopes

    作用域允许您指定可以作为方法调用引用的常用查询

    查看Scopes详细信息

    Count

    获取匹配记录计数

    更新

    保存所有字段

    Save 会保存所有的字段,即使字段是零值

    更新单个列

    当使用 Update 更新单个列时,你需要指定条件,否则会返回 ErrMissingWhereClause 错误,查看 Block Global Updates 获取详情。当使用了 Model 方法,且该对象主键有值,该值会被用于构建条件,例如:

    更新多列

    Updates 方法支持 struct 和 map[string]interface{} 参数。当使用 struct 更新时,默认情况下,GORM 只会更新非零值的字段

    注意 当通过 struct 更新时,GORM 只会更新非零字段。 如果您想确保指定字段被更新,你应该使用 Select 更新选定字段,或使用 map 来完成更新操作

    更新选定字段

    如果您想要在更新时选定、忽略某些字段,您可以使用 Select、Omit

    更新 Hook

    对于更新操作,GORM 支持 BeforeSave、BeforeUpdate、AfterSave、AfterUpdate 钩子,这些方法将在更新记录时被调用,详情请参阅 钩子

    批量更新

    如果您尚未通过 Model 指定记录的主键,则 GORM 会执行批量更新

    阻止全局更新

    如果在没有任何条件的情况下执行批量更新,默认情况下,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误

    对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

    更新的记录数

    获取受更新影响的行数

    高级选项

    使用 SQL 表达式更新

    GORM 允许使用 SQL 表达式更新列,例如:

    并且 GORM 也允许使用 SQL 表达式、自定义数据类型的 Context Valuer 来更新,例如:

    根据子查询进行更新

    使用子查询更新表

    不使用 Hook 和时间追踪

    如果您想在更新时跳过 Hook 方法且不追踪更新时间,可以使用 UpdateColumn、UpdateColumns,其用法类似于 Update、Updates

    检查字段是否有变更?

    GORM 提供了 Changed 方法,它可以被用在 Before Update Hook 里,它会返回字段是否有变更的布尔值

    Changed 方法只能与 Update、Updates 方法一起使用,并且它只是检查 Model 对象字段的值与 Update、Updates 的值是否相等,如果值有变更,且字段没有被忽略,则返回 true

    在更新时修改值

    若要在 Before 钩子中改变要更新的值,如果它是一个完整的更新,可以使用 Save;否则,应该使用 SetColumn ,例如:

    删除

    删除一条记录

    删除一条记录时,删除对象需要指定主键,否则会触发 批量 Delete,例如:

    根据主键删除

    GORM 允许通过内联条件指定主键来检索对象,但只支持整型数值,因为 string 可能导致 SQL 注入。查看 内联条件查询 获取详情

    Delete Hook

    对于删除操作,GORM 支持 BeforeDelete、AfterDelete Hook,在删除记录时会调用这些方法,查看 Hook 获取详情

    批量删除

    如果指定的值不包括主属性,那么 GORM 会执行批量删除,它将删除所有匹配的记录

    阻止全局删除

    如果在没有任何条件的情况下执行批量删除,GORM 不会执行该操作,并返回 ErrMissingWhereClause 错误

    对此,你必须加一些条件,或者使用原生 SQL,或者启用 AllowGlobalUpdate 模式,例如:

    软删除

    如果您的模型包含了一个 gorm.DeletedAt 字段(gorm.Model 已经包含了该字段),它将自动获得软删除的能力!

    拥有软删除能力的模型调用 Delete 时,记录不会被从数据库中真正删除。但 GORM 会将 DeletedAt 置为当前时间, 并且你不能再通过正常的查询方法找到该记录。

    如果您不想引入 gorm.Model,您也可以这样启用软删除特性:

    查找被软删除的记录

    您可以使用 Unscoped 找到被软删除的记录

    永久删除

    您也可以使用 Unscoped 永久删除匹配的记录

    SQL 构建器

    原生 SQL

    原生查询 SQL 和 Scan

    Exec 原生 SQL

    注意 GORM 允许缓存预编译 SQL 语句来提高性能,查看 性能 获取详情

    命名参数

    GORM 支持 sql.NamedArg、map[string]interface{}{} 或 struct 形式的命名参数,例如:

    DryRun 模式

    在不执行的情况下生成 SQL ,可以用于准备或测试生成的 SQL,详情请参考 Session

    Row & Rows

    获取 *sql.Row 结果

    获取 *sql.Rows 结果

    转到 FindInBatches 获取如何在批量中查询和处理记录的信息, 转到 Group 条件 获取如何构建复杂 SQL 查询的信息

    将 sql.Rows 扫描至 model

    使用 ScanRows 将一行记录扫描至 struct,例如:

    高级

    子句(Clause)

    GORM 内部使用 SQL builder 生成 SQL。对于每个操作,GORM 都会创建一个 *gorm.Statement 对象,所有的 GORM API 都是在为 statement 添加/修改 Clause,最后,GORM 会根据这些 Clause 生成 SQL

    例如,当通过 First 进行查询时,它会在 Statement 中添加以下 Clause

    然后 GORM 在 Query callback 中构建最终的查询 SQL,像这样:

    生成 SQL:

    您可以自定义 Clause 并与 GORM 一起使用,这需要实现 Interface 接口

    可以参考 示例

    子句构造器

    不同的数据库, Clause 可能会生成不同的 SQL,例如:

    之所以支持 Clause,是因为 GORM 允许数据库驱动程序通过注册 Clause Builder 来取代默认值,这儿有一个 Limit 的示例

    子句选项

    GORM 定义了很多 Clause,其中一些 Clause 提供了你可能会用到的选项

    尽管很少会用到它们,但如果你发现 GORM API 与你的预期不符合。这可能可以很好地检查它们,例如:

    StatementModifier

    GORM 提供了 StatementModifier 接口,允许您修改语句,使其符合您的要求,这儿有一个 Hint 示例

    http://gorm.book.jasperxu.com/
    // 设置 memberNumber 字段唯一且不为空 Num int
    gorm:"AUTO_INCREMENT"
    // 设置 Num字段自增

    Address string gorm:"index:addr" // 给 Address 创建一个名字是 addr的索引

    IgnoreMe int gorm:"-" //忽略这个字段 }

    default

    指定列的默认值

    precision

    指定列的精度

    scale

    指定列大小

    not null

    指定列为 NOT NULL

    autoIncrement

    指定列为自动增长

    embedded

    嵌套字段

    embeddedPrefix

    嵌入字段的列名前缀

    autoCreateTime

    创建时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoCreateTime:nano

    autoUpdateTime

    创建/更新时追踪当前时间,对于 int 字段,它会追踪时间戳秒数,您可以使用 nano/milli 来追踪纳秒、毫秒时间戳,例如:autoUpdateTime:milli

    index

    根据参数创建索引,多个字段使用相同的名称则创建复合索引,查看 索引 获取详情

    uniqueIndex

    与 index 相同,但创建的是唯一索引

    check

    创建检查约束,例如 check:age > 13,查看 约束 获取详情

    <-

    设置字段写入的权限, <-:create 只创建、<-:update 只更新、<-:false 无写入权限、<- 创建和更新权限

    ->

    设置字段读的权限,->:false 无读权限

    -

    忽略该字段,- 无读写权限、