一文教你用pdmaner生成代码(1)

3,978 阅读6分钟

昨天在技术交流群聊到快速生成CRUD代码时,看到别人用的powerdesign只能生成java的代码,没办法生成前端代码,所有我就在想能不能把前端代码也生成一下。我就想起了有个国产的powerdesign平替开源软件——pdmaner,可以直接导入powerdesign的pdm文件,也支持自定义代码生成模板。我就下来玩了一下,然后就有了下面的文章。

本片先实现后端部分的代码生成,后端使用我比较熟悉的Goframe框架,配合框架自带的代码生成,快速实现一下接口。

下面开始展示按照模板生成代码的思路吧,这里的重点不在于使用哪个框架而已理解代码生成的思路,只要理解了思路,不管是前后端项目还是别的重复度高的项目,都能用这个思路去减少你的工作时间哦。

快速开始

如果有兴趣了解学习一下GoFrame框架,可以先看看文档介绍哦!

首先是安装gf-cli工具

找个位置开始创建项目gf init pdmaner-test

配置一下数据库连接,manifest/config/config.yaml

server:
  address:     ":8000"
  openapiPath: "/api.json"
  swaggerPath: "/swagger"

logger:
  level : "all"
  stdout: true

database:
  default:
    link:  "mysql:root:123456@tcp(127.0.0.1:3306)/pdmaner_test"
    debug: true

给gf-cli也配置一下,用于生成dao代码,hack/config.yaml:

gfcli:
  docker:
    build: "-a amd64 -s linux -p temp -ew"
    tagPrefixes:
      - my.image.pub/my-app
  gen:
    dao:
      - link: "mysql:root:123456@tcp(127.0.0.1:3306)/pdmaner_test"

建库建表

这里展示库我们用pdmaner的示例库的图书目录进行演示

image.png

建表命令:

DROP TABLE IF EXISTS lbra_catalog;
CREATE TABLE lbra_catalog(
    `id` INT(11) NOT NULL AUTO_INCREMENT  COMMENT '分类ID' ,
    `catalog_name` VARCHAR(90) NOT NULL   COMMENT '分类名' ,
    `catalog_desc` VARCHAR(900)    COMMENT '分类描述' ,
    `sort_no` INT    COMMENT '排序号' ,
    `demo_time` DATETIME    COMMENT '演示时间选择' ,
    `created_by` VARCHAR(32)    COMMENT '创建人' ,
    `created_at` DATETIME    COMMENT '创建时间' ,
    `updated_by` VARCHAR(32)    COMMENT '更新人' ,
    `updated_at` DATETIME    COMMENT '更新时间' ,
    `deleted_by` VARCHAR(32)    COMMENT '删除人' ,
    `deleted_at` DATETIME    COMMENT '删除时间' ,
    PRIMARY KEY (id)
)  COMMENT = '图书分类';

修改下数据类型,因为我们用的是gf框架,推荐用框架自带的gtime包的Time

image.png

代码生成

模板介绍

pdmaner的代码生成模板使用的是dot包,是一个基于JavaScript语言的模板生成工具,所以需要先了解一下模板的基础语法,掘金里也有dot的使用文章

pdmaner补充了一些常用的工具函数,比如驼峰转下划线,下划线转驼峰等等:

image.png

生成dao代码

因为dao代码基本所有项目都差不多相同,所以直接用gf-cli生成,输入:gf gen dao

然后就能看到生成了dao、do、entity代码了。

生成model代码

model代码主要是一些结构体的定义,用于controller层向service层传递数据,这部分通常但是我们有pdmaner,下面是我演示用的模板代码,可以参考一下,首先是创建模板

image.png

然后这是我的模板代码:

