326 lines
9.5 KiB
Go
326 lines
9.5 KiB
Go
|
|
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),
|
|||
|
|
)
|
|||
|
|
}
|