GF框架:https://www.topgoer.cn/docs/goframe

如果是业务类型项目,GoFrame官方推荐的Go项目目录结构如下:

/
├── app
│   ├── api
│   ├── model
│   └── service
├── boot
├── config
├── docker
├── document
├── i18n
├── library
├── packed
├── public
├── router
├── template
├── vendor
├── Dockerfile
├── go.mod
└── main.go

 

说明描述
app 业务逻辑层 所有的业务逻辑存放目录。
api 业务接口 接收/解析用户输入参数的入口/接口层。
model 数据模型 数据管理层,仅用于操作管理数据,如数据库操作。
service 逻辑封装 业务逻辑封装层,实现特定的业务需求,可供不同的包调用。
boot 初始化包 用于项目初始化参数设置,往往作为main.go中第一个被import的包。
config 配置管理 所有的配置文件存放目录。
docker 镜像文件 Docker镜像相关依赖文件,脚本文件等等。
document 项目文档 Documentation项目文档,如: 设计文档、帮助文档等等。
i18n I18N国际化 I18N国际化配置文件目录。
library 公共库包 公共的功能封装包,往往不包含业务需求实现。
packed 打包目录 将资源文件打包的Go文件存放在这里,boot包初始化时会自动调用。
public 静态目录 仅有该目录下的文件才能对外提供静态服务访问。
router 路由注册 用于路由统一的注册管理。
template 模板文件 MVC模板文件存放的目录。
vendor 第三方包 第三方依赖包存放目录(可选, 未来会被淘汰)。
Dockerfile 镜像描述 云原生时代用于编译生成Docker镜像的描述文件。
go.mod 依赖管理 使用Go Module包管理的依赖描述文件。
main.go 入口文件 程序入口文件。
GF官方推荐的项目目录结构采用的是MVC设计模式,准确的说是”MVCS“模式。
GF
MVC
MVCS
  • 控制器代码位于/app/api。控制器负责接收并响应客户端的输入与输出,包括对输入参数的过滤、转换、校验,对输出数据结构的维护,并调用service实现业务逻辑处理。 app/api就是对应php的Yii框架的Controller层。
  • 逻辑封装的代码位于/app/service。业务逻辑是需要封装的,特别是一些可复用的业务逻辑,并被控制器调用实现业务逻辑处理。
  • 数据模型的代码位于/app/model。数据模型负责维护所有的数据操作,特别是对数据库的访问控制。数据模型往往是被service调用,不推荐通过控制器直接访问数据模型。
  • 模板解析是可选的,模板文件的存放于/template。在实践中往往可以采用MVVM的模式,例如使用vue/react等框架实现模板解析。如果使用经典的模板解析,可以通过GF框架强大的模板引擎实现模板解析。这个/template相当于是view层。