{{
    var today=new Date();
    var fullYear=today.getFullYear();
    var month=today.getMonth() + 1<10?"0"+today.getMonth():today.getMonth();
    var days=today.getDate()<10?"0"+today.getDate():today.getDate();
    var hours = today.getHours()<10?"0"+today.getHours():today.getHours();         
	var minutes = today.getMinutes()<10?"0"+today.getMinutes():today.getMinutes();      
	var seconds = today.getSeconds()<10?"0"+today.getSeconds():today.getSeconds();
	var filterFields = [
	  "id",
      "created_at",
      "created_by",
      "updated_at",
      "updated_by",
      "deleted_at",
      "deleted_by",
    ];
    var fields = it.entity.fields.filter((item) => !filterFields.includes(item.defKey));
}}// Package model {{=it.func.join(it.entity.defName,it.entity.comment,',')}}
// author : https://github.com/Wyatex
// date : {{=fullYear}}-{{=month}}-{{=days}} {{=hours}}:{{=minutes}}
// desc : {{=it.func.join(it.entity.defName,it.entity.comment,',')}} controller - service 传输数据类型定义
package model

$blankline

import (
	"github.com/gogf/gf/v2/os/gtime"
	"pdmaner-test/internal/model/entity"
)

$blankline

// {{=it.func.camel(it.entity.defKey,true)}}AddInput 添加{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
type {{=it.func.camel(it.entity.defKey,true)}}AddInput struct {
    {{~fields:field:index}}
    {{=it.func.camel(field.defKey,true)}} {{=field.type}} // {{=field.defName}}
    {{~}}
    CreatedBy string // 创建者
}

$blankline

// {{=it.func.camel(it.entity.defKey,true)}}FindPageInput 分页查找{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
type {{=it.func.camel(it.entity.defKey,true)}}FindPageInput struct {
    {{~fields:field:index}}
    {{=it.func.camel(field.defKey,true)}} {{=field.type}} // {{=field.defName}}
    {{~}}
    CreatedStart *gtime.Time // 搜索创建时间范围
    CreatedEnd *gtime.Time // 搜索创建时间范围
    UpdatedStart *gtime.Time // 搜索更新时间范围
    UpdatedEnd *gtime.Time // 搜索更新时间范围
    PageNo int // 查找第几页
    PageItem int // 每页个数
}

$blankline

// {{=it.func.camel(it.entity.defKey,true)}}FindPageOutput 分页查找{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
type {{=it.func.camel(it.entity.defKey,true)}}FindPageOutput struct {
    List      []entity.{{=it.func.camel(it.entity.defKey,true)}}
    ItemCount int // 搜索结果有多少个
    PageCount int // 搜索结果有多少页
    PageNo int // 查找第几页
    PageItem int // 每页个数
}

$blankline

// {{=it.func.camel(it.entity.defKey,true)}}FindListInput 全部查找{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
type {{=it.func.camel(it.entity.defKey,true)}}FindListInput struct {
    {{~fields:field:index}}
    {{=it.func.camel(field.defKey,true)}} {{=field.type}} // {{=field.defName}}
    {{~}}
    CreatedStart *gtime.Time // 搜索创建时间范围
    CreatedEnd *gtime.Time // 搜索创建时间范围
    UpdatedStart *gtime.Time // 搜索更新时间范围
    UpdatedEnd *gtime.Time // 搜索更新时间范围
}

这里我们假设创建、搜索是不需要id、xxx_at、xxx_by的,所以我们过滤一下这几个字段,然后像分页需要传分页参数等等,按需求进行编写。

然后回到代码生成界面就能看到我们的代码了:

image.png

至于代码格式化,就交给gofmt吧,在goland设置里设置成自动对代码进行gofmt格式化就行了。

生成logic代码

这里的logic类似springboot里的ServiceImpl代码,需要我们手写,但是我们依然用pdmaner生成,直接上模板:

