最近在用Go 的gin框架重构之前PHP开发的老项目,由于基础不扎实,打算从零开始进行开发,在写Model和Service时感觉效率比较低,并且大量代码重复,所以就自己用template模板来实现代码的生成。

其中遇到一些问题,记录一下。

一:在使用模板时不能使用模板定义标签如

{{define "index/index.html"}}

{{end}}

上述标签如果存在则获取不到模板数据

代码生成模板的正确写法如下:

//model.tmpl
package model

import (
	"fmt"
	"gin-icqqg/config"
	"github.com/gin-gonic/gin"
)

//{{.Model}} {{.Name}}表
type {{.Model}} struct {
	ID         int64      `gorm:"column:id;primaryKey;autoIncrement;type:int(11);" json:"id,omitempty"`
	{{- range $i,$v:=.Filed}}
	{{$v.Name}}       {{$v.Type}}     `gorm:"column:{{$v.Column}};type:{{$v.SqlType}}({{$v.Size}});" json:"{{$v.Column}},omitempty"` //{{$v.comment}}
	{{- end}}
	CreatedAt  *LocalTime `gorm:"column:created_at" json:"created_at,omitempty"`
	UpdatedAt  *LocalTime `gorm:"column:updated_at" json:"updated_at,omitempty"`
	DeletedAt  *LocalTime `gorm:"column:deleted_at" json:"-,omitempty"`
}

type Add{{.Model}} struct {
    {{- range $i,$v:=.Filed }}
    {{$v.Name}}       {{$v.Type}}     `form:"{{$v.Column}}" json:"{{$v.Column}},omitempty"`//{{$v.Comment}}
    {{- end}}

}
func New{{.Model}}() *{{.Model}} {
    //自动迁移
	db.AutoMigrate(&{{.Model}}{})
	if !db.Migrator().HasTable(&{{.Model}}{}) {
        //创建表
		db.Set("gorm:ENGINE", "InnoDB").Migrator().CreateTable(&{{.Model}}{})
	}
	return &{{.Model}}{}
}
func (m *{{.Model}}) TableName() string {
	return "{{.TableName}}"
}

//List 获取{{.Name}}列表展示的
//get
func (m *{{.Model}}) List(c *gin.Context) {
	var {{.Model}}List []{{.Model}}
	err := db.Model(&{{.Model}}{}).Find(&{{.Model}}List).Error
	if err != nil {
		config.ErrorLog(fmt.Sprintf("%v", err))
		c.JSON(200, gin.H{"code": 500, "msg": "发送错误"})
	} else {
		c.JSON(200, gin.H{"code": 200, "data": {{.Model}}List})
	}
	c.Abort()
}

//Add{{.Model}} 新增{{.Name}}
//post
func (m *{{.Model}}) Add{{.Model}}( c *gin.Context) {
	var add Add{{.Model}}
	err := c.ShouldBind(&add)
	{{- range $i,$v:=.Filed }}
	 m.{{$v.Name}} = add.{{$v.Name}}
	{{- end}}
	err = db.Model(&{{.Model}}{}).Create(&m).Error
	if err != nil {
		config.ErrorLog(fmt.Sprintf("%v", err))
		c.JSON(200, gin.H{"code": 500, "msg": "failed"})
	} else {
		c.JSON(200, gin.H{"code": 200, "msg": "success"})
	}
	c.Abort()
}


//Delete{{.Model}} 通过ID删除{{.Name}}
//delete
func (m *{{.Model}}) Delete{{.Model}}(id string, c *gin.Context) {
	err := db.Model(&{{.Model}}{}).Where("id = ?", id).Delete(m).Error
	if err != nil {
		config.ErrorLog(fmt.Sprintf("%v", err))
		c.JSON(200, gin.H{"code": 500, "msg": "failed"})
	} else {
		c.JSON(200, gin.H{"code": 200, "msg": "success"})
	}
	c.Abort()
}

