我们继续前一篇,来实现 Lookup 的新增。
API方面 :在 api/lookup/v1/lookup.go 后继续添加API请求结构和响应结构 (请求参数用指针形式,因为指针形式可以避免golang类型默认值的影响,例如整型默认值为0,即指针形式的参数意味着用户可能不传递它),主键整数用 int64,不然 int 会遇到类型转换
Go
type IndexReq struct {
g.Meta `path:"/lookup/index" method:"get"`
Id *int64 `json:"id" dc:"ID"`
Name *string `json:"name" dc:"名称"`
Code *int `json:"code" dc:"代码"`
Type *string `json:"type" dc:"类别"`
Position *int `json:"position" dc:"排序位置"`
}
type IndexRes struct {
List []*entity.Lookup `json:"list" dc:"条目列表"`
}
type CreateReq struct {
g.Meta `path:"/lookup/create" method:"post" sm:"创建" tags:"条目"`
Name string `json:"name" v:"required|length:1,100" dc:"名称"`
Code int `json:"code" v:"required|integer" dc:"代码"`
Type string `json:"type" v:"required|length:1,100" dc:"类别"`
Position int `json:"position" dc:"排序位置"`
}
type CreateRes struct {
Id int64 `json:"id" dc:"ID"`
}
业务逻辑方面:在 internal/logic/lookup/lookup.go 后面添加代码 (对获取列表的方法进行了重命名Refactor)。业务逻辑要和数据库打交道,主要是通过 dao ORM方式实现。
Go
func (s *sLookup) Index(ctx context.Context, in *do.Lookup) (res *v1.IndexRes, err error) {
res = &v1.IndexRes{}
err = dao.Lookup.Ctx(ctx).
OrderAsc(dao.Lookup.Columns().Code).
OrderAsc(dao.Lookup.Columns().Id).
Scan(&res.List)
return
}
func (s *sLookup) Create(ctx context.Context, in *do.Lookup) (res *v1.CreateRes, err error) {
var cols = dao.Lookup.Columns()
cnt, err := dao.Lookup.Ctx(ctx).
Where(cols.Code, in.Code).
Where(cols.Type, in.Type).Count()
if err != nil {
return nil, err
}
if cnt > 0 {
return nil, gerror.New("该类型此代码的条目已经存在")
}
insertId, err := dao.Lookup.Ctx(ctx).Data(in).InsertAndGetId()
if err != nil {
return nil, err
}
res = &v1.CreateRes{
Id: insertId,
}
return
}
用 gf gen ctrl反向生成抽象接口 (其实只是已经存在的 api/lookup/lookup.go 的 ILookupV1 interface 里面多了个接口方法,表示给控制器方法加了约定)和控制器方法 (生成 internal/controller/lookup/lookup_v1_create.go 文件)。
用 gf gen service 反向生成抽象接口(其实只是已经存在的 internal/service/lookup.go 的 ILookup interface 里面多了个接口方法,表示给业务逻辑方法加了约定)。
最后就是完善控制器方法internal/controller/lookup/lookup_v1_create.go:查找服务对象并调用业务逻辑方法,将用户请求req的数据传入do,处理结果装入响应res。(写用do对象)
Go
func (c *ControllerV1) Create(ctx context.Context, req *v1.CreateReq) (res *v1.CreateRes, err error) {
//return nil, gerror.NewCode(gcode.CodeNotImplemented)
res, err = service.Lookup().Create(ctx, &do.Lookup{
Name: req.Name,
Code: req.Code,
Type: req.Type,
Position: req.Position,
})
return
}
以上三步走是典型的步骤。我们在 Goland 中来测试一下功能:在 api/lookup 目录点右键,新建 HTTP Request 文件 lookup-crud.http (可以 Tools - Http Client 菜单下的 Collection 子菜单中抄怎么写请求),不通过请求之间空开一行且最后一行保留一个注释行
bash
### GET lookup list
GET http://localhost:8000/lookup/index
### POST lookup create
POST http://localhost:8000/lookup/create
Content-Type: application/json
{
"name": "test-name1",
"code": {{$random.integer()}},
"type": "testCategory",
"position": 0
}
### end
点左边的绿色箭头,可以执行对应的请求。(当然,首先要观察 gf run main.go 的热更新编译结果是正常的)
我们来把更新、查看、删除功能补充完整。
第一步,请求和响应的结构定义,在 api/lookup/v1/lookup.go 中添加
Go
type UpdateReq struct {
g.Meta `path:"/lookup/update/{id}" method:"post" sm:"更新" tags:"条目"`
Id int64 `json:"id" v:"required|integer" dc:"ID"`
Name *string `json:"name" v:"length:1,100" dc:"名称"`
Code *int `json:"code" v:"integer" dc:"代码"`
Type *string `json:"type" v:"length:1,100" dc:"类别"`
Position *int `json:"position" dc:"排序位置"`
}
type UpdateRes struct{}
type DeleteReq struct {
g.Meta `path:"/lookup/delete/{id}" method:"post" sm:"删除" tags:"条目"`
Id int64 `json:"id" v:"required|integer" dc:"ID"`
}
type DeleteRes struct{}
type ViewReq struct {
g.Meta `path:"/lookup/view/{id}" method:"get" sm:"详情" tags:"条目"`
Id int64 `json:"id" v:"required|integer" dc:"ID"`
}
type ViewRes struct {
*entity.Lookup `dc:"条目"`
}
第二步,编写相应的业务逻辑,在 internal/logic/lookup/lookup.go 下添加
Go
func (s *sLookup) Update(ctx context.Context, in *do.Lookup) (res *v1.UpdateRes, err error) {
_, err = dao.Lookup.Ctx(ctx).Data(in).WherePri(in.Id).Update()
return
}
func (s *sLookup) Delete(ctx context.Context, id int64) (res *v1.DeleteRes, err error) {
_, err = dao.Lookup.Ctx(ctx).WherePri(id).Delete()
return
}
func (s *sLookup) View(ctx context.Context, id int64) (res *v1.ViewRes, err error) {
res = &v1.ViewRes{}
err = dao.Lookup.Ctx(ctx).WherePri(id).Scan(&res.Lookup)
return
}
第三步,生成代码并完善控制器,先 gf gen ctrl 生成控制器方法文件,再 gf gen service 生成(其实是修改)服务接口文件。更新方法 internal/controller/lookup/lookup_v1_update.go 修改为
Go
func (c *ControllerV1) Update(ctx context.Context, req *v1.UpdateReq) (res *v1.UpdateRes, err error) {
//return nil, gerror.NewCode(gcode.CodeNotImplemented)
res, err = service.Lookup().Update(ctx, &do.Lookup{
Id: req.Id,
Name: req.Name,
Code: req.Code,
Type: req.Type,
Position: req.Position,
})
return
}
查看方法 internal/controller/lookup/lookup_v1_view.go 修改为
Go
func (c *ControllerV1) View(ctx context.Context, req *v1.ViewReq) (res *v1.ViewRes, err error) {
//return nil, gerror.NewCode(gcode.CodeNotImplemented)
res, err = service.Lookup().View(ctx, req.Id)
return
}
删除方法 internal/controller/lookup/lookup_v1_delete.go 修改为
Go
func (c *ControllerV1) Delete(ctx context.Context, req *v1.DeleteReq) (res *v1.DeleteRes, err error) {
//return nil, gerror.NewCode(gcode.CodeNotImplemented)
res, err = service.Lookup().Delete(ctx, req.Id)
return
}
请求测试文件 api/lookup/lookup-crud.http 添加
bash
### POST lookup update
POST http://localhost:8000/lookup/update/6
Content-Type: application/json
{
"name": "test-name3",
"code": 478,
"type": "testCategory",
"position": 1
}
### GET lookup view
GET http://localhost:8000/lookup/view/6
Content-Type: application/json
{
"id": 6
}
### POST lookup delete
POST localhost:8000/lookup/delete/6
Content-Type: application/json
测试一下,可以发现总体上已经实现了 CRUD 功能。但似乎存在一点小问题,对于 update、view、delete,如果对应 id 的记录不存在,也会返回成功,只是没有对应记录。index 还没有测试加筛选条件的情况。