{{
    var today=new Date();
    var fullYear=today.getFullYear();
    var month=today.getMonth() + 1<10?"0"+today.getMonth():today.getMonth();
    var days=today.getDate()<10?"0"+today.getDate():today.getDate();
    var hours = today.getHours()<10?"0"+today.getHours():today.getHours();         
	var minutes = today.getMinutes()<10?"0"+today.getMinutes():today.getMinutes();      
	var seconds = today.getSeconds()<10?"0"+today.getSeconds():today.getSeconds();    
	var camelName = it.func.camel(it.entity.defKey,true);
	var filterFields = [
	  "id",
      "created_at",
      "created_by",
      "updated_at",
      "updated_by",
      "deleted_at",
      "deleted_by",
    ];
    var fields = it.entity.fields.filter((item) => !filterFields.includes(item.defKey));
}}// Package {{=it.entity.defKey}} {{=it.func.join(it.entity.defName,it.entity.comment,',')}}
// author : https://github.com/Wyatex
// date : {{=fullYear}}-{{=month}}-{{=days}} {{=hours}}:{{=minutes}}
// desc : {{=it.func.join(it.entity.defName,it.entity.comment,',')}} logic代码
package {{=it.entity.defKey}}

$blankline

import (
	"context"
	"math"
	"pdmaner-test/internal/dao"
	"pdmaner-test/internal/model"
	"pdmaner-test/internal/model/entity"
	"pdmaner-test/internal/service"
)

$blankline

type s{{=camelName}} struct{}

$blankline

func init() {
	service.Register{{=camelName}}(New())
}

$blankline

func New() *s{{=camelName}} {
	return &s{{=camelName}}{}
}

$blankline

// Add 添加一个{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) Add(ctx context.Context, in *model.{{=camelName}}AddInput) error {
    uid := service.BizCtx().Get(ctx).User.Id
	in.CreatedBy = uid
	_, err := dao.{{=camelName}}.Ctx(ctx).Insert(in)
	return err
}

$blankline

// Edit 编辑{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) Edit(ctx context.Context, in *entity.{{=camelName}}) error {
    uid := service.BizCtx().Get(ctx).User.Id
	in.UpdatedBy = uid
	_, err := dao.{{=camelName}}.Ctx(ctx).Update(in, "id", in.Id)
	return err
}

$blankline

// Delete 删除{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) Delete(ctx context.Context, id *int, ids []int) (err error) {
    uid := service.BizCtx().Get(ctx).User.Id
	if id != nil {
		_, err = dao.{{=camelName}}.Ctx(ctx).Data(g.Map{"deleted_by" : uid}).Where("id", id).Update()
	} else {
		_, err = dao.{{=camelName}}.Ctx(ctx).Data(g.Map{"deleted_by" : uid}).WhereIn("id", ids).Update()
	}
	if err != nil {
		return err
	}
	if id != nil {
		_, err = dao.{{=camelName}}.Ctx(ctx).Where("id", id).Delete()
	} else {
		_, err = dao.{{=camelName}}.Ctx(ctx).WhereIn("id", ids).Delete()
	}
	return err
}

$blankline

// FindOne 根据id查找{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) FindOne(ctx context.Context, id int) (*entity.{{=camelName}}, error) {
	var m *entity.{{=camelName}}
	err := dao.{{=camelName}}.Ctx(ctx).Where("id", id).Scan(&m)
	return m, err
}

$blankline

