前言
利用模版生成,来对常见的重复的编码,进行一键生成。
使用代码生成,解决的是以下问题:
- 大幅度减少开发时间,将常见工作过程流水化。降低新人使用门槛。
- 减少后台和前端的对接时间。
- 减少重复劳动里容易出错的概率。
- 代码风格统一。
代码生成最常见的业务场景:
- CRUD
- 业务缓存
- xml转go
- json转go
- protobuf转go
- proto-rpc转go
思维导图
1. 业务流程
1.1 生成业务模型与缓存支持
import mc "github.com/fwhezfwhez/model_convert"
func TestTableToStructWithTag(t *testing.T) {
dataSouce := fmt.Sprintf("host=%s port=%s user=%s dbname=%s sslmode=%s password=%s", "localhost", "5432", "postgres", "game", "disable", "123")
tableName := "match_info"
fmt.Println(mc.TableToStructWithTag(dataSouce, tableName, map[string]interface{}{
"dialect": "postgres",
"${db_instance}": "db.DB",
"${db_instance_pkg}": "path/to/db",
}))
}
1.2 生成后台增删改查
import mc "github.com/fwhezfwhez/model_convert"
func TestGenerateCRUD(t *testing.T) {
type MatchInfo struct {
Id int `gorm:"column:id;default:" json:"id" form:"id"`
UpdatedAt time.Time `gorm:"column:updated_at;default:" json:"updated_at" form:"updated_at"`
CreatedAt time.Time `gorm:"column:created_at;default:" json:"created_at" form:"created_at"`
ClubId int `gorm:"column:club_id;default:" json:"club_id" form:"club_id"`
Description string `gorm:"column:description;default:" json:"description" form:"description"`
GameId int `gorm:"column:game_id;default:" json:"game_id" form:"game_id"`
GameAreaId int `gorm:"column:game_area_id;default:" json:"game_area_id" form:"game_area_id"`
RangeProps json.RawMessage `gorm:"column:range_props;default:" json:"range_props" form:"range_props"`
}
rs := mc.GenerateCRUD(MatchInfo{}, map[string]string{
"${generate_to_pkg}": "matchControl",
"${model}": "matchModel.MatchInfo",
"${handle_error}": "commonv2.SaveError(errorx.Wrap(e))",
"${db_instance}": "(matchModel.MatchInfo{}).DB()",
})
_ = rs
fmt.Println(rs)
}
1.3 生成接口文档
import mc "github.com/fwhezfwhez/model_convert"
func TestGenerateMD2(t *testing.T) {
type ShopPropBackupRecord struct {
Id int `gorm:"column:id;default:" json:"id" form:"id"`
CreatedAt time.Time `gorm:"column:created_at;default:" json:"created_at" form:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;default:" json:"updated_at" form:"updated_at"`
ItemKey string `gorm:"column:item_key;default:" json:"item_key" form:"item_key"`
Decription string `gorm:"column:decription;default:" json:"decription" form:"decription"`
Title string `gorm:"column:title;default:" json:"title" form:"title"`
DailyNum int `gorm:"column:daily_num;default:" json:"daily_num" form:"daily_num"`
LifetimeNum int `gorm:"column:lifetime_num;default:" json:"lifetime_num" form:"lifetime_num"`
}
rs := mc.GenerateMarkDown(ShopPropBackupRecord{}, map[string]interface{}{
"${model_chinese_name}": "库存信息",
"${md_order}": 1,
})
fmt.Println(rs)
}
1.4 业务缓存
- 配置缓存key与失效时间
var MatchInfoRedisKeyFormat = "xyx:match_info:%s:%d"
func (o MatchInfo) RedisKey() string {
// TODO set its redis key and required args
return fmt.Sprintf(MatchInfoRedisKeyFormat, config.Mode, o.ClubId)
}
func (o MatchInfo) RedisSecondDuration() int {
// TODO set its redis duration, default 1-7 day, return -1 means no time limit
return int(time.Now().Unix()%7+1) * 60
}
- 在缓存机制下,获取可靠的数据结果
func GetMatchInfo(clubId int, conn redis.Conn) (matchModel.MatchInfo, error) {
var m = matchModel.MatchInfo{
ClubId: clubId,
}
if conn == nil {
conn = redistool.RedisPool.Get()
defer conn.Close()
}
engine := m.DB().Model(m).Where("club_id=?", clubId)
if e := m.MustGet(conn, engine); e != nil {
if e!=redis.ErrNil && e.Error() != "not found record in db nor redis" {
return m, errorx.Wrap(e)
}
// not found this record, then do init or return errr
// - m.Init()
// - return m, errorx.Wrap(e)
return m, errorx.Wrap(e)
}
return m, nil
}
- 获取缓存数组(不演示)
arr, e:=ArrayMustGet(conn, engine)
演示示例:
https://gitee.com/fwhezfwhez/auto-generate-example
2. 其他生成
- JSON转go model
- XML转go model
- protobuf.Message 与 go model 转化
- proto.Server 转 go实例
接口文档一定要写出请求参数和返回结果的可用示例,这样可以方便对接人,通过代码生成来产出无错结构体。不要期望别人一个个对你的字段, 合格的文档最少要有[body, response]:
body:
{
"game_id":999,
"user_id":10077
}
返回:
{
"errmsg":"",
"errcode": 0,
"data": {
}
}
结语
代码生成,是业务迭代的起点,它的作用类似于vue-cli, angular-cli,在一个业务开始输出以前,它就应该存在在那。如果不了解自动生成的人,在达到这个起点时,可能就会走很多弯路:
- 手写model。成本与风险: 写错,拼错,tag魔法,tag敲错,字段对比。
- 手写增删改查。成本与风险: 写的多,功能却不一定足,需要开发联调单元测试,代码风格还不同。
要合理使用代码生成,让你的项目能够更快速的启动,团队风格统一,使用简单。