解决方案(5) 代码生成

前言

利用模版生成,来对常见的重复的编码,进行一键生成。
使用代码生成,解决的是以下问题:

  • 大幅度减少开发时间,将常见工作过程流水化。降低新人使用门槛。
  • 减少后台和前端的对接时间。
  • 减少重复劳动里容易出错的概率。
  • 代码风格统一。

代码生成最常见的业务场景:

  • 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敲错,字段对比。
  • 手写增删改查。成本与风险: 写的多,功能却不一定足,需要开发联调单元测试,代码风格还不同。

要合理使用代码生成,让你的项目能够更快速的启动,团队风格统一,使用简单。

猜你喜欢

转载自blog.csdn.net/fwhezfwhez/article/details/114880495