//Update{{.Model}} 修改{{.Name}}
//put
func (m *{{.Model}}) Update{{.Model}}(id string,c *gin.Context) {
    var add Add{{.Model}}
	err := c.ShouldBind(&add)
	{{- range $i,$v:=.Filed }}
	 m.{{$v.Name}} = add.{{$v.Name}}
	{{- end}}
    if err != nil {
    		config.ErrorLog(fmt.Sprintf("%v", err))
    		c.JSON(200, gin.H{"code": 500, "msg": "failed"})
    }else{
        err := db.Model(&{{.Model}}{}).Where("id = ?", id).Updates(m).Error
        if err != nil {
            config.ErrorLog(fmt.Sprintf("%v", err))
            c.JSON(200, gin.H{"code": 500, "msg": "failed"})
        } else {
            c.JSON(200, gin.H{"code": 200, "msg": "success"})
        }
    }

	c.Abort()
}

//GetOne 通过ID获取信息
//get
func (m *{{.Model}}) GetOne(id string, c *gin.Context) {
	err := db.Model(&{{.Model}}{}).Where("id = ?", id).First(m).Error
	if err != nil {
		config.ErrorLog(fmt.Sprintf("%v", err))
		c.JSON(200, gin.H{"code": 500, "msg": "failed"})
	} else {
		c.JSON(200, gin.H{"code": 200, "data": m})
	}
	c.Abort()
}

接下来的是生成Service代码

//service.tmpl 代码
package web

import (
	"gin-icqqg/model"
	"github.com/gin-gonic/gin"
)

type {{.Model}} struct {
{{- range $i,$v:=.Filed}}
    {{$v.Name}}       {{$v.Type}}     `form:"{{$v.Column}}" json:"{{$v.Column}},omitempty"`    //{{$v.Comment}}
{{- end}}
}

func New{{.Model}}() *{{.Model}} {
	return &{{.Model}}{}
}

//List 数据列表
//@Tags {{.Title}}模块
//@Summary {{.Name}}列表
//@Param token header string true "token"
//@Produce json
// @Success 200 {object} []model.{{.Model}} "{"code":200,"data":[]model.{{.Model}}}"
// @Failure 400 {object} response.Code "请求错误"
// @Failure 500 {object} response.Code "内部错误"
//@Router /api/web/{{.Path}}List [get]
func (a *{{.Model}}) List(c *gin.Context) {
	service := model.New{{.Model}}()
	service.List(c)
	return
}

//Get 通过ID获取角色信息
//@Tags {{.Title}}模块
//@Summary 获取{{.Name}}信息
//@Param token header string true "token"
//@Param id path int true "ID"
//@Produce json
// @Success 200 {object} model.{{.Model}} "{"code":200,"data":model.{{.Model}}}"
// @Failure 400 {object} response.Code "请求错误"
// @Failure 500 {object} response.Code "内部错误"
//@Router /api/web/{{.Path}}/{id} [get]
func (a *{{.Model}}) Get(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(400, gin.H{"code": 400, "msg": "参数有误"})
	} else {
		service := model.New{{.Model}}()
		service.GetOne(id, c)
	}
	return
}

//Add 新增{{.Name}}信息
//@Tags {{.Title}}模块
//@Summary 添加{{.Name}}信息
//@Param token header string true "token"
{{- range $i,$v:=.Filed}}
//@Param {{$v.Column}} formData {{$v.Type}} true "{{$v.Comment}}"
{{- end}}
//@Produce json
// @Success 200 {object} response.Code "{"code":200,"msg":"success"}"
// @Failure 400 {object} response.Code "请求错误"
// @Failure 500 {object} response.Code "内部错误"
//@Router /api/web/{{.Path}} [post]
func (a *{{.Model}}) Add(c *gin.Context) {
    service := model.New{{.Model}}()
    service.Add{{.Model}}(c)
	return
}

