前言

看上去很痛苦的那个工程目录果不其然的让我痛苦了两天,不得不再次对其进行解读。首先我们先根据其设计思想来看

代码分层设计

https://goframe.org/pages/viewpage.action?pageId=3672442#id-%E4%BB%A3%E7%A0%81%E5%88%86%E5%B1%82%E8%AE%BE%E8%AE%A1-%E7%97%9B%E7%82%B9%E6%8F%8F%E8%BF%B0

参考这篇文章可以算是很清晰的看到其设计的思想了。和传统的MVC设计不同的是,UI层即代表了视图以及控制器。而又将model层进行细分,对数据库的操作单独拿了出来。把业务逻辑和直接操作数据(例如sql语句)完全分离开。每一层都是链式的调用,就像UI层无法去直接访问DAL层。

工程目录设计

了解了设计思想,再来说说利用开发工具gf生成的项目目录。从一开始的1.X版本,到现在的2.X的版本,变化可以说是很大的,甚至你在github上找到的开源项目都还是1.X的,那个更贴近于传统的Web目录。而现在的项目结构是这样:

/
├── api		// 对外提供服务的输入/输出数据结构定义
├── internal  // 业务逻辑存放目录。通过Golang internal特性对外部隐藏可见性。
│   ├── cmd   // 入口指令,管理维护多个命令行
│   ├── consts   // 一些常量的定义
│   ├── controller  // 接收/解析用户输入参数的入口/接口层。
│   ├── model  // 数据模型,这个就不必说了
│   │   └── entity  // 通过gf工具生成的模型代码在这里,不需要过多关注
│   └── service  // 业务逻辑封装管理
│       └── internal  // 负责数据库访问
│           ├── dao	  // dao层通过框架的ORM抽象层组件与底层真实的数据库交互
│           └── do	  // 调用dao时需要传递do数据结构对象,用于传递查询条件、输入数据
├── manifest  // 包含程序编译、部署、运行、配置的文件
├── resource  // 静态资源文件
├── utility	  // 自定义工具
├── go.mod	  // 依赖包
└── main.go  // 入口文件

其中呢, 每个文件以及他代表的意思,作者都写的很清晰了,这里就不再进行搬砖了。地址,说好的不搬砖…

流转

上图是官方发布的一个流转图,也是其工作流转。具体解答在这

栗子

光听是不好理解的,下面来个真实的例子。通过账号密码来进行查找用户

  1. 数据库建表,我这里图方便直接建了一个user表,只有username和password两个字段

  2. 然后通过加入到配置config中,通过命令gf gen dao生成映射体。

  3. api/v1下新建user.go,定义请求和响应体,例如这样:

    type UserInfoReq struct {
    	g.Meta   `path:"/user" method:"post" tags:"获取用户"`
    	Username string `json:"username"`
    	Password string `json:"password"`
    }
    
    type UserInfoRes struct {
    	Id       uint   `json:"id" dc:"用户ID"`
    	Username string `json:"username" dc:"用户名"`
    }

    g.Meta规定了他的请求方法以及路由,这个可以翻看他的源码

  4. internal/controller下新建user.go,按照hello.go的样子进行封装。

    // 用户管理
    var User = cUser{}
    
    type cUser struct{}
    
    func (a *cUser) GetUserInfo(ctx context.Context, req *v1.UserInfoReq) (res *v1.UserInfoRes, err error) {
    	res = &v1.UserInfoRes{}
    	getInfoRes, err := service.User().GetUserInfo(ctx, model.GetUserInfoInput{
    		Username: req.Username,
    		Password: req.Password,
    	})
    	if err != nil {
    		return nil, err
    	}
    	res.Id = getInfoRes.Id
    	res.Username = getInfoRes.Username
    	return
    }

    中间的逻辑代码暂时不需要看,继续往下

  5. 同级的service下,新建user.go,这里都是对数据库的操作了,比如我们想通过用户名和密码找到对应的用户id和用户名。

    type sUser struct {
    }
    
    func User() *sUser {
    	return &sUser{}
    }
    
    func (s *sUser) GetUserInfo(ctx context.Context, in model.GetUserInfoInput) (out model.UserInfoOutput, err error) {
    	err = dao.User.Ctx(ctx).Where("username=? and password=?", in.Username, in.Password).Scan(&out)
    	return
    }
  6. 注册路由,在internal/cmd/下,生成的项目目录一定可以在这里看到已经注册好的hello,就是这个位置,将controller.User也注册上去。就大功告成了。