代码规范说明
适用范围:go_backend_frame 仓库,包含后端服务 server_api(Go)与管理前端 admin_client(Vue 3 + TypeScript)。
目标:让新增代码与现有工程保持一致的目录结构、分层方式、命名风格与安全边界,降低协作与维护成本。
一、仓库总览
go_backend_frame/
├── server_api/ # 后端:Go + Gin + GORM(MySQL) + Redis + JWT(本目录)
│ ├── cmd/ # Cobra 子命令:service admin / service api / version
│ ├── config/ # 配置结构体与加载(含默认值)
│ ├── router/ # admin.go(管理端)、api.go(用户端),两端路由分文件
│ ├── internal/ # 业务代码(对外不可引用)
│ │ ├── common/ # 两端共用:app / auth / enums / middleware / model / upload
│ │ ├── admin/ # 管理端:controller / logic / param / resp / middleware / permission
│ │ └── api/ # 用户端:controller / logic / param / resp
│ ├── pkg/ # 无业务归属的工具:response / pagination / password / tree / dberror / oss / mask / sms / sn
│ ├── sql/ # 数据库脚本(建表与升级)
│ └── docs/ # 接口文档与本规范
└── admin_client/ # 前端:Vue 3 + Vite + TS + Element Plus
└── src/
├── api/ # 按业务模块封装后端请求
├── types/ # 请求与响应类型
├── enums/ # 前端业务枚举(值与后端一致)
├── utils/ # 认证、日期、图表等工具
├── store/ # Pinia 状态
├── router/ # 静态路由与动态路由注册
├── directives/ # v-perm 等指令
├── components/ # 通用组件
├── styles/ # 全局主题与 UI 规范
└── views/ # 页面模块二、后端 Go 规范
2.1 分层职责(强制)
| 层 | 目录 | 职责 | 禁止 |
|---|---|---|---|
| 路由 | router/ | 注册路由与中间件链,按端分文件 | 写业务逻辑 |
| 控制器 | internal/<端>/controller/ | 绑定参数 → 调 logic → 响应 | 写 SQL、写业务规则 |
| 业务 | internal/<端>/logic/ | 业务规则、事务、缓存维护 | 直接写 HTTP 响应 |
| 参数 | internal/<端>/param/ | 请求结构体(按功能拆分文件) | 与 resp 混写 |
| 响应 | internal/<端>/resp/ | 响应结构体(按功能拆分文件) | 在 controller 内定义匿名结构 |
| 模型 | internal/common/model/ | GORM 模型(sys_* 表) | 在 resp 中定义表结构 |
| 中间件 | common/middleware、admin/middleware | 横切逻辑(CORS / 鉴权 / 权限 / 日志) | 业务分支 |
| 工具 | pkg/ | 无业务归属的通用能力 | 依赖 internal |
核心约定:
- controller 方法只做三件事:
requestvalidate.Bind→ 调 logic →response.OK/Fail,禁止直接调用 GinShouldBind*。 - controller 调用 logic 必须传入
*gin.Context;登录信息(claims)、IP、User-Agent 统一在 logic 层通过auth.CtxClaims(c)、c.ClientIP()、c.GetHeader("User-Agent")获取。 param、resp必须按功能拆分为多个文件(如user.go、menu.go),禁止合并成单个param.go/resp.go。- 两端共用能力放
internal/common/<功能>;只属于一端的能力放回该端目录;无业务归属的工具放pkg/<功能>。 - 管理端与用户端代码完全分模块,便于独立迁移部署。
2.2 命名规范
- 文件名:小写 + 下划线(
operation_log.go、sys_user_login.go)。 - 包名:简短单数名词,与目录一致(
param、resp、logic)。 - 结构体:大驼峰;请求以
Req结尾,响应以Res/Item结尾(UserSaveReq、UserItem、OverviewRes)。 - 控制器以
Controller结尾(UserController),业务以Logic结尾(UserLogic),字段名为Logic。 - 方法名与路由动作对应:
List / Create / Update / Delete / Tree / Save / SetDefault / Kick / ResetPassword。 - 错误文本使用中文,面向使用者可读(
errors.New("该菜单下存在子级,不能删除"))。
2.3 结构体 Tag 规范
- JSON 字段统一
snake_case:json:"parent_id"。 - 查询参数接口(GET)字段同时声明
form:"...";请求体接口(POST)只需json:"..."。 - 校验规则使用
binding:required、min=6、oneof=1 2、omitempty,max=1024、url、email。 - 每个请求字段必须增加
validate:"字段语义",用于生成面向用户的校验错误;语义从comment提取核心名称,不包含枚举或补充说明。 - 每个字段写
comment:"..."中文注释,作为接口文档与前端类型的事实来源。
go
type UserSaveReq struct {
ID uint `json:"id" validate:"主键ID" comment:"主键ID"`
Username string `json:"username" binding:"required" validate:"登录账号" comment:"登录账号"`
Password string `json:"password" binding:"required,min=6" validate:"登录密码" comment:"登录密码"`
RoleIDs []uint `json:"role_ids" validate:"角色ID列表" comment:"角色ID列表"`
}统一验证器、错误文案和自定义规则注册方式见 请求参数验证。
2.4 响应与错误
- 统一使用
pkg/response:response.OK(c, data)与response.Fail(c, code, msg),HTTP 状态码恒为 200。 - 业务码:
0成功、400参数错误、401未登录/登录失效、403无权限、500业务失败、503依赖不可用。 - 参数绑定失败统一返回
CodeErrParams和pkg/validate生成的错误,不要透传 Gin 或 validator 原始错误。 - 数据库错误使用
pkg/dberror识别(如IsDuplicateKey→ 重复提示),不要直接把驱动错误返回前端。 - 无返回数据的操作接口(删除、设置默认、踢下线)使用
response.OK(c, nil)。
2.5 数据访问
- 不使用
AutoMigrate,表结构变更必须提供sql/下对应脚本(建表脚本 +upgrade_*.sql)。 - 查询统一走 GORM,禁止拼接 SQL 字符串;动态条件使用
db = db.Where(...)链式构造。 - 批量写入、跨表一致性变更使用事务;保存类接口优先使用
clause.OnConflict做 upsert,避免并发产生重复数据。 - 并发一致性依赖数据库唯一约束(如角色编码唯一、唯一默认渠道),不能只靠应用层判断。
- 软删除模型使用
gorm.DeletedAt,查询默认排除已删除记录。 - 金额类字段数据库使用
decimal(如sys_member.balance为decimal(12,2)),Go 模型与 Resp 用字符串承载,禁止 float/double,避免精度丢失。
2.6 鉴权与权限
- 受保护路由必须挂在带
commonmiddleware.Auth的路由组上。 - 管理端业务接口挂
Auth → Permission → OperationLog;用户端仅Auth。 - 按钮权限通过
sys_menu.api_path绑定,格式METHOD:/path(如POST:/admin/user/add),多个用逗号分隔。 - 通用接口(如文件上传)如需免接口鉴权,必须在
enums.SkipPermissionApis白名单中显式登记。 - 菜单、角色、用户权限变更后,必须调用
permission.ClearAllPermissionCache或清对应perm:<user_id>缓存。 - 禁止在 logic 之外绕过 Permission 中间件做权限判断;前端显隐只是体验,安全边界在后端。
2.7 配置与敏感信息
- 运行时配置放
config.yaml,模板为config.example.yaml;config.yaml已在.gitignore中,禁止提交。 - 新增配置项必须同步更新
config/config.go结构体、config.example.yaml与 README 配置表。 - 密钥类字段(私钥、公钥、APIv3 密钥、Secret)写入时允许为空表示不修改,响应结构一律不返回。
- 禁止把密钥、证书、真实地址写入代码或提交到仓库;证书类文件(
.pem、.key)已被忽略。
2.8 日志与审计
- 管理端操作日志由
OperationLog中间件记录方法、路由、参数、响应、IP、UA、耗时与操作人。 - 新增敏感字段(密码、token、secret、各类 key)需同步加入脱敏关键字列表。
- 查询日志类接口(如操作日志列表)应加入跳过名单,避免自身产生日志风暴。
- 服务日志使用标准库
log+ Gin 日志,慢 SQL 阈值 1s。
2.9 文件上传与存储
- 单文件上限 50MB,走存储层流式处理,禁止把整个文件读入内存。
- 业务字段只保存
relative_path,展示时通过当前默认存储配置动态拼地址。 sys_upload_file同时保存相对路径与上传时的完整地址快照。- 远程抓取仅允许 HTTP(S),复用
safeRemoteHTTPClient,禁止自行发起未校验的外部请求(防 SSRF)。
2.10 枚举与常量
- 业务枚举集中在
internal/common/enums/,按功能分文件(菜单、渠道、登录、平台等)。 - 数值枚举统一从 1 开始,
0仅用于“未设置/不过滤”语义。 - 前端
src/enums必须与后端取值保持一致。
2.11 注释与文档
- 每个
package写一段包注释说明职责与约定(如param、resp、controller、logic)。 - 导出方法写单行注释,说明做什么、以及与权限/缓存相关的副作用。
- 新增或修改接口后,同步更新
go_basic_frame_wiki/public/openapi/admin_openapi.yaml与 Apifox 在线文档。
2.12 测试与提交前检查
- 单元测试与被测文件同目录,命名
*_test.go,优先表驱动。 - 提交前执行:
bash
gofmt -l . # 不允许有输出
go vet ./...
go build ./...
go test ./... # 有测试时三、前端 Vue / TypeScript 规范
3.1 目录与模块划分
- 每个业务模块在
src/api、src/types中各有独立文件,文件名与后端模块一致(user.ts、sms.ts、storage.ts)。 - 页面放
src/views/<模块>/<页面>/index.vue,与后端菜单path对应(/system/user→views/system/user/index.vue)。 - 可复用 UI 抽到
src/components/;横切行为用src/directives/。 - 全局状态只放
src/store/,能由接口直接获取的数据不要进 store。
3.2 接口层
- 统一通过
src/api/http.ts的request<T>(),禁止页面内直接使用 axios。 - 方法命名:
getXxxList、createXxx、updateXxx、deleteXxx,返回类型明确(Promise<PageResult<UserItem>>)。 - 请求参数与响应类型写在
src/types/<module>.ts,通过import type引入。
ts
export const getUserList = (params?: Partial<UserListReq>) => {
return request<PageResult<UserItem>>({ url: '/admin/user/list', method: 'get', params })
}3.3 类型规范
- 复用公共类型:
ApiResponse、PageQuery、PageResult、TreeNode<T>、BaseEntity。 - 禁止
any;不确定类型用unknown并在使用前收窄。 - 列表查询参数继承
PageQuery,分页响应使用PageResult<T>。 - 管理端分页列表必须显式引入并使用核心组件
@/components/AppPagination.vue,禁止页面直接使用el-pagination;分页大小选项统一为10 / 15 / 20 / 25,默认page_size为20。 - 后端树结构为
{ data, children },el-tree-select等组件需要平铺时在项目内做转换,并写注释说明原因。
3.4 语法与风格
- 纯 ES6+,函数统一使用箭头函数,不使用
function声明。 - 组件使用
<script setup lang="ts">,组合式 API;ref/reactive显式标注类型。 - 异步流程使用
async/await,加载态放在finally中关闭。 - 枚举集中在
src/enums,使用as const对象 + 文案映射,数值与后端一致(从 1 开始)。
3.5 页面与样式
- 优先复用
src/styles/index.css中的颜色、间距变量与公共类:.page-card、.toolbar、.table-operations。 - 明暗主题必须同时可用,禁止硬编码只适配白底的颜色。
- 适配常用桌面宽度,窄屏要有合理降级。
- 所有日期时间通过
src/utils/datetime.ts的formatDateTime/formatDateTimeCell格式化到秒,不展示毫秒。
3.6 权限与上传
- 按钮级权限使用指令:
v-perm="'POST:/admin/user/add'"。 - 上传使用通用上传组件;提交业务数据时传
relative_path,不要把访问域名写进业务字段。 - 富文本编辑统一复用管理端
src/components/RichTextEditor.vue,不得在业务模块或插件中重复封装 WangEditor;上传、明暗主题和销毁生命周期由通用组件维护。
3.7 提交前检查
bash
pnpm exec vue-tsc --noEmit
pnpm build同时确认:亮/暗主题正常、无权限角色看不到按钮且后端返回 403、空态与错误态处理正确、未提交 .env.local/构建产物/密钥。
四、通用协作规范
- 分支与提交:功能分支开发,提交信息使用
feat/fix/docs/refactor/chore: 描述前缀,一次提交只做一件事。 - 敏感文件:
config.yaml、.env*、证书、密钥、构建产物一律不入库;模板文件(config.example.yaml、.env.example)必须保留并同步更新。 - 接口文档:后端接口变更同步
go_basic_frame_wiki/public/openapi/admin_openapi.yaml,并更新 Apifox 在线文档与 README 顶部文档地址。 - 双端一致:字段命名、枚举取值、分页结构(
list+total)、响应结构(code/msg/data)前后端必须一致。 - 不要过度设计:新增能力优先复用现有
pkg/与common/;只有在确认无复用可能时才新增包。 - 发版说明:每次发版必须更新
config.example.yaml的version配置(版本号唯一来源是config.yaml,服务启动时校验必填),并在go_basic_frame_wiki/server-api/update_doc/新增v{version}.md版本功能说明,内容参照0.0.1.md中各版本章节的结构(版本目标、主要变更、数据库升级、部署顺序、验证命令)。