110 lines
4.4 KiB
Markdown
110 lines
4.4 KiB
Markdown
# 3.3 DAO 数据层
|
||
|
||
:::tip 概述
|
||
DAO 四件(dao/{m}.go、dao/internal/{m}.go、entity、do)由 `gf gen dao` 或代码生成器产出,无需手写。但 `dao.{X}.Ctx(ctx)` 是全系统**唯一查询入口**(架构红线),理解它的两行自定义模板逻辑至关重要。
|
||
:::
|
||
|
||
## 四件产出与职责
|
||
|
||
| 文件 | 产出方式 | 职责 |
|
||
|------|---------|------|
|
||
| `internal/dao/{m}.go` | 自定义模板 hack/tpl/dao_index.tpl | 挂全局钩子 + Ctx 覆写(红线执行点) |
|
||
| `internal/dao/internal/{m}.go` | gf gen dao | 内部实现(列常量/表名/基础查询),勿手改 |
|
||
| `internal/model/entity/{m}.go` | gf gen dao | 实体结构体(对齐表列) |
|
||
| `internal/model/do/{m}.go` | gf gen dao | 数据对象 DO(Insert/Update 入参形态) |
|
||
|
||
## 真实代码(internal/dao/example.go,摘自项目)
|
||
|
||
```go
|
||
// =================================================================================
|
||
// This file is auto-generated by the GoFrame CLI tool. You may modify it as needed.
|
||
// 自定义模板:hack/tpl/dao_index.tpl
|
||
// - 构造时挂载全局模型钩子(审计字段 / 软删除改写;主键交数据库自增)
|
||
// - Ctx 覆写注入软删除与租户隔离条件(架构红线执行点)
|
||
// =================================================================================
|
||
|
||
package dao
|
||
|
||
import (
|
||
"context"
|
||
|
||
"github.com/gogf/gf/v2/database/gdb"
|
||
|
||
"easygoadmin/internal/dao/internal"
|
||
"easygoadmin/internal/model/hooks"
|
||
)
|
||
|
||
// exampleDao is the data access object for the table goframe_example.
|
||
// You can define custom methods on it to extend its functionality as needed.
|
||
type exampleDao struct {
|
||
*internal.ExampleDao
|
||
}
|
||
|
||
var (
|
||
// Example is a globally accessible object for table goframe_example operations.
|
||
Example = exampleDao{
|
||
internal.NewExampleDao(func(m *gdb.Model) *gdb.Model {
|
||
// 全局钩子:Insert 补齐审计字段/租户ID(主键自增);Update 盖章;Delete 改写为软删除
|
||
return m.Hook(hooks.Handler())
|
||
}),
|
||
}
|
||
)
|
||
|
||
// Ctx 覆写:在 SQL 构建之前注入架构红线条件 ——
|
||
// 所有表追加 del_flag=0;租户级表对租户上下文追加 tenant_id IN (0, 当前租户)。
|
||
// 业务代码必须经由本方法访问模型,禁止绕过 dao 直连 DB。
|
||
func (d *exampleDao) Ctx(ctx context.Context) *gdb.Model {
|
||
return hooks.ApplyCtx(ctx, d.ExampleDao.Ctx(ctx), "goframe_example")
|
||
}
|
||
|
||
// Add your custom methods and functionality below.
|
||
```
|
||
|
||
## Ctx 覆写做了什么(hooks.ApplyCtx)
|
||
|
||
```text
|
||
dao.Example.Ctx(ctx)
|
||
│
|
||
├─ 所有表: WHERE del_flag = 0
|
||
└─ 租户级表且非平台上下文:WHERE tenant_id IN (0, 当前租户)
|
||
(租户用户看自己 + 平台共享行;超管 ctx tenant_id=0 不加过滤,全量可见)
|
||
```
|
||
|
||
## 全局钩子行为(hooks.Handler)
|
||
|
||
| 钩子 | 行为 |
|
||
|------|------|
|
||
| Insert | 主键交数据库自增(不填 id);补 del_flag=0、create_time、create_user(ctx 用户名,回退真实姓名);租户级表自动携带 tenant_id(租户用户零值→填自身;超管未指定→`tenant.defaultId`;显式 0 可建平台共享行) |
|
||
| Update | 自动盖章 update_time / update_user(仅 map 数据生效) |
|
||
| Delete | 物理删除一律改写为 `UPDATE del_flag=1`(软删) |
|
||
|
||
## 生成方式
|
||
|
||
```sh
|
||
# 方式一:表结构变更后刷新已实现模块的 dao
|
||
gf gen dao -c hack/config.yaml
|
||
|
||
# 方式二:新模块随生成器一次性产出(dao 四件与 gf gen dao 同构,生成后统一 gofmt)
|
||
go run ./cmd/generator -tables "goframe_example|案例演示表"
|
||
```
|
||
|
||
::: warning 三条红线
|
||
1. **禁止绕过 dao 直连 `g.DB()`**——软删/租户/审计全部失效。
|
||
2. **禁止手写 del_flag/tenant_id 条件**——`Ctx` 已自动注入,手写必错(漏平台行/漏软删行)。
|
||
3. **禁止手改 dao/internal/**——重新生成即被覆盖;自定义方法写在 `dao/{m}.go` 尾部。
|
||
:::
|
||
|
||
## 事务中的 dao
|
||
|
||
`WithTrans(ctx, fn)` 包裹的事务上下文内,`dao.{X}.Ctx(ctx)` 自动走事务连接:
|
||
|
||
```go
|
||
// internal/logic/base.go 的事务包裹(摘自项目)
|
||
// WithTrans 事务包裹(错误自动回滚)
|
||
func WithTrans(ctx context.Context, fn func(ctx context.Context) error) error {
|
||
// g.DB().Begin → fn(gdb.WithTX(ctx, tx)) → 出错 Rollback,否则 Commit
|
||
}
|
||
```
|
||
|
||
纪律:事务内禁止 HTTP/文件 IO;bcrypt 等 CPU 密集操作在事务外预计算(典型:租户开户、用户新增)。
|