// FindPage 分页查询{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) FindPage(ctx context.Context, in *model.{{=camelName}}FindPageInput) (*model.{{=camelName}}FindPageOutput, error) {
	orm := dao.{{=camelName}}.Ctx(ctx).Safe(false)
	items := make([]entity.{{=camelName}}, 0)
	total := 0{{~fields:field:index}}
	{{
	    let fieldCamelName = it.func.camel(field.defKey,true);
	    let fieldType = field.type;
	    let condition = 'nil';
	    let whereCondition = 'Where';
	    let whereData = 'in.' + fieldCamelName;
	    if (fieldType === 'string') {
	        condition = '""';
	        whereCondition = 'WhereLike';
	        whereData = '"%"+' + whereData + '+"%"';
	    }
	}}if in.{{=it.func.camel(field.defKey,true)}} != {{=condition}} {
        orm.{{=whereCondition}}(dao.{{=camelName}}.Columns().{{=fieldCamelName}}, {{=whereData}})
    }
    {{~}}
    if in.CreatedStart != nil {
		orm.WhereGTE(dao.LbraCatalog.Columns().CreatedAt, in.CreatedStart)
	}
	if in.CreatedEnd != nil {
		orm.WhereLT(dao.LbraCatalog.Columns().CreatedAt, in.CreatedEnd)
	}
	if in.UpdatedStart != nil {
		orm.WhereGTE(dao.LbraCatalog.Columns().UpdatedAt, in.UpdatedStart)
	}
	if in.UpdatedEnd != nil {
		orm.WhereLT(dao.LbraCatalog.Columns().UpdatedAt, in.UpdatedEnd)
	}
	err := orm.Limit((in.PageNo-1)*in.PageItem, in.PageItem).
		ScanAndCount(&items, &total, true)
	if err != nil {
		return nil, err
	}
	return &model.{{=camelName}}FindPageOutput{
		List:      items,
		ItemCount: total,
		PageCount: int(math.Ceil(float64(total) / float64(in.PageItem))),
		PageItem:  in.PageItem,
		PageNo:    in.PageNo,
	}, nil
}

$blankline

// FindList 查找所有{{=it.func.join(it.entity.defName,it.entity.comment,',')}}
func (s *s{{=camelName}}) FindList(ctx context.Context, in *model.{{=camelName}}FindListInput) ([]entity.{{=camelName}}, error) {
	orm := dao.{{=camelName}}.Ctx(ctx).Safe(false)
	items := make([]entity.{{=camelName}}, 0){{~fields:field:index}}
	{{
	    let fieldCamelName = it.func.camel(field.defKey,true);
	    let fieldType = field.type;
	    let condition = 'nil';
	    let whereCondition = 'Where';
	    if (fieldType === 'string') {
	        condition = '""';
	        whereCondition = 'WhereLike';
	    }
	}}if in.{{=it.func.camel(field.defKey,true)}} != {{=condition}} {
        orm.{{=whereCondition}}(dao.{{=camelName}}.Columns().{{=fieldCamelName}}, in.{{=fieldCamelName}})
    }
    {{~}}
    if in.CreatedStart != nil {
		orm.WhereGTE(dao.LbraCatalog.Columns().CreatedAt, in.CreatedStart)
	}
	if in.CreatedEnd != nil {
		orm.WhereLT(dao.LbraCatalog.Columns().CreatedAt, in.CreatedEnd)
	}
	if in.UpdatedStart != nil {
		orm.WhereGTE(dao.LbraCatalog.Columns().UpdatedAt, in.UpdatedStart)
	}
	if in.UpdatedEnd != nil {
		orm.WhereLT(dao.LbraCatalog.Columns().UpdatedAt, in.UpdatedEnd)
	}
	err := orm.Scan(&items)
	return items, err
}

因为go原生没有注解,所以我们的依赖注入需要手动操作,上面的init函数就是用来进行依赖注入的。

得益于gdb的orm框架,我们的条件搜索可以使用orm的链式查询进行拼接,预防sql注入。这里有个重点,和mybatis-plus不一样的是,orm默认是safe模式,也就是每执行一次链式操作都会产生一个新的model,如果想在同一个model上进行操作,就需要调用 Safe(false) 关掉safe,这样就能不断在原来的model上叠加where条件了。

像操作人信息通常做法是,将登陆人信息写入到ctx,我们直接从ctx里面去拿用户信息就好了,这里的BizCtx逻辑就先省略了,可以参考focus-single项目实现,这是一个官方的论坛演示项目。

生成service代码

既然logic代码生成出来了,service也就是接口定义可以根据logic代码生成就好了,这部分不需要我们写模板,使用gf-cli生成,即可:gf gen service

生成api代码

在gf中,api层是对外提供服务的输入/输出数据结构定义。考虑到版本管理需要,往往以api/xxx/v1...存在。

这里依然用pdmaner生成:

