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 条件的 Model(DAO层无感知使用) // 这是推荐的无感知使用方式,直接在 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), ) }