签入版本
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# 4.3 后端分层架构
|
||||
|
||||
:::tip 概述
|
||||
后端五层:api / controller / service / logic / dao。分层不是形式主义——每层有明确的"允许/禁止"清单,组合起来构成依赖红线体系。本节逐层说明职责与边界。
|
||||
:::
|
||||
|
||||
## 分层与调用关系
|
||||
|
||||
```text
|
||||
api/v1(契约层)
|
||||
▲ 依赖
|
||||
controller(HTTP 薄层)
|
||||
▲ 依赖接口
|
||||
service(接口层,手维护)
|
||||
▲ 实现
|
||||
logic(业务层,ModuleSpec 基座 + 钩子 + 手写逃生舱)
|
||||
▲ 唯一入口
|
||||
dao(数据访问层,红线条件 + 全局钩子)
|
||||
▲
|
||||
model/entity · model/hooks · gdb
|
||||
```
|
||||
|
||||
侧路能力(横向复用):`utility/{cache,captcha,crypto,excel,ip2layer,jwt,response,ua,upload}` 与 `consts`(错误码/缓存键/上下文键/日志类型)。
|
||||
|
||||
## 各层职责与边界
|
||||
|
||||
### api/v1(契约层)
|
||||
|
||||
+ 全部 Req/Res 结构体,**单包 v1**,每模块一文件(snake_case)
|
||||
+ gmeta 注解声明 method/path/summary/permission/log/logType/public
|
||||
+ gvalid 标签声明校验规则;`dc` 标签供 OpenAPI 文档
|
||||
+ **禁止**:业务逻辑、数据库引用
|
||||
|
||||
### controller(HTTP 薄层)
|
||||
|
||||
+ 一行透传 `return service.Xxx().Method(ctx, req)`
|
||||
+ **禁止**:业务 if/for、import dao、手写响应信封、手写参数解析
|
||||
|
||||
### service(接口层,手维护)
|
||||
|
||||
+ 每模块一文件:接口 `IXxx` + 实例变量 + 取用函数 `Xxx()` + 注册函数 `RegisterXxx()`
|
||||
+ 未注册时取用 panic(启动即暴露接线错误)
|
||||
+ **禁止**:整体运行 `gf gen service`(会覆盖手工重组结构)
|
||||
|
||||
### logic(业务层)
|
||||
|
||||
+ 每模块一文件:`sXxx` 实现 + `xxxSpec` 声明 + `init()` 注册 + 标准委托 + 扩展手写
|
||||
+ 共享能力经 `base.go`(15 个导出上限)与 `module_spec.go` 基座
|
||||
+ 跨模块复用走同包包级函数(ResolveCityNames / LoadIdentity / ApplyDataScope / DictGetText…)
|
||||
+ **禁止**:反向 import task;绕过 dao;在钩子里做 HTTP/文件 IO
|
||||
|
||||
### dao(数据访问层)
|
||||
|
||||
+ 自定义模板产出:构造时挂 `hooks.Handler()`;`Ctx` 覆写注入红线条件
|
||||
+ **禁止**:绕过直连 `g.DB()`;手写 del_flag/tenant_id 条件;手改 dao/internal/
|
||||
|
||||
## 上下文键(consts/context_key.go)
|
||||
|
||||
JWT 中间件写入身份,全链路 `consts.GetXxx(ctx)` 读取:
|
||||
|
||||
| 写入方 | 键 |
|
||||
|--------|-----|
|
||||
| JWT 中间件 | UserID / TenantID / Username / Realname / TokenJTI / TokenExpire / Roles / Perms / DataScope / DeptID |
|
||||
| Response 中间件 | ClientIP / UserAgent / ReqMethod / ReqURL |
|
||||
|
||||
::: warning 两套租户读法(易错点)
|
||||
`CtxTenantID(ctx)` —— 原始值(0=平台),仅供租户设施(hooks/中间件)使用
|
||||
`GetTenantID(ctx)` —— 业务归属值(租户返回自身,超管返回 tenant.defaultId),**新增/导入/唯一校验统一用它**
|
||||
:::
|
||||
|
||||
数据范围读法:`GetDataScope(ctx)` 档位(1-全部;未注入回退全部);`GetDeptID(ctx)` 部门 ID(0=未分配)。
|
||||
|
||||
## 响应与错误码
|
||||
|
||||
统一信封(utility/response):
|
||||
|
||||
```text
|
||||
成功:{"code":0, "ok":true, "msg":"操作成功", "data":{...}}
|
||||
失败:{"code":1, "ok":false, "msg":"...", "data":{}}
|
||||
未认证:HTTP 401 + {"code":401,...}
|
||||
```
|
||||
|
||||
Fail 映射规则:gerror 码 == 401 原样保留;其余一律降级 code=1(前端 ResultEnum.ERROR 才弹 msg)。consts 中的细分错误码(1001 用户不存在…)主要用于日志与语义区分。
|
||||
|
||||
分页契约:`PageData[T]` = `{records, total, size, current, pages}`,records 为空输出 `[]` 而非 null;pageNo 默认 1,pageSize 默认 10 上限 500。
|
||||
|
||||
## 横切约定速查
|
||||
|
||||
```text
|
||||
* 主键:int 数据库自增;新增后 result.LastInsertId() 回读
|
||||
* 软删:del_flag 0 正常 / 1 删除;删除接口全部软删
|
||||
* 表前缀:goframe_;文件命名 snake_case 对齐 dao(前端文件仍驼峰)
|
||||
* 入参/出参 json 驼峰;唯一例外:登录/令牌接口返回
|
||||
access_token/refresh_token/expires_in/token_type(OAuth2 惯例,勿改)
|
||||
* 缓存键统一前缀 easygoadmin:
|
||||
* 权限码 sys:{module小驼峰}:{action};超管 ROLE_SUPER_ADMIN;前端通配 *:*:*
|
||||
* 审计列:id/create_user/create_time/update_user/update_time/del_flag(租户表另有 tenant_id)
|
||||
```
|
||||
Reference in New Issue
Block a user