{{
    var today=new Date();
    var fullYear=today.getFullYear();
    var month=today.getMonth() + 1<10?"0"+today.getMonth():today.getMonth();
    var days=today.getDate()<10?"0"+today.getDate():today.getDate();
    var hours = today.getHours()<10?"0"+today.getHours():today.getHours();         
	var minutes = today.getMinutes()<10?"0"+today.getMinutes():today.getMinutes();      
	var seconds = today.getSeconds()<10?"0"+today.getSeconds():today.getSeconds();    
	var camelName = it.func.camel(it.entity.defKey,true);
	var filterFields = [
	  "id",
      "created_at",
      "created_by",
      "updated_at",
      "updated_by",
      "deleted_at",
      "deleted_by",
    ];
    var fields = it.entity.fields.filter((item) => !filterFields.includes(item.defKey));
}}// Package v1 {{=it.func.join(it.entity.defName,it.entity.comment,',')}}
// author : https://github.com/Wyatex
// date : {{=fullYear}}-{{=month}}-{{=days}} {{=hours}}:{{=minutes}}
// desc : {{=it.func.join(it.entity.defName,it.entity.comment,',')}} api定义
package v1

$blankline

import (
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/os/gtime"
	"pdmaner-test/internal/model"
	"pdmaner-test/internal/model/entity"
)

$blankline

type {{=camelName}}AddReq struct {
	g.Meta      `path:"/{{=it.entity.defKey}}/v1/add" tags:"{{=camelName}}" method:"post" summary:"创建一个{{=it.entity.defName}}"`{{~fields:field:index}}
	{{
	    let jsonName = it.func.camel(field.defKey,false);
	    let tag = '`json:"' + jsonName + '" dc:"{{=field.defName}}"';
	    if (field.notNull) {
	        tag += ' v:"required#' + field.defName + '不能为空"`';
	    } else {
	        tag += '`'
	    }
	}}{{=it.func.camel(field.defKey,true)}} {{=field.type}} {{=tag}}
    {{~}}
}

$blankline

type {{=camelName}}AddRes bool

$blankline

type {{=camelName}}EditReq struct {
	g.Meta      `path:"/{{=it.entity.defKey}}/v1/edit" tags:"{{=camelName}}" method:"post" summary:"编辑{{=it.entity.defName}}"`
	{{~it.entity.fields:field:index}}{{
	    let jsonName = it.func.camel(field.defKey,false);
	    let tag = '`json:"' + jsonName + '" dc:"{{=field.defName}}"';
	    if (field.notNull) {
	        tag += ' v:"required#' + field.defName + '不能为空"`';
	    } else {
	        tag += '`'
	    }
	}}{{=it.func.camel(field.defKey,true)}} {{=field.type}} {{=tag}}
	{{~}}
}

$blankline

type {{=camelName}}EditRes struct{}

$blankline

type {{=camelName}}DelReq struct {
    g.Meta      `path:"/{{=it.entity.defKey}}/v1/del" tags:"{{=camelName}}" method:"post" summary:"删除{{=it.entity.defName}}"`
    Id *int `json:"id" dc:"删除单个id"`
    Ids []int `json:"ids" dc:"批量删除"`
}

$blankline

type {{=camelName}}DelRes struct{}

$blankline

type {{=camelName}}FindOneReq struct {
    g.Meta      `path:"/{{=it.entity.defKey}}/v1/fine_one" tags:"{{=camelName}}" method:"post" summary:"根据id查询{{=it.entity.defName}}"`
    Id int `json:"id" dc:"删除单个id" v:"required#id不能为空"`
}

$blankline

type {{=camelName}}FindOneRes entity.{{=camelName}}

$blankline

