# 规范目录 [命名](#命名) [包名package](#包名package) [变量](#变量) [常量](#常量) [接口](#接口) [结构体](#结构体) [方法](#方法) [注释](#注释) [数据库设计](#数据库设计) [API设计风格](#api设计风格) ## 命名 文件和目录命名一律采用小写,不用驼峰式,尽量见名思义,看见文件名就可以知道这个文件下的大概内容。尽量和标准库不要冲突。 其中测试文件以_test.go结尾,除测试文件外,命名`不出现下划线`。 例子: stringutil.go, stringutil_test.go ## 包名package 包名用全小写,使用短命名,尽量和标准库不要冲突。 包名统一使用单数形式。 ## 变量 变量命名一般采用驼峰式,当遇到特有名词(缩写或简称,如DNS)的时候,特有名词根据是否私有全部大写或小写, `不使用下划线`。 例子: apiClient、URLString ## 常量 同变量规则,力求语义表达完整清楚,不要嫌名字长。 如果模块复杂,为避免混淆,可按功能统一定义在package下的一个文件中。 ## 接口 单个函数的接口名以 er 为后缀 type Reader interface { Read(p []byte) (n int, err error) } 两个函数的接口名综合两个函数名,如: type WriteFlusher interface { Write([]byte) (int, error) Flush() error } 三个以上函数的接口名类似于结构体名,如: type Car interface { Start() Stop() Drive() } ## 结构体 结构体名应该是名词或名词短语,`不使用下划线`,如Account,Book,避免使用Manager这样的。 如果该数据结构需要序列化,如json, 则首字母大写, 包括里面的字段。 ## 方法 方法名应该是动词或动词短语,采用驼峰式, `不使用下划线`。将功能及必要的参数体现在名字中, 不要嫌长, 如updateById,getUserInfo. 如果是结构体方法,那么 Receiver 的名称使用缩写,一般使用结构体名的首字母或前两个字母(小写)作为 Receiver 的名称。 如: func (f foo) method() { ... } func (re *Receiver) method() { ... } 对于结构体方法中,Receiver命名应该统一, 要么都使用值, 要么都用指针。 ## 注释 每个包都应该有一个包注释,位于 package 之前。如果同一个包有多个文件,只需要在一个文件中编写即可;如果你想在每个文件中的头部加上注释,需要在版权注释和 Package前面加一个空行,否则版权注释会作为Package的注释。如: // Copyright 2009 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package net 每个以大写字母开头(即可以导出)的方法应该有注释,且以该函数名开头。如: // Get 会响应对应路由转发过来的 get 请求 func (c *Controller) Get() { ... } 大写字母开头的方法以为着是可供调用的公共方法,如果你的方法想只在本包内掉用,请以小写字母开发。如: func (c *Controller) curl() { ... } 注释应该用一个完整的句子,注释的第一个单词应该是要注释的指示符,以便在 godoc 中容易查找。 注释应该以一个句点 . 结束 ## 数据库设计 * 表名统一采用单数形式,如user_info * 字段名统一采用驼峰式,如小写加下划线,如user_id * 数据表设计时索引名称要加上表名(或缩写,索引名称也尽量避免过长)前缀,idx/uni后缀,索引名不能重复,postgresql数据库时索引名全局唯一 * 每个表必须要有主键,不然goctl生成的模型文件会报错 * 每个表必须带上created_at、updated_at和deleted_at字段,方便生成仓库文件 软删除可以在仓库创建时选择是否使用 * 数据库关键字不能用作字段名, 如:desc, asc 等 pg select 带关键字字段时会报错 ## API设计风格 以go-zero框架开发为例: * `接口请求参数和返回结果统一下划线分隔`, 请求参数结构体以Req结尾 返回结果结构体以Res结尾 * 代码目录和文件名称全部小写 `不加下划线` (模型和repository生成文件除外) * app下创建应用api文件统一放在apifile目录 以main.api为主文件 * 数据库表名全部改成单数(方便生成工具使用) * 接口查询和更新Req参数结构非必传(optional)字段用指针,为统一方便记忆全部请求参数都使用指针类型, 这样logic获取请求参数都是指针 (因为指针结构体请求时不传该参会为nil类型,使copier复制时就会跳过修改此字段) eg: ``` UpdateAppInstanceReq { AppKey *string `path:"appKey"` UserId *int64 `json:"userId,optional"` Name *string `json:"name,optional"` Logo *string `json:"logo,optional"` } ``` * GET请求参数使用参数的flag要写成form传递 用json传递会导致参数无法获取到 ``` QueryDeviceReq { Page *uint64 `form:"page,optional,default=1"` PageSize *uint64 `form:"page_size,optional,default=20"` GroupId *int64 `form:"group_id,optional"` IotProductKey *string `form:"iot_product_key,optional"` Keyword *string `form:"keyword,optional"` } ``` * Api创建和删除非必要不在logic return 内容, 200响应即成功,eg: ``` @handler Create post / (CreateReq) @handler Delete delete /:id (IdPathReq) ``` * ##### Api定义尽量保持一致:(动作+资源) * 查询 => QueryXXX * 创建 => CreateXXX * 编辑 => UpdateXXX * 删除 => DeleteXXX * 详情 => DetailXXX 其他: * 上传 => UploadXXX * 触发 => TriggerXXX * ##### 请求参数结构体命名为handler后方法名+Req * ##### 返回结果结构体命名为handler后方法名+Res ``` 示例: service smartenergy { @doc "查询触发器" @handler QueryTrigger get / (QueryTriggerReq) returns (QueryTriggerRes) @doc "创建触发器" @handler CreateTrigger post / (CreateTriggerReq) returns (TriggerRes) @doc "编辑触发器" @handler UpdateTrigger put /:id (UpdateTriggerReq) @doc "触发器详情" @handler DetailTrigger get /:id (TriggerIdReq) @doc "删除触发器" @handler DeleteTrigger delete /:id (TriggerIdReq) } ``` * 尽量避免修改handler 因为修改api文件重新生成不会生成已有handler 无修改过handler方便直接删除目录重新生成