Skip to content

3.3 DAO 数据层 ​

概述

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}.gogf gen dao内部实现(列常量/表名/基础查询),勿手改
internal/model/entity/{m}.gogf gen dao实体结构体(对齐表列)
internal/model/do/{m}.gogf 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|案例演示表"

三条红线

  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 密集操作在事务外预计算(典型:租户开户、用户新增)。

小蚂蚁云团队 · 提供技术支持