type {{=camelName}}PageReq struct {
    g.Meta      `path:"/{{=it.entity.defKey}}/v1/page" tags:"{{=camelName}}" method:"post" summary:"分页查询{{=it.entity.defName}}"`
    {{~fields:field:index}}{{=it.func.camel(field.defKey,true)}} {{=field.type}} `json:"{{=it.func.camel(field.defKey,false)}}" dc:"{{=field.defName}}"`
	{{~}}CreatedStart *gtime.Time `json:"createdStart" dc:"搜索创建时间范围"`
    CreatedEnd *gtime.Time `json:"createdEnd" dc:"搜索创建时间范围"`
    UpdatedStart *gtime.Time `json:"updatedStart" dc:"搜索更新时间范围"`
    UpdatedEnd *gtime.Time `json:"updatedEnd" dc:"搜索更新时间范围"`
    PageNo int `json:"pageNo" dc:"查找第几页" v:"required#查询页数不能为空"`
    PageItem int `json:"pageItem" dc:"每页个数" v:"required#每页个数不能为空"`
}

$blankline

type {{=camelName}}PageRes model.{{=camelName}}FindPageOutput

$blankline

type {{=camelName}}ListReq struct{
    g.Meta      `path:"/{{=it.entity.defKey}}/v1/list" tags:"{{=camelName}}" method:"post" summary:"查询所有{{=it.entity.defName}}"`
    {{~fields:field:index}}{{=it.func.camel(field.defKey,true)}} {{=field.type}} `json:"{{=it.func.camel(field.defKey,false)}}" dc:"{{=field.defName}}"`
	{{~}}CreatedStart *gtime.Time `json:"createdStart" dc:"搜索创建时间范围"`
    CreatedEnd *gtime.Time `json:"createdEnd" dc:"搜索创建时间范围"`
    UpdatedStart *gtime.Time `json:"updatedStart" dc:"搜索更新时间范围"`
    UpdatedEnd *gtime.Time `json:"updatedEnd" dc:"搜索更新时间范围"`
}

type {{=camelName}}ListRes []entity.{{=camelName}}

$blankline

存放在:api/lbra/v1/lbra_catalog.go

生成controller代码

只要我们按照gf的规范写好api代码,就能通过gf-cli生成基础的代码,执行:gf gen ctrl,可以生成未完成的controller代码,这时候我们可以借助pdmaner生成一下通用的函数体代码:

{{
	var camelName = it.func.camel(it.entity.defKey,true);
}}

func (c *ControllerV1) {{=camelName}}Add(ctx context.Context, req *v1.{{=camelName}}AddReq) (res *v1.{{=camelName}}AddRes, err error) {
	var in *model.{{=camelName}}AddInput
	err = gconv.Struct(req, in)
	if err != nil {
		return nil, err
	}
	err = service.{{=camelName}}().Add(ctx, in)
	return nil, err
}

$blankline

func (c *ControllerV1) {{=camelName}}Del(ctx context.Context, req *v1.{{=camelName}}DelReq) (res *v1.{{=camelName}}DelRes, err error) {
	IdOrIdsNil := gcode.New(-1, "id或者ids不能为空", nil)
	if req.Id == nil {
		if req.Ids == nil {
			return nil, gerror.NewCode(IdOrIdsNil)
		} else if len(req.Ids) == 0 {
			return nil, gerror.NewCode(IdOrIdsNil)
		}
	}
	err = service.{{=camelName}}().Delete(ctx, req.Id, req.Ids)
	return nil, err
}

$blankline

func (c *ControllerV1) {{=camelName}}Edit(ctx context.Context, req *v1.{{=camelName}}EditReq) (res *v1.{{=camelName}}EditRes, err error) {
	var in *entity.{{=camelName}}
	err = gconv.Struct(req, in)
	if err != nil {
		return nil, err
	}
	err = service.{{=camelName}}().Edit(ctx, in)
	return nil, err
}

$blankline

func (c *ControllerV1) {{=camelName}}FindOne(ctx context.Context, req *v1.{{=camelName}}FindOneReq) (res *v1.{{=camelName}}FindOneRes, err error) {
	m, err := service.{{=camelName}}().FindOne(ctx, req.Id)
	return (*v1.{{=camelName}}FindOneRes)(m), gerror.NewCode(gcode.CodeNotImplemented)
}

$blankline

