├── .gitignore ├── LICENSE ├── README.md ├── config ├── conf.yaml └── config.go ├── go.mod ├── main.go ├── mysql └── init.go ├── tpl ├── go │ ├── model.template │ ├── modelTool.template │ └── vo.template └── ts │ └── type.template └── utils ├── file.go ├── format.go └── generate.go /.gitignore: -------------------------------------------------------------------------------- 1 | /generateFile -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 wei-yg 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 一个用于根据数据库表 生成go model(集成gorm操作) 生成器 2 | ###### 一枚前端 学习go ing... 希望看到的朋友 star一下就是给我最大的动力 3 | ###### 哪里做的不好也希望大佬指正 4 | 5 | ## 更新日志 6 | #### 2022-10-27 7 | ###### 1.支持go model 代码生成 8 | ###### 2.支持前端TS 接口模型生成 9 | ###### 3.集成gorm 查询 10 | ###### 4.支持事务处理 11 | 12 | 13 | ## 目录结构 14 | ``` 15 | code-generator-go 16 | ├─main.go //主入口 17 | ├─utils //工具包 也是生成器核心代码 18 | | ├─file.go 19 | | ├─format.go 20 | | └generate.go 21 | ├─tpl // 模板文件目录 (可以根据自己项目情况修改模板文件) 22 | | ├─ts // 生成前端的ts文件模板目录 23 | | | └type.template 24 | | ├─go // 生成go的文件模板目录 25 | | | ├─model.template 26 | | | ├─modelTool.template 27 | | | └vo.template 28 | ├─mysql // mysql初始化 gorm 29 | | └init.go 30 | ├─generateFile // 文件生成的目录 31 | | ├─vo 32 | | ├─types // 存放给前端的ts文件 33 | | ├─model // 存放go的model文件 集成了gorm 34 | ├─config 35 | | ├─conf.yaml 36 | | └config.go 37 | ``` 38 | 39 | ## run run run 40 | 41 | ### 虽然还不知道怎么用,但是 先试一把 42 | ``` 43 | go 1.18+ (当前使用1.19,可修改为1.18) 44 | 在model中使用了泛型 所以需要1.18+ 可自行修改 tpl/go/model.template 修改删除泛型代码适配低版本 45 | ``` 46 | ``` 47 | 修改config目录下 conf.yaml文件 修改为你自己的数据库信息 48 | ``` 49 | ``` 50 | 执行 main.go 51 | ``` 52 | ## 53 | 54 | #### 不出意外 generateFile 目录下即生成了对应文件 55 | ###### 此刻代码生成完结,可以自己参照代码 修改 template 模板实现自己想要的代码 56 | 57 | # 58 | # 59 | # 生成的 model 文件使用说明 60 | 61 | ### 看到此文件了解生成的代码约定 ```````` 重要! 重要! 重要! 62 | #### 63 | #### 约定一 : 数据库如需要 创建时间 修改时间 软删除 需按照以下字段(也是gorm的默认字段) 64 | ``` 65 | DeletedAt gorm.DeletedAt `json:"deletedAt"` // 删除时间 66 | CreatedAt time.Time `json:"createdAt"` // 创建时间 67 | UpdatedAt time.Time `json:"updatedAt"` // 修改时间 68 | ``` 69 | ###### 如需要改此字段也可以在 tpl/go/model.template 内自行修改 70 | #### 约定二 : 命名方式 71 | ``` 72 | struct 大驼峰 AbcAbc 73 | json 小驼峰 abcAbc 74 | 数据库 下划线 abc_abc 75 | ``` 76 | ###### 如需要改此字段也可以在 tpl/go/model.template和 utils/ 实现文件 内自行修改 77 | 78 | #### 开始使用 : 打开任意 model 文件 79 | ###### 我这里以一个简单的 user 表为例 80 | ``` 81 | type User struct { 82 | Id int `json:"id" gorm:"column:id;primaryKey;not null"` 83 | Name string `json:"name" gorm:"column:name"` // 姓名 84 | Password string `json:"password" gorm:"column:password"` // 密码 85 | Gender int `json:"gender" gorm:"column:gender;"` // 性别 1 女 2 男 86 | Age int `json:"age" gorm:"column:age;"` // 年龄 87 | DeletedAt gorm.DeletedAt `json:"deletedAt"` // 删除时间 88 | CreatedAt time.Time `json:"createdAt"` // 创建时间 89 | UpdatedAt time.Time `json:"updatedAt"` // 修改时间 90 | } 91 | 92 | type UserModel interface { 93 | Create(data *User, ops ...SetUserWhereOption) (id int, err error) // 创建 94 | UpdateById(id int, data User, ops ...SetUserWhereOption) (err error) // 根据id修改 95 | UpdateByCondition(data User, ops ...SetUserWhereOption) (err error) // 根据条件map 批量修改 96 | FindById(id int, ops ...SetUserWhereOption) (result User, err error) // 根据id 查询 97 | FindOneByCondition(ops ...SetUserWhereOption) (result User, err error) // 根据条件查询一个 98 | FindByCondition(ops ...SetUserWhereOption) (result []User, err error) // 根据条件查询多个 99 | FindCountByCondition(ops ...SetUserWhereOption) (count int64, err error) // 查询符合条件的个数 100 | FindByConditionWithPage(ops ...SetUserWhereOption) (result ResultPageData[User], err error) // 根据条件分页查询 101 | } 102 | type userModel struct { 103 | db *gorm.DB 104 | table string 105 | } 106 | 107 | func NewUserModel(db *gorm.DB) UserModel { 108 | return &userModel{ 109 | db: db, 110 | table: "user", 111 | } 112 | } 113 | 114 | // SetUserWhereOption 设置查询条件 115 | type SetUserWhereOption func(o *WhereOption[User]) 116 | 117 | (`````````````此处省略部分代码`````````) 118 | 119 | // 分页查询 120 | func (m *userModel) FindByConditionWithPage(ops ...SetUserWhereOption) (result ResultPageData[User], err error) { 121 | query := make(map[string]interface{}) 122 | whereOption := &WhereOption[User]{ 123 | PageNum: 1, 124 | PageSize: 10, 125 | QueryMap: query, 126 | } 127 | for _, o := range ops { 128 | o(whereOption) 129 | } 130 | offsetVal := (whereOption.PageNum - 1) * whereOption.PageSize 131 | tx, _ := userByConditionBase(m, ops...) 132 | // 获取where条件 133 | tx = getWhereStrByWhereOption[User](tx, whereOption) 134 | tx = getOrderByWhereOption[User](tx, whereOption) 135 | err = tx.Count(&result.Total).Offset(offsetVal).Limit(whereOption.PageSize).Find(&result.List).Error 136 | if int64(whereOption.PageNum*whereOption.PageSize) >= result.Total { 137 | result.NextPage = -1 138 | } else { 139 | result.NextPage = result.PageNum + 1 140 | } 141 | result.PageNum = whereOption.PageNum 142 | result.PageSize = whereOption.PageSize 143 | if errors.Is(err, gorm.ErrRecordNotFound) { 144 | return result, nil 145 | } 146 | return result, err 147 | } 148 | ``` 149 | #### 老规矩先试为敬 我们先调用这个 FindByConditionWithPage 根据条件分页查询 (如果不懂泛型 先去学1.18更新的泛型再往下看) 150 | ``` 151 | db := 假如你已经初始化gorm db 152 | userModel := model.NewUserModel(db) 153 | pageRes, err := userModel.FindByConditionWithPage() //获取 第一页数据 每页10条(默认PageNum 1,PageSize 10) 154 | ``` 155 | ##### pageRes 分页返回结构体 156 | ``` 157 | // 文件在 generateFile/model/modelTool.go (生成的文件) 158 | type ResultPageData[T any] struct { 159 | List []T `json:"list"` // 结构体切片 泛型 160 | Total int64 `json:"total"` // 总条数 161 | PageNum int `json:"pageNum"` // 当前页码 162 | NextPage int `json:"nextPage"` // 下一页码数 -1 即为最后一页 163 | PageSize int `json:"pageSize"` // 每页大小 164 | } 165 | ``` 166 | ## 167 | #### 加入查询条件试 依然是调用 FindByConditionWithPage 利用go的 选项模式 实现参数可传可不传(不懂选项模式的可以继续去学习) 168 | ``` 169 | // 如果你用的是goland 在括号内输入 fun是会自动提示的 170 | // 这个回调接收一个 o 类型为 *model.WhereOption[model.User] 171 | // 任意查询实现即是对这个 o 进行赋值 model内会解析o内 内容作为查询条件 172 | pageRes, err := userModel.FindByConditionWithPage(func(o *model.WhereOption[model.User]) { 173 | o.PageNum = 1 // 设置查询第一页 174 | o.PageSize = 5 // 设置每页大小为5页 175 | 176 | // 这里 o.QueryEntry即为泛型传进来的 model.User 177 | o.QueryEntry.Gender = 1 // 查询 条件 女 178 | }) 179 | ``` 180 | ###### 此刻即可满足定义查询 181 | ## 182 | ### 下面看看复杂查询 183 | #### 假如我需要查询的是年龄小于30,id大于1的 且带分页的 184 | ``` 185 | // 如果你用的是goland 在括号内输入 fun是会自动提示的 186 | // 这个回调接收一个 o 类型为 *model.WhereOption[model.User] 187 | // 任意查询实现即是对这个 o 进行赋值 model内会解析o内 内容作为查询条件 188 | pageRes, err := userModel.FindByConditionWithPage(func(o *model.WhereOption[model.User]) { 189 | o.PageNum = 1 // 设置查询第一页 190 | o.PageSize = 5 // 设置每页大小为5页 191 | 192 | // 这里 o.QueryMap 则是参考 gorm的map传参 193 | // 可选项参考 文件 generateFile/model/modelTool.go 内 mapCondition2whereSql 方法 case 2 194 | o.QueryMap["age < ?"] = 30 // 查询年龄小于30 195 | o.QueryMap["id > ?"] = 1 // 查询id大于1的 196 | }) 197 | ``` 198 | #### 事务处理 199 | ``` 200 | // 利用 o.Tx 传入 同一个db操作即可 (参考 gorm Transaction) 201 | db.Transaction(func(tx *gorm.DB) error { 202 | err := customerDao.UpdateByCondition(currentCustomerInfo, func(o *model.WhereOption[model.Customer]) { 203 | o.Tx = tx 204 | }) 205 | if err != nil { 206 | return err 207 | } 208 | // 操作记录 209 | consumptionDao := l.svcCtx.ConsumptionLogDao 210 | consumptionLog := model.ConsumptionLog{ 211 | ConsumerId: req.Id, 212 | OperatorId: tokenInfo.Id, 213 | Money: req.Money, 214 | } 215 | _, err = consumptionDao.Create(&consumptionLog, func(o *model.WhereOption[model.ConsumptionLog]) { 216 | o.Tx = tx 217 | }) 218 | if err != nil { 219 | return err 220 | } 221 | return nil 222 | }) 223 | ``` 224 | ###### 还有模糊查询 排序等就不一一列举 自行开发吧! 225 | ###### model 内还有很大优化空间 继续加油... 226 | 227 | -------------------------------------------------------------------------------- /config/conf.yaml: -------------------------------------------------------------------------------- 1 | Mysql: 2 | #数据库配置 3 | Host: your host 4 | Port: 3306 5 | User: your user 6 | Password: your pwd 7 | DBName: your db 8 | 9 | ModelFile: 10 | #存储路径 11 | ModelPath: "./generateFile/model/" 12 | VoPath: "./generateFile/vo/" 13 | TypePath: "./generateFile/types/" 14 | #是否强制覆盖原文件 15 | IsCover: false 16 | #package名 17 | PackageName: "model" 18 | #需要生成model的表名,支持多个,支持全库扫描(不输入任何参数) 19 | TableName: 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /config/config.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "gopkg.in/yaml.v3" 5 | "os" 6 | ) 7 | 8 | type Yaml struct { 9 | MysqlConfig Mysql `yaml:"Mysql"` 10 | ModelFileConfig ModelFile `yaml:"ModelFile"` 11 | } 12 | 13 | type Mysql struct { 14 | Host string `yaml:"Host"` 15 | Port int `yaml:"Port"` 16 | User string `yaml:"User"` 17 | Password string `yaml:"Password"` 18 | DBName string `yaml:"DBName"` 19 | } 20 | 21 | type ModelFile struct { 22 | ModelPath string `yaml:"ModelPath"` 23 | VoPath string `yaml:"VoPath"` 24 | TypePath string `yaml:"TypePath"` 25 | PackageName string `yaml:"PackageName"` 26 | IsCover bool `yaml:"IsCover"` 27 | TableName []string `yaml:"TableName"` 28 | } 29 | 30 | var YamlConfig Yaml 31 | 32 | func LoadConfig() error { 33 | content, err := os.ReadFile("./config/conf.yaml") 34 | if err != nil { 35 | return err 36 | } 37 | err = yaml.Unmarshal(content, &YamlConfig) 38 | if err != nil { 39 | return err 40 | } 41 | return nil 42 | } 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module generate 2 | 3 | go 1.19 4 | 5 | require ( 6 | golang.org/x/text v0.4.0 7 | gopkg.in/yaml.v3 v3.0.1 8 | gorm.io/driver/mysql v1.4.3 9 | gorm.io/gorm v1.24.0 10 | ) 11 | 12 | require ( 13 | github.com/go-sql-driver/mysql v1.6.0 // indirect 14 | github.com/jinzhu/inflection v1.0.0 // indirect 15 | github.com/jinzhu/now v1.1.5 // indirect 16 | ) 17 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "generate/config" 6 | "generate/utils" 7 | ) 8 | 9 | func main() { 10 | err := config.LoadConfig() 11 | if err != nil { 12 | fmt.Println("加载配置文件失败") 13 | return 14 | } 15 | fmt.Println("读取配置文件成功,开始生成文件!") 16 | 17 | // 生成model文件 18 | utils.CreateFiles(config.YamlConfig) 19 | } 20 | -------------------------------------------------------------------------------- /mysql/init.go: -------------------------------------------------------------------------------- 1 | package mysql 2 | 3 | import ( 4 | "fmt" 5 | "generate/config" 6 | "gorm.io/driver/mysql" 7 | "gorm.io/gorm" 8 | "sync" 9 | ) 10 | 11 | var mysqlOnce sync.Once 12 | var db *gorm.DB 13 | var err error 14 | 15 | func InitMysql() *gorm.DB { 16 | if db != nil { 17 | return db 18 | } 19 | 20 | mysqlOnce.Do(func() { 21 | baseConf := config.YamlConfig.MysqlConfig 22 | dsn := fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=true", 23 | baseConf.User, 24 | baseConf.Password, 25 | baseConf.Host, 26 | baseConf.Port, 27 | baseConf.DBName, 28 | ) 29 | //dsn = dsn + "&loc=Asia%2FShanghai" 30 | 31 | db, err = gorm.Open(mysql.Open(dsn), &gorm.Config{}) 32 | }) 33 | if err != nil { 34 | fmt.Println("数据库连接失败", err) 35 | return nil 36 | } 37 | return db 38 | } 39 | -------------------------------------------------------------------------------- /tpl/go/model.template: -------------------------------------------------------------------------------- 1 | package {{ .Package }} 2 | 3 | {{if .IsImportTime}}import ("time" 4 | "errors" 5 | "gorm.io/gorm" 6 | ){{else}}import ("errors" 7 | "gorm.io/gorm" 8 | ){{end}} 9 | 10 | {{if ne .TableInfo.Comment "" }}//{{ .TableInfo.Comment }}{{end}} 11 | type {{ .ModelStructName}} struct { 12 | {{range $index, $field := .ModelFields}}{{ if eq $field.Field "created_at"}}CreatedAt time.Time `json:"createdAt"` // 创建时间{{ else if eq $field.Field "updated_at" }}UpdatedAt time.Time `json:"updatedAt"` // 修改时间{{ else if eq $field.Field "deleted_at" }}DeletedAt gorm.DeletedAt `json:"deletedAt"` // 删除时间{{ else }}{{ MysqlTableName2GolangStructName $field.Field}} {{ MysqlFiled2GolangType $field.Type}} `json:"{{ Hump2JsonField $field.Field}}" gorm:"column:{{$field.Field}}{{if and $field.Key "PRI" }};primaryKey{{end}}{{if and $field.Key "NO" }};not null{{end}}"` {{if ne $field.Comment "" }}//{{ $field.Comment }}{{end}}{{end}} 13 | {{end}}} 14 | 15 | type {{ .ModelStructName}}Model interface { 16 | Create(data *{{ .ModelStructName}}, ops ...Set{{ .ModelStructName}}WhereOption) (id {{ MysqlFiled2GolangType .IdType}}, err error) // 创建 17 | UpdateById(id {{ MysqlFiled2GolangType .IdType}}, data {{ .ModelStructName}}, ops ...Set{{ .ModelStructName}}WhereOption) (err error) // 根据id修改 18 | UpdateByCondition(data {{ .ModelStructName}}, ops ...Set{{ .ModelStructName}}WhereOption) (err error) // 根据条件map 批量修改 19 | FindById(id {{ MysqlFiled2GolangType .IdType}}, ops ...Set{{ .ModelStructName}}WhereOption) (result {{ .ModelStructName}}, err error) // 根据id 查询 20 | FindOneByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (result {{ .ModelStructName}}, err error) // 根据条件查询一个 21 | FindByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (result []{{ .ModelStructName}}, err error) // 根据条件查询多个 22 | FindCountByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (count int64, err error) // 查询符合条件的个数 23 | FindByConditionWithPage(ops ...Set{{ .ModelStructName}}WhereOption) (result ResultPageData[{{ .ModelStructName}}], err error) // 根据条件分页查询 24 | } 25 | 26 | // Set{{ .ModelStructName}}WhereOption 设置查询条件 27 | type Set{{ .ModelStructName}}WhereOption func(o *WhereOption[{{ .ModelStructName}}]) 28 | 29 | type {{FirstLower .ModelStructName}}Model struct { 30 | db *gorm.DB 31 | table string 32 | } 33 | 34 | func New{{ .ModelStructName}}Model(db *gorm.DB) {{ .ModelStructName}}Model { 35 | return &{{FirstLower .ModelStructName}}Model{ 36 | db: db, 37 | table: "{{.TableInfo.Name}}", 38 | } 39 | } 40 | 41 | 42 | // 基础处理函数 43 | func {{FirstLower .ModelStructName}}ByConditionBase(m *{{FirstLower .ModelStructName}}Model, ops ...Set{{ .ModelStructName}}WhereOption) (tx *gorm.DB, whereOption *WhereOption[{{ .ModelStructName}}]) { 44 | query := make(map[string]interface{}) 45 | whereOption = &WhereOption[{{ .ModelStructName}}]{ 46 | QueryMap: query, 47 | } 48 | for _, o := range ops { 49 | o(whereOption) 50 | } 51 | tx = m.db.Table(m.table) 52 | // 获取where条件 53 | tx = getWhereStrByWhereOption[{{ .ModelStructName}}](tx, whereOption) 54 | return tx, whereOption 55 | } 56 | func (m *{{FirstLower .ModelStructName}}Model) Create(data *{{ .ModelStructName}},ops ...Set{{ .ModelStructName}}WhereOption) (id {{ MysqlFiled2GolangType .IdType}}, err error) { 57 | tx, _ := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 58 | err = tx.Table(m.table).Create(data).Error 59 | return data.Id, err 60 | } 61 | 62 | func (m *{{FirstLower .ModelStructName}}Model) UpdateById(id {{ MysqlFiled2GolangType .IdType}}, data {{ .ModelStructName}},ops ...Set{{ .ModelStructName}}WhereOption) (err error) { 63 | tx, _ := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 64 | return tx.Table(m.table).Where("id = ? ", id).Updates(data).Error 65 | } 66 | 67 | func (m *{{FirstLower .ModelStructName}}Model) UpdateByCondition(data {{ .ModelStructName}}, ops ...Set{{ .ModelStructName}}WhereOption) (err error) { 68 | tx, _ := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 69 | return tx.Updates(data).Error 70 | } 71 | 72 | func (m *{{FirstLower .ModelStructName}}Model) FindById(id {{ MysqlFiled2GolangType .IdType}}, ops ...Set{{ .ModelStructName}}WhereOption) (result {{ .ModelStructName}}, err error) { 73 | tx, _ := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 74 | err = tx.Table(m.table).First(&result, id).Error 75 | if errors.Is(err, gorm.ErrRecordNotFound) { 76 | return result, nil 77 | } 78 | return result, err 79 | } 80 | 81 | func find{{ .ModelStructName}}Base(m *{{FirstLower .ModelStructName}}Model, ops ...Set{{ .ModelStructName}}WhereOption) *gorm.DB { 82 | tx, whereOption := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 83 | if whereOption.Order != "" { 84 | tx = tx.Order(whereOption.Order) 85 | } 86 | return tx 87 | } 88 | func (m *{{FirstLower .ModelStructName}}Model) FindOneByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (result {{ .ModelStructName}}, err error) { 89 | err = find{{ .ModelStructName}}Base(m, ops...).First(&result).Error 90 | if errors.Is(err, gorm.ErrRecordNotFound) { 91 | return result, nil 92 | } 93 | return result, err 94 | } 95 | 96 | func (m *{{FirstLower .ModelStructName}}Model) FindByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (result []{{ .ModelStructName}}, err error) { 97 | err = find{{ .ModelStructName}}Base(m, ops...).Find(&result).Error 98 | if errors.Is(err, gorm.ErrRecordNotFound) { 99 | return result, nil 100 | } 101 | return result, err 102 | } 103 | 104 | func (m *{{FirstLower .ModelStructName}}Model) FindCountByCondition(ops ...Set{{ .ModelStructName}}WhereOption) (count int64, err error) { 105 | err = find{{ .ModelStructName}}Base(m, ops...).Count(&count).Error 106 | return count, err 107 | } 108 | 109 | func (m *{{FirstLower .ModelStructName}}Model) FindByConditionWithPage(ops ...Set{{ .ModelStructName}}WhereOption) (result ResultPageData[{{ .ModelStructName}}], err error) { 110 | query := make(map[string]interface{}) 111 | whereOption := &WhereOption[{{ .ModelStructName}}]{ 112 | PageNum: 1, 113 | PageSize: 10, 114 | QueryMap: query, 115 | } 116 | for _, o := range ops { 117 | o(whereOption) 118 | } 119 | offsetVal := (whereOption.PageNum - 1) * whereOption.PageSize 120 | tx, _ := {{FirstLower .ModelStructName}}ByConditionBase(m, ops...) 121 | // 获取where条件 122 | tx = getWhereStrByWhereOption[{{ .ModelStructName}}](tx, whereOption) 123 | tx = GetOrderByWhereOption[{{ .ModelStructName}}](tx, whereOption) 124 | err = tx.Count(&result.Total).Offset(offsetVal).Limit(whereOption.PageSize).Find(&result.List).Error 125 | if int64(whereOption.PageNum*whereOption.PageSize) >= result.Total { 126 | result.NextPage = -1 127 | } else { 128 | result.NextPage = result.PageNum + 1 129 | } 130 | result.PageNum = whereOption.PageNum 131 | result.PageSize = whereOption.PageSize 132 | if errors.Is(err, gorm.ErrRecordNotFound) { 133 | return result, nil 134 | } 135 | return result, err 136 | } -------------------------------------------------------------------------------- /tpl/go/modelTool.template: -------------------------------------------------------------------------------- 1 | package {{ .packageName}} 2 | 3 | import ( 4 | "fmt" 5 | "gorm.io/gorm" 6 | "reflect" 7 | "strings" 8 | "time" 9 | ) 10 | 11 | type WhereOption[T any] struct { 12 | PageNum int `json:"pageNum"` //页码 13 | PageSize int `json:"pageSize"` //每页大小 14 | Order string `json:"order"` //排序 15 | QueryMap map[string]interface{} `json:"query"` //查询条件map 16 | QueryEntry T `json:"queryEntry"` // 查询实体 17 | Tx *gorm.DB `json:"tx"` // 事务时使用 18 | } 19 | type ResultPageData[T any] struct { 20 | List []T `json:"list"` 21 | Total int64 `json:"total"` 22 | PageNum int `json:"pageNum"` 23 | NextPage int `json:"nextPage"` 24 | PageSize int `json:"pageSize"` 25 | } 26 | 27 | type Model struct { 28 | CreatedAt time.Time 29 | UpdatedAt time.Time 30 | DeletedAt gorm.DeletedAt 31 | } 32 | 33 | func Struct2MapFieldName(obj interface{}) map[string]interface{} { 34 | t := reflect.TypeOf(obj) 35 | v := reflect.ValueOf(obj) 36 | var data = make(map[string]interface{}) 37 | for i := 0; i < t.NumField(); i++ { 38 | jsonKey := t.Field(i).Tag.Get("gorm") 39 | jsonKey = strings.Split(jsonKey, ";")[0] 40 | jsonKey = strings.Split(jsonKey, ":")[1] 41 | if jsonKey != "-" { 42 | data[jsonKey] = v.Field(i).Interface() 43 | } 44 | } 45 | return data 46 | } 47 | 48 | // 检索条件 处理函数 49 | func getWhereStrByWhereOption[T any](tx *gorm.DB, whereOption *WhereOption[T]) *gorm.DB { 50 | 51 | // 判断结构体是否有值 52 | if &whereOption.QueryEntry != nil { 53 | tx = tx.Where(whereOption.QueryEntry) 54 | } 55 | if len(whereOption.QueryMap) > 0 { 56 | m := whereOption.QueryMap 57 | whereSql, values := mapCondition2whereSql(m) 58 | tx = tx.Where(whereSql, values...) 59 | } 60 | 61 | return tx 62 | } 63 | 64 | // 处理 排序 65 | func getOrderByWhereOption[T any](tx *gorm.DB, whereOption *WhereOption[T]) *gorm.DB { 66 | if whereOption.Order != "" { 67 | tx = tx.Order(whereOption.Order) 68 | } 69 | return tx 70 | } 71 | 72 | // map条件转whereSql 73 | func mapCondition2whereSql(m map[string]interface{}) (whereSql string, values []interface{}) { 74 | for key, value := range m { 75 | conditionKey := strings.Split(key, " ") 76 | KeyIndex := len(conditionKey) 77 | if KeyIndex > 2 { 78 | fmt.Println("map构建的条件格式不对,类似于`age >`(已舍弃)") 79 | continue 80 | } 81 | if whereSql != "" { 82 | whereSql += " AND " 83 | } 84 | switch KeyIndex { 85 | case 1: 86 | whereSql += fmt.Sprint(conditionKey[0], " = ?") 87 | values = append(values, value) 88 | break 89 | case 2: 90 | field := conditionKey[0] 91 | switch conditionKey[1] { 92 | case "=": 93 | whereSql += fmt.Sprint(field, " = ?") 94 | values = append(values, value) 95 | break 96 | case ">": 97 | whereSql += fmt.Sprint(field, " > ?") 98 | values = append(values, value) 99 | break 100 | case ">=": 101 | whereSql += fmt.Sprint(field, " >= ?") 102 | values = append(values, value) 103 | break 104 | case "<": 105 | whereSql += fmt.Sprint(field, " < ?") 106 | values = append(values, value) 107 | break 108 | case "<=": 109 | whereSql += fmt.Sprint(field, " <= ?") 110 | values = append(values, value) 111 | break 112 | case "in": 113 | whereSql += fmt.Sprint(field, " in (?)") 114 | values = append(values, value) 115 | break 116 | case "like": 117 | whereSql += fmt.Sprint(field, " like ?") 118 | values = append(values, value) 119 | break 120 | case "<>": 121 | whereSql += fmt.Sprint(field, " != ?") 122 | values = append(values, value) 123 | break 124 | case "!=": 125 | whereSql += fmt.Sprint(field, " != ?") 126 | values = append(values, value) 127 | break 128 | } 129 | break 130 | } 131 | } 132 | return 133 | } 134 | 135 | // 丢弃不属于该表字段 mapKey 避免报错 136 | func filterUserMapKey(m map[string]interface{}, fields []string) map[string]interface{} { 137 | newMap := make(map[string]interface{}) 138 | for _, field := range fields { 139 | if v, ok := m[field]; ok { 140 | // 存在 141 | newMap[field] = v 142 | } 143 | } 144 | return newMap 145 | } 146 | -------------------------------------------------------------------------------- /tpl/go/vo.template: -------------------------------------------------------------------------------- 1 | package {{ .Package }} 2 | 3 | {{if ne .TableInfo.Comment "" }}//{{ .TableInfo.Comment }}{{end}} 4 | type {{ .ModelStructName}}Vo struct { 5 | {{range $index, $field := .ModelFields}}{{ MysqlTableName2GolangStructName $field.Field}} {{ MysqlFiled2GolangType $field.Type}} `json:"{{ Hump2JsonField $field.Field}}"` {{if ne $field.Comment "" }}//{{ $field.Comment }}{{end}} 6 | {{end}}} 7 | 8 | -------------------------------------------------------------------------------- /tpl/ts/type.template: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{if ne .TableInfo.Comment "" }}//{{ .TableInfo.Comment }}{{end}} 4 | export interface I{{MysqlTableName2TsInterfaceName .TableInfo.Name}} { 5 | {{range $index, $field := .ModelFields}}{{ Hump2JsonField $field.Field}}:{{MysqlFiled2TsType $field.Type}}; {{if ne $field.Comment "" }}//{{ $field.Comment }}{{end}} 6 | {{end}} 7 | } 8 | 9 | export const new{{MysqlTableName2TsInterfaceName .TableInfo.Name}} = ():I{{MysqlTableName2TsInterfaceName .TableInfo.Name}} => { 10 | return { 11 | {{range $index, $field := .ModelFields}}{{ Hump2JsonField $field.Field}}:{{TsDefaultValue $field.Type }}, 12 | {{end}} 13 | } 14 | } -------------------------------------------------------------------------------- /utils/file.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "os" 7 | ) 8 | 9 | // GenerateFile 生成文件,参数为:文件路径名,文件内容,原有的是否覆盖(false为不覆盖) 10 | func GenerateFile(folder string, fileName string, content string, cover bool) { 11 | filePath := folder + fileName 12 | // 如果不存在就递归创建目录 13 | if !checkPathIsExist(folder) { 14 | err := os.MkdirAll(folder, os.ModePerm) 15 | if err != nil { 16 | fmt.Println(err) 17 | return 18 | } 19 | } 20 | 21 | var f *os.File 22 | var err error 23 | if checkPathIsExist(filePath) { 24 | if !cover { 25 | fmt.Println("文件: " + filePath + " 已存在(未覆盖)") 26 | return 27 | } 28 | f, err = os.OpenFile(filePath, os.O_WRONLY|os.O_TRUNC, 0666) //打开文件 29 | if err != nil { 30 | panic(err) 31 | } 32 | } else { 33 | f, err = os.Create(filePath) 34 | if err != nil { 35 | panic(err) 36 | } 37 | } 38 | defer f.Close() 39 | _, err = io.WriteString(f, content) 40 | if err != nil { 41 | panic(err) 42 | } 43 | fmt.Println("文件: ", filePath, " 已生成!") 44 | } 45 | 46 | // 检查文件是否存在 47 | func checkPathIsExist(filename string) bool { 48 | _, err := os.Stat(filename) 49 | if os.IsNotExist(err) { 50 | return false // 不存在 51 | } 52 | return true // 存在 53 | } 54 | -------------------------------------------------------------------------------- /utils/format.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "generate/config" 6 | "golang.org/x/text/cases" 7 | "golang.org/x/text/language" 8 | "log" 9 | "os/exec" 10 | "strings" 11 | ) 12 | 13 | // MysqlTableName2GolangStructName 数据库表名转结构体名 14 | func MysqlTableName2GolangStructName(str string) string { 15 | return FirstUpper(UnderlineToHump(str)) 16 | } 17 | 18 | // MysqlTableName2TsInterfaceName 数据库表面转ts 接口名 19 | func MysqlTableName2TsInterfaceName(str string) string { 20 | return MysqlTableName2GolangStructName(str) 21 | } 22 | 23 | // TsDefaultValue ts默认值 24 | func TsDefaultValue(str string) string { 25 | str = MysqlFiled2TsType(str) 26 | if str == "number" { 27 | return "0" 28 | } 29 | return "\"\"" 30 | } 31 | 32 | // UnderlineToHump 下划线转驼峰 33 | func UnderlineToHump(str string) string { 34 | newStr := strings.Replace(str, "_", " ", -1) 35 | newStr = cases.Title(language.English).String(newStr) 36 | newStr = strings.Replace(newStr, " ", "", -1) 37 | return newStr 38 | } 39 | 40 | // Hump2JsonField 驼峰转json字段 (首字母小写) 41 | func Hump2JsonField(str string) string { 42 | return FirstLower(UnderlineToHump(str)) 43 | } 44 | 45 | // FirstLower 字符串首字母小写 46 | func FirstLower(s string) string { 47 | if s == "" { 48 | return "" 49 | } 50 | return strings.ToLower(s[:1]) + s[1:] 51 | } 52 | 53 | // FirstUpper 字符串首字母大写 54 | func FirstUpper(s string) string { 55 | if s == "" { 56 | return "" 57 | } 58 | return strings.ToUpper(s[:1]) + s[1:] 59 | } 60 | 61 | // MysqlFiled2GolangType 数据库字段转go类型 62 | func MysqlFiled2GolangType(fieldType string) string { 63 | typeArr := strings.Split(fieldType, "(") 64 | switch typeArr[0] { 65 | case "int": 66 | return "int" 67 | case "integer": 68 | return "int" 69 | case "mediumint": 70 | return "int" 71 | case "bit": 72 | return "int" 73 | case "year": 74 | return "int" 75 | case "smallint": 76 | return "int" 77 | case "tinyint": 78 | return "int" 79 | case "bigint": 80 | return "int64" 81 | case "decimal": 82 | return "float32" 83 | case "double": 84 | return "float32" 85 | case "float": 86 | return "float32" 87 | case "real": 88 | return "float32" 89 | case "numeric": 90 | return "float32" 91 | case "timestamp": 92 | return "time.Time" 93 | case "datetime": 94 | return "time.Time" 95 | case "time": 96 | return "time.Time" 97 | default: 98 | return "string" 99 | } 100 | } 101 | 102 | // MysqlFiled2TsType 数据库字段转Ts类型 103 | func MysqlFiled2TsType(fieldType string) string { 104 | typeArr := strings.Split(fieldType, "(") 105 | switch typeArr[0] { 106 | case "int": 107 | return "number" 108 | case "integer": 109 | return "number" 110 | case "mediumint": 111 | return "number" 112 | case "bit": 113 | return "number" 114 | case "year": 115 | return "number" 116 | case "smallint": 117 | return "number" 118 | case "tinyint": 119 | return "number" 120 | case "bigint": 121 | return "number" 122 | case "decimal": 123 | return "number" 124 | case "double": 125 | return "number" 126 | case "float": 127 | return "number" 128 | case "real": 129 | return "number" 130 | case "numeric": 131 | return "number" 132 | case "timestamp": 133 | return "string" 134 | case "datetime": 135 | return "string" 136 | case "time": 137 | return "string" 138 | default: 139 | return "string" 140 | } 141 | } 142 | 143 | // GoFileFormat go文件格式化 144 | func GoFileFormat() { 145 | cmd := exec.Command("/bin/bash", "-c", "gofmt -s -w "+config.YamlConfig.ModelFileConfig.ModelPath+"*.go") 146 | out, err := cmd.CombinedOutput() 147 | if err != nil { 148 | log.Fatalf("cmd.Run() 格式化model文件失败 %s\n", err) 149 | } 150 | cmd = exec.Command("/bin/bash", "-c", "gofmt -s -w "+config.YamlConfig.ModelFileConfig.VoPath+"*.go") 151 | out, err = cmd.CombinedOutput() 152 | if err != nil { 153 | log.Fatalf("cmd.Run() 格式化vo文件失败 %s\n", err) 154 | } 155 | fmt.Printf("格式化文件成功!\n%s\n", string(out)) 156 | } 157 | -------------------------------------------------------------------------------- /utils/generate.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "bytes" 5 | "generate/config" 6 | "generate/mysql" 7 | "gorm.io/gorm" 8 | "strings" 9 | "text/template" 10 | ) 11 | 12 | type ModelFileType struct { 13 | Package string 14 | IsImportTime bool 15 | TableInfo Table 16 | Name string 17 | ModelFields []Field 18 | ModelStructName string //模型结构体名字 19 | IdType string // 模型 id的类型 20 | } 21 | type Table struct { 22 | Name string `gorm:"column:Name"` 23 | Comment string `gorm:"column:Comment"` 24 | IsFilterDelete bool 25 | DeleteFieldName string 26 | DeleteData int 27 | } 28 | 29 | type Field struct { 30 | Field string `gorm:"column:Field"` 31 | Type string `gorm:"column:Type"` 32 | Null string `gorm:"column:Null"` 33 | Key string `gorm:"column:Key"` 34 | Default string `gorm:"column:Default"` 35 | Extra string `gorm:"column:Extra"` 36 | Privileges string `gorm:"column:Privileges"` 37 | Comment string `gorm:"column:Comment"` 38 | } 39 | 40 | // CreateFiles 生成文件 41 | func CreateFiles(config config.Yaml) { 42 | tableNames := config.ModelFileConfig.TableName 43 | // 获取表单信息 44 | db := mysql.InitMysql() 45 | tables := getTables(db, tableNames, config.MysqlConfig.DBName) //生成所有表信息 46 | for _, table := range tables { 47 | fields := getFields(db, &table) 48 | // 生成model 49 | loadTemplate2GoModelFile(&config.ModelFileConfig, table, fields) 50 | 51 | // 生成vo文件 52 | loadTemplate2GoVoFile(&config.ModelFileConfig, table, fields) 53 | 54 | //生成ts 接口文件 55 | loadTemplate2TsTypeFile(&config.ModelFileConfig, table, fields) 56 | } 57 | 58 | //生成 model 工具文件 59 | loadTemplate2ModelToolFile(&config.ModelFileConfig) 60 | 61 | // 格式化go文件 62 | GoFileFormat() 63 | } 64 | 65 | // 读取模板引擎创建 Ts文件 66 | func loadTemplate2TsTypeFile(modelFileConfig *config.ModelFile, table Table, fields []Field) { 67 | //向模板注入函数 68 | funcMap := template.FuncMap{ 69 | "MysqlTableName2TsInterfaceName": MysqlTableName2TsInterfaceName, //表名转结构体名 70 | "MysqlFiled2TsType": MysqlFiled2TsType, //数据库类型转go类型 71 | "Hump2JsonField": Hump2JsonField, //下划线转驼峰 72 | "FirstLower": FirstLower, // 首字母小写 73 | "TsDefaultValue": TsDefaultValue, //ts根据类型设置默认值 74 | } 75 | modelTpl, err := template.New("type.template").Funcs(funcMap).ParseFiles("./tpl/ts/type.template") 76 | model := &ModelFileType{} 77 | model.Package = modelFileConfig.PackageName 78 | model.TableInfo = table 79 | model.ModelFields = fields 80 | //model.TsInterfaceName = utils.MysqlTableName2GolangStructName(model.TableInfo.Name) 81 | 82 | var fileBuffer bytes.Buffer 83 | err = modelTpl.Execute(&fileBuffer, model) 84 | if err != nil { 85 | panic(err) 86 | } 87 | fileName := FirstLower(UnderlineToHump(table.Name)) 88 | GenerateFile(modelFileConfig.TypePath, fileName+"Type.ts", fileBuffer.String(), modelFileConfig.IsCover) 89 | } 90 | 91 | // 生成model格式化文件 92 | func loadTemplate2ModelToolFile(modelFileConfig *config.ModelFile) { 93 | modelTpl, _ := template.New("modelTool.template").ParseFiles("./tpl/go/modelTool.template") 94 | data := make(map[string]interface{}) 95 | data["packageName"] = config.YamlConfig.ModelFileConfig.PackageName 96 | var fileBuffer bytes.Buffer 97 | _ = modelTpl.Execute(&fileBuffer, data) 98 | GenerateFile(modelFileConfig.ModelPath, "modelTool.go", fileBuffer.String(), modelFileConfig.IsCover) 99 | } 100 | 101 | // 读取模板引擎创建 go文件 102 | func loadTemplate2GoVoFile(modelFileConfig *config.ModelFile, table Table, fields []Field) { 103 | //向模板注入函数 104 | funcMap := template.FuncMap{ 105 | "MysqlTableName2GolangStructName": MysqlTableName2GolangStructName, //表名转结构体名 106 | "MysqlFiled2GolangType": MysqlFiled2GolangType, //数据库类型转go类型 107 | "Hump2JsonField": Hump2JsonField, //下划线转驼峰 108 | "FirstLower": FirstLower, // 首字母小写 109 | } 110 | modelTpl, err := template.New("vo.template").Funcs(funcMap).ParseFiles("./tpl/go/vo.template") 111 | model := &ModelFileType{} 112 | model.Package = modelFileConfig.PackageName 113 | model.TableInfo = table 114 | model.ModelFields = fields 115 | model.ModelStructName = MysqlTableName2GolangStructName(model.TableInfo.Name) 116 | 117 | var fileBuffer bytes.Buffer 118 | err = modelTpl.Execute(&fileBuffer, model) 119 | if err != nil { 120 | panic(err) 121 | } 122 | fileName := FirstLower(UnderlineToHump(table.Name)) 123 | GenerateFile(modelFileConfig.VoPath, fileName+"Vo.go", fileBuffer.String(), modelFileConfig.IsCover) 124 | } 125 | 126 | // 读取模板引擎创建 go model文件 127 | func loadTemplate2GoModelFile(modelFileConfig *config.ModelFile, table Table, fields []Field) { 128 | //向模板注入函数 129 | funcMap := template.FuncMap{ 130 | "MysqlTableName2GolangStructName": MysqlTableName2GolangStructName, //表名转结构体名 131 | "MysqlFiled2GolangType": MysqlFiled2GolangType, //数据库类型转go类型 132 | "Hump2JsonField": Hump2JsonField, //下划线转驼峰 133 | "FirstLower": FirstLower, // 首字母小写 134 | } 135 | modelTpl, err := template.New("model.template").Funcs(funcMap).ParseFiles("./tpl/go/model.template") 136 | model := &ModelFileType{} 137 | model.Package = modelFileConfig.PackageName 138 | for _, field := range fields { 139 | // 如果包含 datetime 则导入时间包 140 | if field.Type == "datetime" { 141 | model.IsImportTime = true 142 | break 143 | } 144 | } 145 | for _, field := range fields { 146 | if field.Field == "id" { 147 | model.IdType = field.Type 148 | break 149 | } 150 | } 151 | model.TableInfo = table 152 | model.ModelFields = fields 153 | model.ModelStructName = MysqlTableName2GolangStructName(model.TableInfo.Name) 154 | 155 | var fileBuffer bytes.Buffer 156 | err = modelTpl.Execute(&fileBuffer, model) 157 | if err != nil { 158 | panic(err) 159 | } 160 | fileName := FirstLower(UnderlineToHump(table.Name)) 161 | GenerateFile(modelFileConfig.ModelPath, fileName+"Model.go", fileBuffer.String(), modelFileConfig.IsCover) 162 | } 163 | 164 | // 获取具体表单 165 | func getTables(db *gorm.DB, tableNames []string, dbName string) []Table { 166 | 167 | // 字符串拼接生成表名范围 168 | tableNamesStr := "'" + strings.Join(tableNames, "','") + "'" 169 | 170 | // 获取指定表信息 171 | var tables []Table 172 | if tableNamesStr == "''" { 173 | db.Raw("SELECT TABLE_NAME as Name,TABLE_COMMENT as Comment FROM information_schema.TABLES " + 174 | "WHERE table_schema='" + dbName + "';").Find(&tables) 175 | } else { 176 | db.Raw("SELECT TABLE_NAME as Name,TABLE_COMMENT as Comment FROM information_schema.TABLES " + 177 | "WHERE TABLE_NAME IN (" + tableNamesStr + ") AND " + 178 | "table_schema='" + dbName + "';").Find(&tables) 179 | } 180 | return tables 181 | } 182 | 183 | // 获取字段的详情信息 184 | func getFields(db *gorm.DB, table *Table) (fields []Field) { 185 | db.Raw("show FULL COLUMNS from " + table.Name + ";").Find(&fields) 186 | return 187 | } 188 | --------------------------------------------------------------------------------