昨天在技术交流群聊到快速生成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的示例库的图书目录进行演示
建表命令:
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
:
代码生成
模板介绍
pdmaner的代码生成模板使用的是dot
包,是一个基于JavaScript语言的模板生成工具,所以需要先了解一下模板的基础语法,掘金里也有dot的使用文章
pdmaner补充了一些常用的工具函数,比如驼峰转下划线,下划线转驼峰等等:
生成dao代码
因为dao代码基本所有项目都差不多相同,所以直接用gf-cli生成,输入:gf gen dao
然后就能看到生成了dao、do、entity代码了。
生成model代码
model代码主要是一些结构体的定义,用于controller层向service层传递数据,这部分通常但是我们有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 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的,所以我们过滤一下这几个字段,然后像分页需要传分页参数等等,按需求进行编写。
然后回到代码生成界面就能看到我们的代码了:
至于代码格式化,就交给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代码:
设置好接口地址:
自动生成请求参数,发送!
nice!一切正常,这样我们除了定义数据表,其他地方不敲一行代码,就完成了基础crud代码的创建。剩下的还有前端代码生成,我会展示用一个vue3后台前端管理模板:Soybean Admin 进行展示,期待一下咯!