func (c *ControllerV1) {{=camelName}}List(ctx context.Context, req *v1.{{=camelName}}ListReq) (res *v1.{{=camelName}}ListRes, err error) {
	var in *model.{{=camelName}}FindListInput
	err = gconv.Struct(req, in)
	if err != nil {
		return nil, err
	}
	list, err := service.{{=camelName}}().FindList(ctx, in)
	return (*v1.{{=camelName}}ListRes)(&list), err
}

$blankline

func (c *ControllerV1) {{=camelName}}Page(ctx context.Context, req *v1.{{=camelName}}PageReq) (res *v1.{{=camelName}}PageRes, err error) {
	var in *model.{{=camelName}}FindPageInput
	err = gconv.Struct(req, in)
	if err != nil {
		return nil, err
	}
	list, err := service.{{=camelName}}().FindPage(ctx, in)
	return (*v1.{{=camelName}}PageRes)(list), err
}

因为gf-cli生成的controller代码文件是拆分成每个接口一个文件,所以这里生成之后还得多ctrl-c,ctrl-v进去。

至此我们的所以crud代码就生成好咯,来跑起来看看!

$ go run main.go

2023-12-04 16:38:45.109 [INFO] pid[15940]: http server started listening on [:8000]
2023-12-04 16:38:45.109 [INFO] {40ca3afb25949d1737d2364f959e0a9c} swagger ui is serving at address: http://127.0.0.1:8000/swagger/
2023-12-04 16:38:45.110 [INFO] {40ca3afb25949d1737d2364f959e0a9c} openapi specification is serving at address: http://127.0.0.1:8000/api.json

  ADDRESS | METHOD |           ROUTE           |                                 HANDLER                                  |           MIDDLEWARE             
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | ALL    | /*                        | github.com/gogf/gf/v2/net/ghttp.internalMiddlewareServerTracing          | GLOBAL MIDDLEWARE                
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | ALL    | /api.json                 | github.com/gogf/gf/v2/net/ghttp.(*Server).openapiSpec                    |                                  
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | GET    | /hello                    | pdmaner-test/internal/controller/hello.(*ControllerV1).Hello             | ghttp.MiddlewareHandlerResponse  
          |        |                           |                                                                          | middleware.DemoSetCtx            
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/add      | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogAdd     | ghttp.MiddlewareHandlerResponse  
          |        |                           |                                                                          | middleware.DemoSetCtx            
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/del      | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogDel     | ghttp.MiddlewareHandlerResponse  
          |        |                           |                                                                          | middleware.DemoSetCtx            
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/edit     | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogEdit    | ghttp.MiddlewareHandlerResponse  
          |        |                           |                                                                          | middleware.DemoSetCtx            
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/fine_one | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogFindOne | ghttp.MiddlewareHandlerResponse  
          |        |                           |                                                                          | middleware.DemoSetCtx
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/list     | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogList    | ghttp.MiddlewareHandlerResponse
          |        |                           |                                                                          | middleware.DemoSetCtx
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | POST   | /lbra_catalog/v1/page     | pdmaner-test/internal/controller/lbra.(*ControllerV1).LbraCatalogPage    | ghttp.MiddlewareHandlerResponse
          |        |                           |                                                                          | middleware.DemoSetCtx
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------
  :8000   | ALL    | /swagger/*                | github.com/gogf/gf/v2/net/ghttp.(*Server).swaggerUI                      | HOOK_BEFORE_SERVE
----------|--------|---------------------------|--------------------------------------------------------------------------|----------------------------------

调试一下

可以看到因为定义好了api,所以框架也帮我们直接生成了swagger和openapi代码,这样我们就能直接在调试工具导入接口定义了,我个人喜欢apifox,其他的工具也是差不多,这里我们导入openapi代码:

image.png

设置好接口地址:

image.png

自动生成请求参数,发送!

image.png

nice!一切正常,这样我们除了定义数据表,其他地方不敲一行代码,就完成了基础crud代码的创建。剩下的还有前端代码生成,我会展示用一个vue3后台前端管理模板:Soybean Admin 进行展示,期待一下咯!