//Edit 修改{{.Name}}信息
//@Tags {{.Title}}模块
//@Summary 修改{{.Name}}信息
//@Param token header string true "token"
{{- range $i,$v:=.Filed}}
//@Param {{$v.Column}} formData {{$v.Type}} true "{{$v.Comment}}"
{{- end}}
//@Produce json
// @Success 200 {object} response.Code "{"code":200,"msg":"success"}"
// @Failure 400 {object} response.Code "请求错误"
// @Failure 500 {object} response.Code "内部错误"
//@Router /api/web/{{.Path}}/{id}/edit [put]
func (a *{{.Model}}) Edit(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(400, gin.H{"code": 400, "msg": "参数有误"})
		c.Abort()
		return
	}
    service := model.New{{.Model}}()
    service.Update{{.Model}}(id,c)
	return
}

//Delete 删除{{.Name}}信息
//@Tags {{.Title}}模块
//@Summary 删除{{.Name}}信息
//@Param token header string true "token"
//@Param id path int true "ID"
//@Produce json
// @Success 200 {object} response.Code "{"code":200,"msg":"success"}"
// @Failure 400 {object} response.Code "请求错误"
// @Failure 500 {object} response.Code "内部错误"
//@Router /api/web/{{.Path}}/{id} [delete]
func (a *{{.Model}}) Delete(c *gin.Context) {
	id := c.Param("id")
	if id == "" {
		c.JSON(400, gin.H{"code": 400, "msg": "参数有误"})
		c.Abort()
		return
	}
	service := model.New{{.Model}}()
	service.Delete{{.Model}}(id, c)
	return
}

接下来解析模板并且将文件保存到指定文件夹中:

func Index(c *gin.Context) {

	Model := "Product"
	var Filed []map[string]interface{}
	list1 := map[string]interface{}{"Name": "Name", "Type": "string", "Column": "name", "SqlType": "varchar", "Size": "50", "Comment": "名称"}
	list2 := map[string]interface{}{"Name": "Title", "Type": "string", "Column": "title", "SqlType": "varchar", "Size": "50", "Comment": "标题"}
	list3 := map[string]interface{}{"Name": "Version", "Type": "string", "Column": "version", "SqlType": "varchar", "Size": "50", "Comment": "型号"}
	Filed = append(Filed, list1, list2, list3)
	Data := map[string]interface{}{
		"Model":     Model,
		"Filed":     Filed,
		"TableName": "table_product",
		"Name":      "产品",
		"Title":     "后台产品",
		"Path":      "product",
	}

	tempModel, _ := template.New("model.tmpl").ParseFiles("resource/view/admin/model.tmpl")
	tempService, _ := template.New("service.tmpl").ParseFiles("resource/view/admin/service.tmpl")

	var ModelBytes bytes.Buffer
	var ServiceBytes bytes.Buffer
	if err := tempModel.Execute(&ModelBytes, Data); err != nil {
		c.String(http.StatusInternalServerError, fmt.Sprintf("Error executing template: %v", err))
		return
	}
	if err := tempService.Execute(&ServiceBytes, Data); err != nil {
		c.String(http.StatusInternalServerError, fmt.Sprintf("Error executing template: %v", err))
		return
	}
	err := ioutil.WriteFile("model/"+strings.ToLower(Model)+".go", ModelBytes.Bytes(), 0777)
	err = ioutil.WriteFile("api/web/"+strings.ToLower(Model)+".go", ServiceBytes.Bytes(), 0777)
	if err != nil {
		config.ErrorLog(fmt.Sprintf("%v", err))
	}
	c.JSON(http.StatusOK, gin.H{"code": 200, "msg": "模板输出成功"})

}

上述模板文件集成了swagger Api文档注解,控制台swag init 即可生成对应的接口文档。

经过上述三个操作,代码已经生成了,访问时直接在路由文件中添加对应的路由地址即可。