Files
assets/dao/base/catch_sql.go

326 lines
9.5 KiB
Go
Raw Permalink Normal View History

2026-03-18 10:18:03 +08:00
package base
import (
"context"
"fmt"
"gitea.com/red-future/common/utils"
"github.com/gogf/gf/v2/database/gdb"
"github.com/gogf/gf/v2/frame/g"
)
// ==================== CatchSQL 全局SQL条件拼接控制 ====================
// SQLConditionBuilder SQL条件构建器
type SQLConditionBuilder struct {
// 是否自动添加租户ID条件
EnableTenantId bool
// 是否自动添加创建人条件
EnableCreator bool
// 是否自动添加修改人条件
EnableUpdater bool
// 是否自动添加删除标记条件(只查询未删除数据)
EnableDeletedFilter bool
// 自定义额外条件
ExtraConditions map[string]interface{}
}
// DefaultSQLConditionBuilder 默认SQL条件构建器配置
var DefaultSQLConditionBuilder = SQLConditionBuilder{
EnableTenantId: true,
EnableCreator: false,
EnableUpdater: false,
EnableDeletedFilter: true,
ExtraConditions: make(map[string]interface{}),
}
// ctxKeyCatchSQL 上下文键
type ctxKeyCatchSQL string
const (
// ctxKeySQLBuilder SQL构建器上下文键
ctxKeySQLBuilder ctxKeyCatchSQL = "catch_sql_builder"
// ctxKeySkipCatchSQL 跳过CatchSQL的上下文键
ctxKeySkipCatchSQL ctxKeyCatchSQL = "catch_sql_skip"
)
// WithSQLBuilder 设置自定义SQL条件构建器到上下文
func WithSQLBuilder(ctx context.Context, builder *SQLConditionBuilder) context.Context {
return context.WithValue(ctx, ctxKeySQLBuilder, builder)
}
// GetSQLBuilder 从上下文获取SQL条件构建器
func GetSQLBuilder(ctx context.Context) *SQLConditionBuilder {
if ctx == nil {
return &DefaultSQLConditionBuilder
}
if builder, ok := ctx.Value(ctxKeySQLBuilder).(*SQLConditionBuilder); ok && builder != nil {
return builder
}
return &DefaultSQLConditionBuilder
}
// SkipCatchSQL 跳过CatchSQL条件拼接
func SkipCatchSQL(ctx context.Context) context.Context {
return context.WithValue(ctx, ctxKeySkipCatchSQL, true)
}
// IsSkipCatchSQL 检查是否跳过CatchSQL
func IsSkipCatchSQL(ctx context.Context) bool {
if ctx == nil {
return false
}
v, ok := ctx.Value(ctxKeySkipCatchSQL).(bool)
return ok && v
}
// ==================== CatchSQL 核心方法 ====================
// CatchSQL 全局统一控制SQL条件拼接
// 根据上下文自动添加租户ID、创建人、修改人、删除标记等条件
// 使用示例:
//
// // 基础使用(自动添加默认条件)
// m := base.CatchSQL(ctx, g.DB().Model("asset"))
// m.Where("status", 1).Scan(&result)
//
// // 自定义条件构建器
// builder := &base.SQLConditionBuilder{
// EnableTenantId: true,
// EnableCreator: true,
// }
// ctx = base.WithSQLBuilder(ctx, builder)
// m := base.CatchSQL(ctx, g.DB().Model("asset"))
//
// // 跳过CatchSQL
// ctx = base.SkipCatchSQL(ctx)
// m := base.CatchSQL(ctx, g.DB().Model("asset"))
func CatchSQL(ctx context.Context, model *gdb.Model) *gdb.Model {
if ctx == nil || model == nil {
return model
}
// 检查是否跳过
if IsSkipCatchSQL(ctx) {
return model
}
builder := GetSQLBuilder(ctx)
userInfo, _ := utils.GetUserInfo(ctx)
// 1. 自动添加租户ID条件
if builder.EnableTenantId && !g.IsEmpty(userInfo.TenantId) {
model = model.Where("tenant_id", userInfo.TenantId)
}
// 2. 自动添加创建人条件
if builder.EnableCreator && !g.IsEmpty(userInfo.UserName) {
model = model.Where("creator", userInfo.UserName)
}
// 3. 自动添加修改人条件
if builder.EnableUpdater && !g.IsEmpty(userInfo.UserName) {
model = model.Where("updater", userInfo.UserName)
}
// 4. 自动添加删除标记条件(只查询未删除数据)
if builder.EnableDeletedFilter {
model = model.Where("is_deleted", 0)
}
// 5. 添加自定义额外条件
for field, value := range builder.ExtraConditions {
if field != "" && value != nil {
model = model.Where(field, value)
}
}
return model
}
// CatchSQLWithTable 指定表名创建带CatchSQL条件的Model
// 使用示例:
//
// m := base.CatchSQLWithTable(ctx, "asset")
// m.Where("status", 1).Scan(&result)
func CatchSQLWithTable(ctx context.Context, table string) *gdb.Model {
if ctx == nil {
return g.DB().Model(table).Safe()
}
model := g.DB().Model(table).Safe().Ctx(ctx)
return CatchSQL(ctx, model)
}
// CatchSQLWithSchema 指定Schema和表名创建带CatchSQL条件的Model
// 使用示例:
//
// m := base.CatchSQLWithSchema(ctx, "public", "asset")
// m.Where("status", 1).Scan(&result)
func CatchSQLWithSchema(ctx context.Context, schema, table string) *gdb.Model {
if ctx == nil {
return g.DB().Schema(schema).Model(table).Safe()
}
model := g.DB().Schema(schema).Model(table).Safe().Ctx(ctx)
return CatchSQL(ctx, model)
}
// ==================== 快捷条件构建器 ====================
// NewSQLBuilder 创建新的SQL条件构建器
func NewSQLBuilder() *SQLConditionBuilder {
return &SQLConditionBuilder{
EnableTenantId: true,
EnableCreator: false,
EnableUpdater: false,
EnableDeletedFilter: true,
ExtraConditions: make(map[string]interface{}),
}
}
// WithTenantId 启用/禁用租户ID条件
func (b *SQLConditionBuilder) WithTenantId(enable bool) *SQLConditionBuilder {
b.EnableTenantId = enable
return b
}
// WithCreator 启用/禁用创建人条件
func (b *SQLConditionBuilder) WithCreator(enable bool) *SQLConditionBuilder {
b.EnableCreator = enable
return b
}
// WithUpdater 启用/禁用修改人条件
func (b *SQLConditionBuilder) WithUpdater(enable bool) *SQLConditionBuilder {
b.EnableUpdater = enable
return b
}
// WithDeletedFilter 启用/禁用删除标记过滤
func (b *SQLConditionBuilder) WithDeletedFilter(enable bool) *SQLConditionBuilder {
b.EnableDeletedFilter = enable
return b
}
// WithExtraCondition 添加自定义条件
func (b *SQLConditionBuilder) WithExtraCondition(field string, value interface{}) *SQLConditionBuilder {
if b.ExtraConditions == nil {
b.ExtraConditions = make(map[string]interface{})
}
b.ExtraConditions[field] = value
return b
}
// Build 构建并返回Model
func (b *SQLConditionBuilder) Build(ctx context.Context, model *gdb.Model) *gdb.Model {
ctx = WithSQLBuilder(ctx, b)
return CatchSQL(ctx, model)
}
// ==================== 常用场景快捷方法 ====================
// CatchSQLForTenant 只添加租户ID条件的快捷方法
func CatchSQLForTenant(ctx context.Context, model *gdb.Model) *gdb.Model {
builder := NewSQLBuilder().
WithTenantId(true).
WithDeletedFilter(false)
ctx = WithSQLBuilder(ctx, builder)
return CatchSQL(ctx, model)
}
// CatchSQLForCreator 只添加创建人条件的快捷方法
func CatchSQLForCreator(ctx context.Context, model *gdb.Model) *gdb.Model {
builder := NewSQLBuilder().
WithTenantId(false).
WithCreator(true).
WithDeletedFilter(false)
ctx = WithSQLBuilder(ctx, builder)
return CatchSQL(ctx, model)
}
// CatchSQLForList 列表查询的快捷方法租户ID + 删除标记)
func CatchSQLForList(ctx context.Context, model *gdb.Model) *gdb.Model {
builder := NewSQLBuilder().
WithTenantId(true).
WithDeletedFilter(true)
ctx = WithSQLBuilder(ctx, builder)
return CatchSQL(ctx, model)
}
// CatchSQLForAdmin 管理员查询的快捷方法(只过滤删除标记,不过滤租户)
func CatchSQLForAdmin(ctx context.Context, model *gdb.Model) *gdb.Model {
builder := NewSQLBuilder().
WithTenantId(false).
WithDeletedFilter(true)
ctx = WithSQLBuilder(ctx, builder)
return CatchSQL(ctx, model)
}
// ==================== DAO层无感知集成 ====================
// CtxModel 创建带 CatchSQL 条件的 ModelDAO层无感知使用
// 这是推荐的无感知使用方式,直接在 DAO 的 Ctx() 方法中调用
//
// 使用示例DAO层
//
// func (d *assetDao) Ctx(ctx context.Context) *gdb.Model {
// return base.CtxModel(ctx, entity.Asset{})
// }
//
// func (d *assetDao) GetById(ctx context.Context, id uint64) (*entity.Asset, error) {
// var result entity.Asset
// err := d.Ctx(ctx).Where("id", id).Scan(&result) // 自动带 tenant_id 和 is_deleted=0 条件
// return &result, err
// }
func CtxModel(ctx context.Context, table interface{}) *gdb.Model {
if ctx == nil {
return g.DB().Model(table).Safe()
}
model := g.DB().Model(table).Safe().Ctx(ctx)
return CatchSQL(ctx, model)
}
// CtxModelWithHook 创建带 CatchSQL 条件和 Hook 的 Model完整版
// 同时启用 CatchSQL 条件拼接和 CatchSQLHook 自动字段赋值
//
// 使用示例DAO层
//
// func (d *assetDao) Ctx(ctx context.Context) *gdb.Model {
// return base.CtxModelWithHook(ctx, entity.Asset{})
// }
//
// func (d *assetDao) Insert(ctx context.Context, data g.Map) (int64, error) {
// return d.Ctx(ctx).Data(data).InsertAndGetId() // 自动赋值 tenant_id, creator, updater
// }
func CtxModelWithHook(ctx context.Context, table interface{}) *gdb.Model {
if ctx == nil {
return g.DB().Model(table).Safe()
}
model := g.DB().Model(table).Safe().Ctx(ctx)
// 先应用 CatchSQL 条件
model = CatchSQL(ctx, model)
// 再应用 Hook用于 Insert/Update 自动字段赋值)
model = model.Hook(CatchSQLHook())
return model
}
// ==================== 调试工具 ====================
// GetCatchSQLInfo 获取当前CatchSQL的配置信息用于调试
func GetCatchSQLInfo(ctx context.Context) string {
if IsSkipCatchSQL(ctx) {
return "CatchSQL: skipped"
}
builder := GetSQLBuilder(ctx)
userInfo, _ := utils.GetUserInfo(ctx)
return fmt.Sprintf(
"CatchSQL{TenantId:%v(%v), Creator:%v(%v), Updater:%v(%v), DeletedFilter:%v, Extra:%v}",
builder.EnableTenantId, userInfo.TenantId,
builder.EnableCreator, userInfo.UserName,
builder.EnableUpdater, userInfo.UserName,
builder.EnableDeletedFilter,
len(builder.ExtraConditions),
)
}