开发约束:
  • 包名定义,言简意赅,且推荐通过不同的import路径来区分相同包名的包引入。
  • 控制器。
    • 结构体定义用来约束控制器的输入输出,比如虽然仅有一个参数,但也采用了结构化定义,我们直接查看该结构体便可得知该接口的输入参数格式,而不用进入代码中去分析,从而极大提高维护效率。结构体转换可以使用GetStruct或者Parse方法,其中Parse同时可以执行数据校验。结构体转换方法的参数都可以给定一个结构体的空指针,内部会自动初始化结构体对象,转换失败(例如提交参数不存在)不会执行初始化。可以通过给结构体绑定v的标签进行设定校验规则以及定义的错误提示。例如:
      复制代码
      // 账号唯一性检测请求参数,用于前后端交互参数格式约定
      type CheckPassportRequest struct {
          Passport string
      }

      // 结构体转换
      var data *SignUpRequest
      // 这里没有使用Parse而是仅用GetStruct获取对象,
      // 数据校验交给后续的service层统一处理。
      if err := r.GetStruct(&data); err != nil {
          response.JsonExit(r, 1, err.Error())
      }
      
      
      // 登录请求参数,用于前后端交互参数格式约定。 这个就是提示下吧?会直接拦截校验空了吗?
      type SignInRequest struct {
          Passport string `v:"required#账号不能为空"`
          Password string `v:"required#密码不能为空"`
      }
      复制代码
    • 控制器负责接收、转换、校验、处理请求参数后,将所需的参数传递给调用的service层一个或者多个包方法,而不是直接将Request对象传递给service例如:
      复制代码
      // 用户登录接口
      func (c *C) SignIn(r *ghttp.Request) {
          var data *SignInRequest
          if err := r.Parse(&data); err != nil {   //请求参数解析到结构化对象data中
              response.JsonExit(r, 1, err.Error())
          }
      
          //仅仅把service层需要的参数传递给service,注意这里Session也是通过控制器传过去的
          if err := user.SignIn(data.Passport, data.Password, r.Session); err != nil {
              response.JsonExit(r, 1, err.Error())   
          } else {
              response.JsonExit(r, 0, "ok")
          }
      }



      .......api层和service层,请求参数输入结构体以及简单的校验规则的放置于控制器api中管理,然后往往通过gconv.Struct方法转换为service对应方法需要的输入参数
      .......api的输入参数与service的输入参数数据结构往往比较类似,但不是完全一致,但两者相同属性的数据类型往往需要一一对应,以方便转换。

       api层请求输入参数:

      // 注册请求参数,用于前后端交互参数格式约定
      type SignUpRequest struct {
          Passport  string `v:"required|length:6,16#账号不能为空|账号长度应当在:min到:max之间"`
          Password  string `v:"required|length:6,16#请输入确认密码|密码长度应当在:min到:max之间"`
          Password2 string `v:"required|length:6,16|same:Password#密码不能为空|密码长度应当在:min到:max之间|两次密码输入不相等"`
          Nickname  string
      }

      service层对应方法输入参数:
      // 注册输入参数
      type SignUpParam struct {
          Passport  string
          Password  string
          Password2 string
          Nickname  string
      }
      api请求参数到service输入参数的转换:
      func (c *C) SignUp(r *ghttp.Request) {
          var (
              data        *SignUpRequest
              signUpParam *user.SignUpParam
          )
          if err := r.Parse(&data); err != nil {
              response.JsonExit(r, 1, err.Error())
          }
          if err := gconv.Struct(data, &signUpParam); err != nil {
              response.JsonExit(r, 1, err.Error())
          }
          if err := user.SignUp(signUpParam); err != nil {
              response.JsonExit(r, 1, err.Error())
          } else {
              response.JsonExit(r, 0, "ok")
          }
      }
       
      复制代码

      Go的HTTP请求流程中,不存在”全局变量”获取请求参数的方式,只有根据service的需要按需传递参数。

    • 中间件使用,middleware?   https://www.topgoer.cn/docs/goframe/goframe-1cm3sofvosh6l        ???看视频再学习下
    • 基础类库,主要固定返回数据格式及数据结构。其中JsonExitJson的区别在于,JsonExit调用时会输出JSON数据后直接退出当前的路由方法;而Json在执行输出后会继续执行后续的路由方法逻辑。JsonExit 会多出来r.Exit()的逻辑。
    • 启动执行,引入包。boot包、router包需要在最开始引入,可以在包前加上_ 。https://www.topgoer.cn/docs/goframe/goframe-1cm3spkv6ak16
    • 路由注册https://www.topgoer.cn/docs/goframe/goframe-1cm3spemitknf       ???看视频再学习下
    • 配置文件config.toml。在boot包中执行代码层级的初始化,比如一些组件模块的设置。
    •  

       

    • 任何时候,您都可以通过g.Server()方法获得一个默认的Server对象,该方法采用单例模式设计,也就是说,多次调用该方法,返回的是同一个Server对象。通过Run()方法执行Server的监听运行,在没有任何额外设置的情况下,它默认监听80端口。
  • 一些web操作等,看