实战一:用户管理 API

把前面章节串起来做一个小而完整的“用户管理”API:先建表并执行 gf gen dao 生成 dao/do/entity(见代码生成章节),再用规范路由定义接口、logic 层做 CRUD 与密码哈希,错误统一走 gerror 错误码,main 注册启动。

接口定义(api/user/v1)

package v1

import "github.com/gogf/gf/v2/frame/g"

type UserCreateReq struct {
	g.Meta   `path:"/user" method:"post" tags:"User" summary:"create user"`
	Name     string `v:"required|length:2,30" json:"name"`
	Password string `v:"required|length:6,64" json:"password"`
}
type UserCreateRes struct{ Id int64 `json:"id"` }
type UserGetOneReq struct {
	g.Meta `path:"/user/{id}" method:"get" tags:"User" summary:"get one user"`
	Id     int64 `v:"required" in:"path" json:"id"`
}
type UserGetOneRes struct{ Id int64 `json:"id"`; Name string `json:"name"` }

入参校验声明在 v 标签,in:"path" 表示参数来自路径;Update/Delete 等结构同构扩展。密码用通用 bcrypt(golang.org/x/crypto/bcrypt)哈希后入库——GoFrame 未内置哈希组件,选型以官方为准。

业务逻辑(internal/logic/user)

package user

import (
	"context"
	"github.com/gogf/gf/v2/errors/gerror"
	"golang.org/x/crypto/bcrypt"
	"userdemo/api/user/v1"
	"userdemo/internal/code"
	"userdemo/internal/dao"
	"userdemo/internal/model/do"
	"userdemo/internal/model/entity"
)

func Create(ctx context.Context, in *v1.UserCreateReq) (out *v1.UserCreateRes, err error) {
	hash, err := bcrypt.GenerateFromPassword([]byte(in.Password), bcrypt.DefaultCost)
	if err != nil { return nil, err }
	id, err := dao.User.Ctx(ctx).Data(do.User{Name: in.Name, Password: string(hash)}).InsertAndGetId()
	if err != nil { return nil, err }
	return &v1.UserCreateRes{Id: id}, nil
}

func GetOne(ctx context.Context, in *v1.UserGetOneReq) (out *v1.UserGetOneRes, err error) {
	var user entity.User
	if err = dao.User.Ctx(ctx).Where("id", in.Id).Scan(&user); err != nil { return nil, err }
	if user.Id == 0 { return nil, gerror.NewCode(code.UserNotExist) } // 业务错误码 >1000
	return &v1.UserGetOneRes{Id: user.Id, Name: user.Name}, nil
}

错误码常量放独立包:code.UserNotExist = gcode.New(1001, "用户不存在", nil),统一由响应中间件输出。

注册启动(main)

规范路由可用 BindHandler 单路由注册直接绑定业务函数;大批量接口则用 controller 包配合 group.Bind(new(v1.Controller)),可交给 gf gen ctrl/service 生成骨架:

package main

import (
	"context"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	apiv1 "userdemo/api/user/v1"
	"userdemo/internal/logic/user"
)

func main() {
	s := g.Server()
	s.Use(ghttp.MiddlewareHandlerResponse) // 统一 {code,message,data}
	s.BindHandler("/user", func(ctx context.Context, req *apiv1.UserCreateReq) (res *apiv1.UserCreateRes, err error) {
		return user.Create(ctx, req)
	})
	s.BindHandler("/user/{id}", func(ctx context.Context, req *apiv1.UserGetOneReq) (res *apiv1.UserGetOneRes, err error) {
		return user.GetOne(ctx, req)
	})
	s.Run()
}

启动后 POST /user 创建、GET /user/{id} 查询均返回统一结构;开启 openapiPath/api.json 自动出文档,Swagger UI 可直接调试。

要点回顾

  • DAO 调用必须 Ctx(ctx);写入用 do,查询结果映射 entity,参数全部走 ORM 参数化。
  • 校验写在 v 标签、错误抛 gerror 错误码,接口层零手写防御,返回结构全局统一。
  • 密码只存哈希;单接口结构体一对 Req/Res,扩展成列表/更新/删除照抄即可。
笔记加载中…