Compare commits
	
		
			27 Commits
		
	
	
		
			b10a55f62d
			...
			e0b984e2cf
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| e0b984e2cf | |||
| 6c8fd23f1a | |||
| d2e96ecfe6 | |||
| 2be1b29234 | |||
| c6b8d29b61 | |||
| f359598109 | |||
| 5ed8f2007e | |||
| 1071c7558e | |||
| 15b1d9e6b0 | |||
| e5233b4fae | |||
| 21ad50b273 | |||
| c586fa5c4f | |||
| a4d887ecb1 | |||
| 352fd61f92 | |||
| bc99c08d55 | |||
| ce189a9067 | |||
| 3b1e7c7232 | |||
| 334472815c | |||
| 128bf9e062 | |||
| 7a87758a4e | |||
| 8a0efca6ee | |||
| 4ee868a059 | |||
| 0098405793 | |||
| c854f0ebeb | |||
| 4ba14dc23d | |||
| 5e87706d0a | |||
| 5024ae4238 | 
							
								
								
									
										24
									
								
								abstract/api_sql_manager.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								abstract/api_sql_manager.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | // Package abstract ... | ||||||
|  | // | ||||||
|  | // Description : abstract ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-20 17:17 | ||||||
|  | package abstract | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IManager 接口 => sql 数据管理实例约束 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 17:17 2024/8/20 | ||||||
|  | type IManager interface { | ||||||
|  | 	// SetDatabaseClient 设置数据库连接的客户端 | ||||||
|  | 	SetDatabaseClient(database IWrapperClient) | ||||||
|  | 	// Execute 执行 | ||||||
|  | 	Execute(ctx context.Context, databaseFlag string) (any, error) | ||||||
|  | } | ||||||
| @ -8,7 +8,7 @@ | |||||||
| package abstract | package abstract | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"git.zhangdeman.cn/zhangdeman/database" | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -19,21 +19,21 @@ import ( | |||||||
| // Date : 15:06 2023/10/14 | // Date : 15:06 2023/10/14 | ||||||
| type IDatabase interface { | type IDatabase interface { | ||||||
| 	// Create 创建数据 | 	// Create 创建数据 | ||||||
| 	Create(dbInstance *gorm.DB, data any, optionList ...database.SetOption) error | 	Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error | ||||||
| 	// Update 更新数据 | 	// Update 更新数据 | ||||||
| 	Update(dbInstance *gorm.DB, updateData any, optionFuncList ...database.SetOption) (int64, error) | 	Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) | ||||||
| 	// UpdateOne 更新一条数据 | 	// UpdateOne 更新一条数据 | ||||||
| 	UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...database.SetOption) (int64, error) | 	UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) | ||||||
| 	// List 查询数据列表 | 	// List 查询数据列表 | ||||||
| 	List(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error | 	List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error | ||||||
| 	// Delete 删除数据 | 	// Delete 删除数据 | ||||||
| 	Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...database.SetOption) (int64, error) | 	Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...define.SetOption) (int64, error) | ||||||
| 	// Detail 数据详情 | 	// Detail 数据详情 | ||||||
| 	Detail(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error | 	Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error | ||||||
| 	// IsNotFound 错误是否为数据不存在 | 	// IsNotFound 错误是否为数据不存在 | ||||||
| 	IsNotFound(err error) bool | 	IsNotFound(err error) bool | ||||||
| 	// Count 查询数据数量 | 	// Count 查询数据数量 | ||||||
| 	Count(dbInstance *gorm.DB, optionFuncList ...database.SetOption) (int64, error) | 	Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error) | ||||||
| 	// Tx 执行事务 | 	// Tx 执行事务 | ||||||
| 	Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error | 	Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error | ||||||
| 	// Begin 开启事务 | 	// Begin 开启事务 | ||||||
|  | |||||||
							
								
								
									
										35
									
								
								abstract/wrapper_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								abstract/wrapper_client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,35 @@ | |||||||
|  | // Package abstract ... | ||||||
|  | // | ||||||
|  | // Description : abstract ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-20 17:26 | ||||||
|  | package abstract | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IWrapperClient 包装的客户端, 包装 IWrapperDatabaseClient | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 17:26 2024/8/20 | ||||||
|  | type IWrapperClient interface { | ||||||
|  | 	// AddWithConfigFile 通过配置文件增加数据库实例 | ||||||
|  | 	AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error | ||||||
|  | 	// AddWithConfig 通过具体的配置增加数据库实例 | ||||||
|  | 	AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error | ||||||
|  | 	// BatchAddWithConfigDir 通过具体的配置列表增加数据库实例 | ||||||
|  | 	BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error | ||||||
|  | 	// GetDBClient 基于数据库标识获取数据库实例 | ||||||
|  | 	GetDBClient(dbFlag string) (IWrapperDatabaseClient, error) | ||||||
|  | 	// GetMasterClient 获取主库连接 | ||||||
|  | 	GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) | ||||||
|  | 	// GetSlaveClient 获取从库连接 | ||||||
|  | 	GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								abstract/wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								abstract/wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,37 @@ | |||||||
|  | // Package abstract ... | ||||||
|  | // | ||||||
|  | // Description : abstract ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-20 17:59 | ||||||
|  | package abstract | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // IWrapperDatabaseClient 包装gorm | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 18:00 2024/8/20 | ||||||
|  | type IWrapperDatabaseClient interface { | ||||||
|  | 	// Init 初始化客户端连接 | ||||||
|  | 	Init(databaseConfig *define.Database, cacheTableStructureConfig *define.CacheTableStructureConfig) error | ||||||
|  | 	// GetMaster 获取master连接 | ||||||
|  | 	GetMaster(ctx context.Context) *gorm.DB | ||||||
|  | 	// GetSlave 获取slave连接 | ||||||
|  | 	GetSlave(ctx context.Context) *gorm.DB | ||||||
|  | 	// GetDatabaseClient 获取数据库连接 | ||||||
|  | 	GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error) | ||||||
|  | 	// CacheDataTableStructureConfig 缓存数据表结构的配置 | ||||||
|  | 	CacheDataTableStructureConfig() *define.CacheTableStructureConfig | ||||||
|  | 	// GetTableFieldList 获取指定表数据字段列表 | ||||||
|  | 	GetTableFieldList(tableName string) ([]*define.ColumnConfig, error) | ||||||
|  | 	// SetTableStructure 设置数据表结构 | ||||||
|  | 	SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig) | ||||||
|  | } | ||||||
							
								
								
									
										281
									
								
								api2sql/execute.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										281
									
								
								api2sql/execute.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,281 @@ | |||||||
|  | // Package api2sql ... | ||||||
|  | // | ||||||
|  | // Description : api2sql ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-21 20:45 | ||||||
|  | package api2sql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/consts" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/abstract" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/wrapper" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	Exec = &execute{} | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type execute struct { | ||||||
|  | 	databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例 | ||||||
|  | 	baseDao               database.BaseDao        // 基础dao | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetDatabaseClientManager 设置数据库连接管理实例 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 21:06 2024/8/21 | ||||||
|  | func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapperClient) { | ||||||
|  | 	e.databaseClientManager = databaseClientManager | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Run 执行 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:48 2024/8/21 | ||||||
|  | func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	if err := e.formatAndValidateInputParam(inputParam); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if len(inputParam.InputSql) > 0 { | ||||||
|  | 		// 基于表达式执行 | ||||||
|  | 		return e.Express(ctx, inputParam) | ||||||
|  | 	} | ||||||
|  | 	switch inputParam.SqlType { | ||||||
|  | 	case consts.SqlTypeCount: | ||||||
|  | 		return e.Count(ctx, inputParam) | ||||||
|  | 	case consts.SqlTypeDetail: | ||||||
|  | 		return e.Detail(ctx, inputParam) | ||||||
|  | 	case consts.SqlTypeList: | ||||||
|  | 		return e.List(ctx, inputParam) | ||||||
|  | 	case consts.SqlTypeInsert: | ||||||
|  | 		return e.Insert(ctx, inputParam) | ||||||
|  | 	case consts.SqlTypeUpdate: | ||||||
|  | 		return e.Update(ctx, inputParam) | ||||||
|  | 	case consts.SqlTypeDelete: | ||||||
|  | 		return e.Delete(ctx, inputParam) | ||||||
|  | 	default: | ||||||
|  | 		return nil, errors.New(inputParam.SqlType + " : sql type is not support") | ||||||
|  |  | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // List 列表 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:52 2024/8/21 | ||||||
|  | func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	var ( | ||||||
|  | 		err        error | ||||||
|  | 		tx         *gorm.DB | ||||||
|  | 		optionList []define.SetOption | ||||||
|  | 	) | ||||||
|  | 	if tx, err = e.getTx(ctx, inputParam); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	if optionList, err = e.getOptionList(ctx, inputParam); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	// 动态生成结果解析结构体 | ||||||
|  | 	st := wrapper.NewDynamic() | ||||||
|  | 	for _, columnConfig := range inputParam.ColumnList { | ||||||
|  | 		tag := fmt.Sprintf(`gorm:"%v" json:"%v"`, columnConfig.Column, columnConfig.Alias) | ||||||
|  | 		column := wrapper.String(columnConfig.Column).SnakeCaseToCamel() | ||||||
|  | 		switch columnConfig.Type { | ||||||
|  | 		case "int", "int8", "int16", "int32", "int64": | ||||||
|  | 			st.AddInt(column, tag, "") | ||||||
|  | 		case "uint", "uint8", "uint16", "uint32", "uint64": | ||||||
|  | 			st.AddUint(column, tag, "") | ||||||
|  | 		case "bool": | ||||||
|  | 			st.AddBool(column, tag, "") | ||||||
|  | 		case "float": | ||||||
|  | 			st.AddBool(column, tag, "") | ||||||
|  | 		case "string": | ||||||
|  | 			st.AddString(column, tag, "") | ||||||
|  | 		case "map": | ||||||
|  | 			st.AddMap(column, tag, "") | ||||||
|  | 		case "slice": | ||||||
|  | 			st.AddSlice(column, tag, "") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	val := st.ToStructDefaultSliceValue() | ||||||
|  | 	if err = e.baseDao.List(tx, &val, optionList...); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 	return val, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Detail 详情 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:52 2024/8/21 | ||||||
|  | func (e *execute) Detail(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Insert 插入 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:52 2024/8/21 | ||||||
|  | func (e *execute) Insert(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Update 更新 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:52 2024/8/21 | ||||||
|  | func (e *execute) Update(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Count 数量 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:52 2024/8/21 | ||||||
|  | func (e *execute) Count(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Delete 删除 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:51 2024/8/21 | ||||||
|  | func (e *execute) Delete(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Express 基于表达式执行 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:51 2024/8/21 | ||||||
|  | func (e *execute) Express(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { | ||||||
|  | 	if nil == e.databaseClientManager { | ||||||
|  | 		return nil, errors.New("database client is nil, please use `SetDatabaseClientManager` set instance") | ||||||
|  | 	} | ||||||
|  | 	// 格式化 inputParam | ||||||
|  | 	return nil, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // formatAndValidateInputParam 格式化并校验输入参数 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 10:38 2024/8/22 | ||||||
|  | func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) error { | ||||||
|  | 	if nil == inputParam { | ||||||
|  | 		return errors.New("inputParam is nil") | ||||||
|  | 	} | ||||||
|  | 	if len(inputParam.DatabaseFlag) == 0 { | ||||||
|  | 		return errors.New("databaseFlag is empty") | ||||||
|  | 	} | ||||||
|  | 	if len(inputParam.Table) == 0 { | ||||||
|  | 		return errors.New("table is empty") | ||||||
|  | 	} | ||||||
|  | 	if len(inputParam.SqlType) == 0 { | ||||||
|  | 		return errors.New("sqlType is empty") | ||||||
|  | 	} | ||||||
|  | 	databaseClient, err := e.databaseClientManager.GetDBClient(inputParam.DatabaseFlag) | ||||||
|  | 	if nil != err { | ||||||
|  | 		return errors.New(inputParam.DatabaseFlag + " : database client get fail -> " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	// 尝试获取表结构, api 转 sql 要求必须开启表结构缓存, 否则无法确定相关数据如何解析 | ||||||
|  | 	if inputParam.TableColumnConfig, err = databaseClient.GetTableFieldList(inputParam.Table); nil != err { | ||||||
|  | 		return errors.New(inputParam.DatabaseFlag + " : get table field list fail -> " + err.Error()) | ||||||
|  | 	} | ||||||
|  | 	if len(inputParam.TableColumnConfig) == 0 { | ||||||
|  | 		return errors.New(inputParam.DatabaseFlag + " : table field list is empty, please enable `CacheDataTableStructureConfig` or `SetTableColumnConfig`") | ||||||
|  | 	} | ||||||
|  | 	// 操作字段列表为空 | ||||||
|  | 	if err = e.validateColumn(inputParam); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// 验证 force no limit | ||||||
|  | 	if inputParam.ForceNoLimit { | ||||||
|  | 		inputParam.Limit = 0 | ||||||
|  | 		inputParam.WithCount = false | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // validateColumn 验证表字段相关 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 11:48 2024/8/22 | ||||||
|  | func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { | ||||||
|  | 	if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{ | ||||||
|  | 		consts.SqlTypeList, consts.SqlTypeDetail, | ||||||
|  | 	}).Has(inputParam.SqlType) >= 0 { | ||||||
|  | 		return errors.New("column list is empty") | ||||||
|  | 	} | ||||||
|  | 	// 验证字段是否都正确 | ||||||
|  | 	tableColumnTable := make(map[string]bool) | ||||||
|  | 	for _, itemColumn := range inputParam.TableColumnConfig { | ||||||
|  | 		tableColumnTable[itemColumn.Column] = true | ||||||
|  | 	} | ||||||
|  | 	for _, columnConfig := range inputParam.ColumnList { | ||||||
|  | 		if !tableColumnTable[columnConfig.Column] { | ||||||
|  | 			return errors.New(columnConfig.Column + " : input column not found in table column list") | ||||||
|  | 		} | ||||||
|  | 		if len(columnConfig.Alias) == 0 { | ||||||
|  | 			columnConfig.Alias = columnConfig.Column | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getTx 获取数据库连接 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 12:20 2024/8/23 | ||||||
|  | func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (*gorm.DB, error) { | ||||||
|  | 	if nil != inputParam.Tx { | ||||||
|  | 		return inputParam.Tx, nil | ||||||
|  | 	} | ||||||
|  | 	if inputParam.ForceMaster || // 强制操作主库 | ||||||
|  | 		wrapper.ArrayType[string]([]string{consts.SqlTypeDelete, consts.SqlTypeInsert, consts.SqlTypeUpdate}).Has(inputParam.SqlType) >= 0 { // 写操作 | ||||||
|  | 		return e.databaseClientManager.GetMasterClient(ctx, inputParam.DatabaseFlag) | ||||||
|  | 	} | ||||||
|  | 	return e.databaseClientManager.GetSlaveClient(ctx, inputParam.DatabaseFlag) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getOptionList 设置where条件 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 12:31 2024/8/23 | ||||||
|  | func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]define.SetOption, error) { | ||||||
|  | 	optionList := []define.SetOption{ | ||||||
|  | 		database.WithTable(inputParam.Table), | ||||||
|  | 	} | ||||||
|  | 	// 设置 limit offset | ||||||
|  | 	if !inputParam.ForceNoLimit && inputParam.Limit > 0 { | ||||||
|  | 		optionList = append(optionList, database.WithLimit(inputParam.Limit, inputParam.Offset)) | ||||||
|  | 	} | ||||||
|  | 	for _, item := range inputParam.ConditionList { | ||||||
|  | 		optionFunc, err := database.WithAnyCondition(item.Column, item.Operate, item.Value) | ||||||
|  | 		if nil != err { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		optionList = append(optionList, optionFunc) | ||||||
|  | 	} | ||||||
|  | 	return optionList, nil | ||||||
|  | } | ||||||
							
								
								
									
										74
									
								
								api2sql/execute_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								api2sql/execute_test.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,74 @@ | |||||||
|  | // Package api2sql ... | ||||||
|  | // | ||||||
|  | // Description : api2sql ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-23 17:36 | ||||||
|  | package api2sql | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"encoding/json" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/consts" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"reflect" | ||||||
|  | 	"testing" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func Test_execute_Run(t *testing.T) { | ||||||
|  | 	clientManager := database.NewWrapperClient() | ||||||
|  | 	if err := clientManager.AddWithConfig("TEST_DATABASE", nil, &define.Database{ | ||||||
|  | 		Master: &define.Driver{ | ||||||
|  | 			DBType: "sqlite3", | ||||||
|  | 			Host:   "/tmp/gateway.db", | ||||||
|  | 		}, | ||||||
|  | 		Slave: &define.Driver{ | ||||||
|  | 			DBType: "sqlite3", | ||||||
|  | 			Host:   "/tmp/gateway.db", | ||||||
|  | 		}, | ||||||
|  | 	}, []string{}); nil != err { | ||||||
|  | 		panic(err.Error()) | ||||||
|  | 	} | ||||||
|  | 	dbClient, _ := clientManager.GetDBClient("TEST_DATABASE") | ||||||
|  |  | ||||||
|  | 	dbClient.SetTableStructure(map[string][]*define.ColumnConfig{ | ||||||
|  | 		"project": []*define.ColumnConfig{ | ||||||
|  | 			&define.ColumnConfig{ | ||||||
|  | 				Column: "flag", | ||||||
|  | 				Alias:  "project_flag", | ||||||
|  | 				Type:   "string", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	}) | ||||||
|  | 	Exec.SetDatabaseClientManager(clientManager) | ||||||
|  | 	res, err := Exec.List(context.Background(), &define.Api2SqlParam{ | ||||||
|  | 		DatabaseFlag:     "TEST_DATABASE", | ||||||
|  | 		Table:            "project", | ||||||
|  | 		ForceMaster:      false, | ||||||
|  | 		InputSql:         "", | ||||||
|  | 		TableSplitConfig: define.TableSplitConfig{}, | ||||||
|  | 		SqlType:          consts.SqlTypeList, | ||||||
|  | 		ColumnList: []*define.ColumnConfig{ | ||||||
|  | 			&define.ColumnConfig{ | ||||||
|  | 				Column: "flag", | ||||||
|  | 				Alias:  "project_flag", | ||||||
|  | 				Type:   "string", | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 		Limit:             0, | ||||||
|  | 		Offset:            0, | ||||||
|  | 		ForceNoLimit:      false, | ||||||
|  | 		OrderField:        "id", | ||||||
|  | 		OrderRule:         "desc", | ||||||
|  | 		WithCount:         false, | ||||||
|  | 		ConditionList:     nil, | ||||||
|  | 		TableColumnConfig: nil, | ||||||
|  | 		Tx:                nil, | ||||||
|  | 	}) | ||||||
|  | 	byteData, _ := json.Marshal(res) | ||||||
|  | 	tt := reflect.TypeOf(res) | ||||||
|  | 	fmt.Println(tt.String(), res, err, string(byteData)) | ||||||
|  | } | ||||||
							
								
								
									
										90
									
								
								base.go
									
									
									
									
									
								
							
							
						
						
									
										90
									
								
								base.go
									
									
									
									
									
								
							| @ -7,6 +7,7 @@ package database | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  |  | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
| @ -25,8 +26,8 @@ type BaseDao struct { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 8:06 下午 2021/8/8 | // Date : 8:06 下午 2021/8/8 | ||||||
| func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption) error { | func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error { | ||||||
| 	o := &Option{} | 	o := &define.Option{} | ||||||
| 	for _, itemFunc := range optionList { | 	for _, itemFunc := range optionList { | ||||||
| 		itemFunc(o) | 		itemFunc(o) | ||||||
| 	} | 	} | ||||||
| @ -42,7 +43,7 @@ func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption) | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 8:12 下午 2021/8/8 | // Date : 8:12 下午 2021/8/8 | ||||||
| func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...SetOption) (int64, error) { | func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) { | ||||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||||
| 	r := dbInstance.Updates(updateData) | 	r := dbInstance.Updates(updateData) | ||||||
| 	return r.RowsAffected, r.Error | 	return r.RowsAffected, r.Error | ||||||
| @ -53,7 +54,7 @@ func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ... | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:05 2024/1/13 | // Date : 17:05 2024/1/13 | ||||||
| func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...SetOption) (int64, error) { | func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) { | ||||||
| 	optionFuncList = append(optionFuncList, WithLimit(1, 0)) | 	optionFuncList = append(optionFuncList, WithLimit(1, 0)) | ||||||
| 	return b.Update(dbInstance, updateData, optionFuncList...) | 	return b.Update(dbInstance, updateData, optionFuncList...) | ||||||
| } | } | ||||||
| @ -63,7 +64,7 @@ func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 8:14 下午 2021/8/8 | // Date : 8:14 下午 2021/8/8 | ||||||
| func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...SetOption) error { | func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error { | ||||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||||
| 	return dbInstance.Find(result).Error | 	return dbInstance.Find(result).Error | ||||||
| } | } | ||||||
| @ -73,7 +74,7 @@ func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...SetOpt | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 11:46 2023/2/9 | // Date : 11:46 2023/2/9 | ||||||
| func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...SetOption) (int64, error) { | func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...define.SetOption) (int64, error) { | ||||||
| 	dbInstance = dbInstance.Model(dataModel) | 	dbInstance = dbInstance.Model(dataModel) | ||||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...).Delete(dataModel) | 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...).Delete(dataModel) | ||||||
| 	return dbInstance.RowsAffected, dbInstance.Error | 	return dbInstance.RowsAffected, dbInstance.Error | ||||||
| @ -84,7 +85,7 @@ func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...S | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 8:25 下午 2021/8/8 | // Date : 8:25 下午 2021/8/8 | ||||||
| func (b *BaseDao) Detail(dbInstance *gorm.DB, result any, optionFuncList ...SetOption) error { | func (b *BaseDao) Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error { | ||||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||||
| 	return dbInstance.First(result).Error | 	return dbInstance.First(result).Error | ||||||
| } | } | ||||||
| @ -118,7 +119,7 @@ func (b *BaseDao) IsNotFound(err error) bool { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 8:25 下午 2021/8/8 | // Date : 8:25 下午 2021/8/8 | ||||||
| func (b *BaseDao) Count(dbInstance *gorm.DB, optionFuncList ...SetOption) (int64, error) { | func (b *BaseDao) Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error) { | ||||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||||
| 	var cnt int64 | 	var cnt int64 | ||||||
| 	return cnt, dbInstance.Count(&cnt).Error | 	return cnt, dbInstance.Count(&cnt).Error | ||||||
| @ -173,10 +174,10 @@ func (b *BaseDao) Rollback(db *gorm.DB) error { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:38 2022/5/15 | // Date : 17:38 2022/5/15 | ||||||
| func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm.DB { | func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...define.SetOption) *gorm.DB { | ||||||
|  |  | ||||||
| 	// 构建查询条件 | 	// 构建查询条件 | ||||||
| 	o := &Option{} | 	o := &define.Option{} | ||||||
| 	for _, fn := range optionFuncList { | 	for _, fn := range optionFuncList { | ||||||
| 		fn(o) | 		fn(o) | ||||||
| 	} | 	} | ||||||
| @ -199,7 +200,72 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm | |||||||
| 			tx = tx.Offset(o.Offset) | 			tx = tx.Offset(o.Offset) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	sqlBlockList := make([]string, 0) | 	// in 语句 | ||||||
|  | 	if nil != o.In { | ||||||
|  | 		tx = tx.Where(o.In) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// not in 语句 | ||||||
|  | 	if nil != o.NotIn { | ||||||
|  | 		for field, value := range o.NotIn { | ||||||
|  | 			tx = tx.Where(field+" NOT IN ? ", value) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// like 语句 | ||||||
|  | 	if nil != o.Like { | ||||||
|  | 		for field, value := range o.Like { | ||||||
|  | 			tx = tx.Where(field+" LIKE ? ", "%"+value+"%") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// NOT LIKE 语句 | ||||||
|  | 	if nil != o.NotLike { | ||||||
|  | 		for field, value := range o.NotLike { | ||||||
|  | 			tx = tx.Where(field+" NOT LIKE ? ", "%"+value+"%") | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// >= | ||||||
|  | 	if nil != o.Start { | ||||||
|  | 		for field, value := range o.Start { | ||||||
|  | 			tx = tx.Where(field+" >= ?", value) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// < | ||||||
|  | 	if nil != o.End { | ||||||
|  | 		for field, value := range o.End { | ||||||
|  | 			tx = tx.Where(field+" < ?", value) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// between | ||||||
|  | 	for field, betweenVal := range o.Between { | ||||||
|  | 		tx = tx.Where("`"+field+"` BETWEEN ? AND ?", betweenVal[0], betweenVal[1]) | ||||||
|  | 	} | ||||||
|  | 	// not between | ||||||
|  | 	for field, notBetweenVal := range o.NotBetween { | ||||||
|  | 		tx = tx.Where("`"+field+"` NOT BETWEEN ? AND ?", notBetweenVal[0], notBetweenVal[1]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// 排序 | ||||||
|  | 	for _, orderRule := range o.Order { | ||||||
|  | 		tx = tx.Order(orderRule) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	// or 语句 | ||||||
|  | 	if nil != o.OR { | ||||||
|  | 		for _, itemOr := range o.OR { | ||||||
|  | 			orOption := &define.Option{} | ||||||
|  | 			for _, fn := range itemOr { | ||||||
|  | 				fn(orOption) | ||||||
|  | 			} | ||||||
|  | 			orSql, orBindVal := optionToSql(orOption) | ||||||
|  | 			tx.Or(orSql, orBindVal) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	/*sqlBlockList := make([]string, 0) | ||||||
| 	bindValueList := make([]any, 0) | 	bindValueList := make([]any, 0) | ||||||
| 	sql, bindVal := optionToSql(o) | 	sql, bindVal := optionToSql(o) | ||||||
| 	tx.Or(sql, bindVal...) | 	tx.Or(sql, bindVal...) | ||||||
| @ -212,6 +278,6 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm | |||||||
| 		} | 		} | ||||||
| 		orSql, orBindVal := optionToSql(orOption) | 		orSql, orBindVal := optionToSql(orOption) | ||||||
| 		tx.Or(orSql, orBindVal...) | 		tx.Or(orSql, orBindVal...) | ||||||
| 	} | 	}*/ | ||||||
| 	return tx | 	return tx | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										327
									
								
								client.go
									
									
									
									
									
								
							
							
						
						
									
										327
									
								
								client.go
									
									
									
									
									
								
							| @ -1,327 +0,0 @@ | |||||||
| // Package database ... |  | ||||||
| // |  | ||||||
| // Description : mysql客户端 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 2021-03-01 9:20 下午 |  | ||||||
| package database |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"context" |  | ||||||
| 	"errors" |  | ||||||
| 	"fmt" |  | ||||||
| 	"git.zhangdeman.cn/zhangdeman/consts" |  | ||||||
| 	"git.zhangdeman.cn/zhangdeman/serialize" |  | ||||||
| 	"path/filepath" |  | ||||||
| 	"strings" |  | ||||||
| 	"sync" |  | ||||||
|  |  | ||||||
| 	"git.zhangdeman.cn/zhangdeman/logger/wrapper" |  | ||||||
| 	"go.uber.org/zap" |  | ||||||
|  |  | ||||||
| 	gormLogger "gorm.io/gorm/logger" |  | ||||||
|  |  | ||||||
| 	"gorm.io/driver/mysql" |  | ||||||
| 	"gorm.io/driver/sqlite" |  | ||||||
| 	"gorm.io/gorm" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| var ( |  | ||||||
| 	// Client mysql客户端 |  | ||||||
| 	Client *client |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func init() { |  | ||||||
| 	Client = &client{ |  | ||||||
| 		lock:        &sync.RWMutex{}, |  | ||||||
| 		clientTable: make(map[string]*DBClient), |  | ||||||
| 	} |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type client struct { |  | ||||||
| 	lock        *sync.RWMutex |  | ||||||
| 	clientTable map[string]*DBClient |  | ||||||
| 	logger      *zap.Logger |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AddWithConfigFile 使用文件生成新的客户端,文件名去掉后缀作为flag |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 19:19 2022/6/5 |  | ||||||
| func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error { |  | ||||||
| 	var ( |  | ||||||
| 		err error |  | ||||||
| 		cfg *cfgFile |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	if cfg, err = c.getCfg(cfgFilePath); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if nil == cfg { |  | ||||||
| 		// 不支持的配置文件格式 |  | ||||||
| 		return nil |  | ||||||
| 	} |  | ||||||
| 	return c.AddWithConfig(cfg.Flag, logInstance, cfg.Config, extraFieldList) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // AddWithConfig ... |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 20:41 2023/4/18 |  | ||||||
| func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *Database, extraFieldList []string) error { |  | ||||||
| 	dbClient := &DBClient{ |  | ||||||
| 		dbFlag:         flag, |  | ||||||
| 		loggerInstance: logInstance, |  | ||||||
| 		master:         nil, |  | ||||||
| 		slave:          nil, |  | ||||||
| 		extraFieldList: extraFieldList, |  | ||||||
| 		cfg:            Driver{}, |  | ||||||
| 	} |  | ||||||
| 	var err error |  | ||||||
| 	if dbClient.master, err = c.GetDatabaseClient(databaseConfig.Master, logInstance); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	if dbClient.slave, err = c.GetDatabaseClient(databaseConfig.Slave, logInstance); nil != err { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	c.lock.Lock() |  | ||||||
| 	c.clientTable[dbClient.dbFlag] = dbClient |  | ||||||
| 	c.lock.Unlock() |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // BatchAddWithConfigDir 自动读取目录下配置文件, 生成客户端 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 19:19 2022/6/5 |  | ||||||
| func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error { |  | ||||||
| 	filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*")) |  | ||||||
| 	for i := range filepathNames { |  | ||||||
| 		if err := c.AddWithConfigFile(filepathNames[i], logInstance, extraFieldList); nil != err { |  | ||||||
| 			return err |  | ||||||
| 		} |  | ||||||
| 	} |  | ||||||
| 	return nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getCfg 读取配置 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 18:05 2022/6/11 |  | ||||||
| func (c *client) getCfg(cfgPath string) (*cfgFile, error) { |  | ||||||
| 	fileArr := strings.Split(cfgPath, ".") |  | ||||||
| 	if len(fileArr) < 2 { |  | ||||||
| 		// 获取不到类型 |  | ||||||
| 		return nil, errors.New("文件格式必须是JSON或者YAML") |  | ||||||
| 	} |  | ||||||
| 	fileType := strings.ToLower(fileArr[len(fileArr)-1]) |  | ||||||
| 	fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator)) |  | ||||||
| 	result := &cfgFile{ |  | ||||||
| 		Path:   cfgPath, |  | ||||||
| 		Type:   "", |  | ||||||
| 		Flag:   fileFlagArr[len(fileFlagArr)-1], |  | ||||||
| 		Config: &Database{}, |  | ||||||
| 	} |  | ||||||
| 	var ( |  | ||||||
| 		err     error |  | ||||||
| 		cfgInfo Database |  | ||||||
| 	) |  | ||||||
| 	switch fileType { |  | ||||||
| 	case consts.FileTypeYaml: |  | ||||||
| 		fallthrough |  | ||||||
| 	case consts.FileTypeYml: |  | ||||||
| 		result.Type = consts.FileTypeYaml |  | ||||||
| 		if err = serialize.File.ReadYmlContent(cfgPath, &result.Config); nil != err { |  | ||||||
| 			return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error()) |  | ||||||
| 		} |  | ||||||
| 	case consts.FileTypeJson: |  | ||||||
| 		result.Type = consts.FileTypeJson |  | ||||||
| 		if err = serialize.File.ReadJSONContent(cfgPath, &cfgInfo); nil != err { |  | ||||||
| 			return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error()) |  | ||||||
| 		} |  | ||||||
| 	default: |  | ||||||
| 		// 不是JSON , 也不是YML, 跳过 |  | ||||||
| 		return nil, nil |  | ||||||
| 	} |  | ||||||
| 	if len(result.Config.Master.Timezone) == 0 { |  | ||||||
| 		// 默认使用本地时区 |  | ||||||
| 		result.Config.Master.Timezone = "Local" |  | ||||||
| 	} else { |  | ||||||
| 		result.Config.Slave.Timezone = result.Config.Master.Timezone |  | ||||||
| 	} |  | ||||||
| 	return result, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetDBClient 获取db client |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 19:32 2022/6/5 |  | ||||||
| func (c *client) GetDBClient(dbFlag string) (*DBClient, error) { |  | ||||||
| 	c.lock.RLock() |  | ||||||
| 	defer c.lock.RUnlock() |  | ||||||
| 	var ( |  | ||||||
| 		exist    bool |  | ||||||
| 		dbClient *DBClient |  | ||||||
| 	) |  | ||||||
| 	if dbClient, exist = c.clientTable[dbFlag]; !exist { |  | ||||||
| 		return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag) |  | ||||||
| 	} |  | ||||||
| 	return dbClient, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetMasterClient 获取主库客户端 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 19:36 2022/6/5 |  | ||||||
| func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { |  | ||||||
| 	var ( |  | ||||||
| 		err      error |  | ||||||
| 		dbClient *DBClient |  | ||||||
| 	) |  | ||||||
| 	if dbClient, err = c.GetDBClient(dbFlag); nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	session := dbClient.master.Session(&gorm.Session{}) |  | ||||||
| 	session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-master") |  | ||||||
| 	return session, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetSlaveClient 获取从库客户端 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 19:37 2022/6/5 |  | ||||||
| func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { |  | ||||||
| 	var ( |  | ||||||
| 		err      error |  | ||||||
| 		dbClient *DBClient |  | ||||||
| 	) |  | ||||||
| 	if dbClient, err = c.GetDBClient(dbFlag); nil != err { |  | ||||||
| 		return nil, err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	session := dbClient.slave.Session(&gorm.Session{}) |  | ||||||
| 	session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-slave") |  | ||||||
| 	return session, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getGormClient 获取GORM client方法 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 11:24 2022/6/6 |  | ||||||
| func (c *client) getGormClient() (*gorm.DB, error) { |  | ||||||
| 	return nil, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetDatabaseClient 获取数据库连接 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 18:41 2022/6/11 |  | ||||||
| func (c *client) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm.DB, error) { |  | ||||||
| 	var ( |  | ||||||
| 		instance *gorm.DB |  | ||||||
| 		err      error |  | ||||||
| 	) |  | ||||||
|  |  | ||||||
| 	if conf.DBType == consts.DatabaseDriverMysql { |  | ||||||
| 		if instance, err = gorm.Open(mysql.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} else if conf.DBType == consts.DatabaseDriverSqlite3 { |  | ||||||
| 		if instance, err = gorm.Open(sqlite.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { |  | ||||||
| 			return nil, err |  | ||||||
| 		} |  | ||||||
| 	} else { |  | ||||||
| 		return nil, fmt.Errorf("%v : db driver is not support", conf.DBType) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil) |  | ||||||
|  |  | ||||||
| 	return instance, nil |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // buildConnectionDSN 构建连接信息 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 18:42 2022/6/11 |  | ||||||
| func (c *client) buildConnectionDSN(conf *Driver) string { |  | ||||||
| 	if conf.DBType == consts.DatabaseDriverSqlite3 { |  | ||||||
| 		// 兼容sqlite3 |  | ||||||
| 		return conf.Host |  | ||||||
| 	} |  | ||||||
| 	return fmt.Sprintf( |  | ||||||
| 		"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s", |  | ||||||
| 		conf.Username, |  | ||||||
| 		conf.Password, |  | ||||||
| 		conf.Host, |  | ||||||
| 		conf.Port, |  | ||||||
| 		conf.Database, |  | ||||||
| 		conf.Charset, |  | ||||||
| 		conf.Timezone, |  | ||||||
| 	) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // DBClient 包装日志实例 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 3:09 PM 2021/12/24 |  | ||||||
| type DBClient struct { |  | ||||||
| 	dbFlag         string      // 数据库标识 |  | ||||||
| 	loggerInstance *zap.Logger // 日志实例 |  | ||||||
| 	master         *gorm.DB    // 主库 |  | ||||||
| 	slave          *gorm.DB    // 从库 |  | ||||||
| 	extraFieldList []string    // 提取的字段 |  | ||||||
| 	cfg            Driver      // 数据库配置 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // SetFlag 设置数据库标识 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 16:18 2022/6/5 |  | ||||||
| func (dc *DBClient) SetFlag(dbFlag string) { |  | ||||||
| 	dc.dbFlag = dbFlag |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetMaster 获取主库连接 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 3:28 PM 2021/12/24 |  | ||||||
| func (dc *DBClient) GetMaster(ctx context.Context) *gorm.DB { |  | ||||||
| 	session := dc.master.Session(&gorm.Session{}) |  | ||||||
| 	session.Logger = dc.getLogger(ctx, session, "slave") |  | ||||||
| 	return session |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // GetSlave 获取从库链接 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 3:29 PM 2021/12/24 |  | ||||||
| func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB { |  | ||||||
| 	session := dc.slave.Session(&gorm.Session{}) |  | ||||||
| 	session.Logger = dc.getLogger(ctx, session, "slave") |  | ||||||
| 	return session |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // getLogger 获取日志实例 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 3:45 PM 2021/12/24 |  | ||||||
| func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node string) gormLogger.Interface { |  | ||||||
| 	return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.loggerInstance, dc.dbFlag+"|"+node, dc.extraFieldList) |  | ||||||
| } |  | ||||||
							
								
								
									
										72
									
								
								define/api2sql.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								define/api2sql.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,72 @@ | |||||||
|  | // Package define ... | ||||||
|  | // | ||||||
|  | // Description : define ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-21 16:05 | ||||||
|  | package define | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // Api2SqlParam 接口转sql的输入配置 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 16:06 2024/8/21 | ||||||
|  | // | ||||||
|  | // 一个合法的 sql 配置格式 : | ||||||
|  | // | ||||||
|  | // select 语句 : SELECT {FIELD_LIST} FROM {TABLE} WHERE {WHERE} ORDER BY {ORDER_FIELD} {ORDER_RULE} LIMIT {LIMIT} OFFSET {OFFSET} | ||||||
|  | // | ||||||
|  | // insert 语句 : INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST}) | ||||||
|  | // | ||||||
|  | // update 语句 : UPDATE {TABLE} SET {SET} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE} | ||||||
|  | // | ||||||
|  | // delete 语句 : DELETE FROM {TABLE} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE} | ||||||
|  | // | ||||||
|  | // count 语句 : SELECT COUNT(*) as count FROM {TABLE} WHERE {WHERE} | ||||||
|  | type Api2SqlParam struct { | ||||||
|  | 	DatabaseFlag      string           `json:"database_flag"`       // 数据库标识 | ||||||
|  | 	Table             string           `json:"table"`               // 操作的数据表 | ||||||
|  | 	ForceMaster       bool             `json:"force_master"`        // 针对查询语句, 是否强制读主, 如果查询语句在事务中, 默认强制读主 | ||||||
|  | 	InputSql          string           `json:"input_sql"`           // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置 | ||||||
|  | 	TableSplitConfig  TableSplitConfig `json:"table_split_config"`  // 分表配置 | ||||||
|  | 	SqlType           string           `json:"sql_type"`            // sql语句类型 : detail - 查询详情 list - 查询列表  count - 查询数量 update - 更新 insert - 插入 delete - 删除 | ||||||
|  | 	ColumnList        []*ColumnConfig  `json:"column_list"`         // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出 | ||||||
|  | 	Limit             int              `json:"limit"`               // 操作数量 | ||||||
|  | 	Offset            int              `json:"offset"`              // 操作偏移量 | ||||||
|  | 	ForceNoLimit      bool             `json:"force_no_limit"`      // 强制允许不限制 : 正常操作 select / delete / update 均需要指定本次操作数据量, 如果确定不限制, 此参数设置为 `true` , sqlType = list , 且 ForceNoLimit = true 时,  WithCount 参数无效 | ||||||
|  | 	OrderField        string           `json:"order_field"`         // 排序字段, 仅 sqlType = list 生效 | ||||||
|  | 	OrderRule         string           `json:"order_rule"`          // 排序规则, Asc / Desc | ||||||
|  | 	WithCount         bool             `json:"with_count"`          // 是否返回数据总量, 仅 sqlType = list 生效 | ||||||
|  | 	ConditionList     []SqlCondition   `json:"value_list"`          // 字段列表 | ||||||
|  | 	TableColumnConfig []*ColumnConfig  `json:"table_column_config"` // 表字段配置 | ||||||
|  | 	Tx                *gorm.DB         `json:"-"`                   // 前后已有的数据库连接, 直接复用 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // TableSplitConfig 分表配置 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 14:42 2024/8/23 | ||||||
|  | type TableSplitConfig struct { | ||||||
|  | 	IsSplit       bool   `json:"is_split"`       // 是否分表 | ||||||
|  | 	SplitField    string `json:"split_field"`    // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 | ||||||
|  | 	FieldValue    any    `json:"field_value"`    // 分表字段值 | ||||||
|  | 	SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 | ||||||
|  | 	TableCnt      int64  `json:"table_cnt"`      // 一共分表多少张 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SqlCondition sql条件 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 14:46 2024/8/23 | ||||||
|  | type SqlCondition struct { | ||||||
|  | 	Column  string `json:"column"`  // 表字段 | ||||||
|  | 	Operate string `json:"operate"` // 操作 : == / !== / in / not in / like / not like | ||||||
|  | 	Value   any    `json:"value"`   // 数据值 | ||||||
|  | } | ||||||
| @ -1,11 +1,11 @@ | |||||||
| // package database... | // Package define ... | ||||||
| // | // | ||||||
| // Description : 数据定义 | // Description : 数据定义 | ||||||
| // | // | ||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 2021-03-01 9:27 下午 | // Date : 2021-03-01 9:27 下午 | ||||||
| package database | package define | ||||||
| 
 | 
 | ||||||
| // DBConfig 数据库连接的配置 | // DBConfig 数据库连接的配置 | ||||||
| // | // | ||||||
| @ -22,12 +22,12 @@ type DBConfig struct { | |||||||
| 	Connection Connection `json:"connection" yaml:"connection"` // 连接数量配置 | 	Connection Connection `json:"connection" yaml:"connection"` // 连接数量配置 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // cfgFile 配置文件定义 | // CfgFile 配置文件定义 | ||||||
| // | // | ||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 14:47 2022/6/9 | // Date : 14:47 2022/6/9 | ||||||
| type cfgFile struct { | type CfgFile struct { | ||||||
| 	Flag   string    `json:"flag"`   // 数据库标识 | 	Flag   string    `json:"flag"`   // 数据库标识 | ||||||
| 	Path   string    `json:"path"`   // 配置文件路径 | 	Path   string    `json:"path"`   // 配置文件路径 | ||||||
| 	Type   string    `json:"type"`   // 配置文件类型 | 	Type   string    `json:"type"`   // 配置文件类型 | ||||||
							
								
								
									
										39
									
								
								define/sql_option.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								define/sql_option.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,39 @@ | |||||||
|  | // Package define ... | ||||||
|  | // | ||||||
|  | // Description : define ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-23 17:46 | ||||||
|  | package define | ||||||
|  |  | ||||||
|  | // SetOption 设置选项 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 11:46 2022/5/15 | ||||||
|  | type SetOption func(o *Option) | ||||||
|  |  | ||||||
|  | // Option 扩展选项 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 8:05 下午 2021/8/8 | ||||||
|  | type Option struct { | ||||||
|  | 	Model      any               `json:"-"`           // 操作model | ||||||
|  | 	Table      string            `json:"table"`       // 查询的数据表 | ||||||
|  | 	Limit      int               `json:"limit"`       // 限制数量 | ||||||
|  | 	Offset     int               `json:"offset"`      // 偏移量 | ||||||
|  | 	In         map[string]any    `json:"in"`          // in语句 | ||||||
|  | 	NotIn      map[string]any    `json:"not_in"`      // not in语句 | ||||||
|  | 	Where      map[string]any    `json:"where"`       // where 条件 | ||||||
|  | 	Between    map[string][2]any `json:"between"`     // between 条件 | ||||||
|  | 	NotBetween map[string][2]any `json:"not_between"` // not between 条件 | ||||||
|  | 	Start      map[string]any    `json:"start"`       // >= 条件 | ||||||
|  | 	End        map[string]any    `json:"end"`         // < 条件 | ||||||
|  | 	Like       map[string]string `json:"like"`        // like 语句 | ||||||
|  | 	NotLike    map[string]string `json:"not_like"`    // not like 语句 | ||||||
|  | 	NotEqual   map[string]any    `json:"not_equal"`   // != 语句 | ||||||
|  | 	Order      []string          `json:"order"`       // 排序规则 | ||||||
|  | 	OR         [][]SetOption     `json:"or"`          // or 语句, or 和其他属性 and 关系, 内部 OR 关系 | ||||||
|  | } | ||||||
							
								
								
									
										29
									
								
								define/table.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								define/table.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | // Package define ... | ||||||
|  | // | ||||||
|  | // Description : define ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-21 14:56 | ||||||
|  | package define | ||||||
|  |  | ||||||
|  | // CacheTableStructureConfig 缓存表结构的配置 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 14:56 2024/8/21 | ||||||
|  | type CacheTableStructureConfig struct { | ||||||
|  | 	Enable           bool `json:"enable"`             // 是否启用表结构缓存 | ||||||
|  | 	SyncTimeInterval int  `json:"sync_time_interval"` // 开启的情况向, 多久同步一次表结构, 默认值 3600, 单位 : s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // ColumnConfig ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 16:42 2024/8/23 | ||||||
|  | type ColumnConfig struct { | ||||||
|  | 	Column string `json:"column"` // 字段名 | ||||||
|  | 	Alias  string `json:"alias"`  // 字段别名 | ||||||
|  | 	Type   string `json:"type"`   // 字段类型 | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								go.mod
									
									
									
									
									
								
							| @ -5,11 +5,11 @@ go 1.21 | |||||||
| toolchain go1.21.3 | toolchain go1.21.3 | ||||||
|  |  | ||||||
| require ( | require ( | ||||||
| 	git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 | 	git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5 | ||||||
| 	git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a | 	git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a | ||||||
| 	git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 | 	git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 | ||||||
| 	git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd | 	git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd | ||||||
| 	git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b | 	git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3 | ||||||
| 	github.com/pkg/errors v0.9.1 | 	github.com/pkg/errors v0.9.1 | ||||||
| 	github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 | 	github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 | ||||||
| 	go.uber.org/zap v1.27.0 | 	go.uber.org/zap v1.27.0 | ||||||
|  | |||||||
							
								
								
									
										8
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										8
									
								
								go.sum
									
									
									
									
									
								
							| @ -4,6 +4,10 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyz | |||||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 h1:U12XDtyRrmsqb/wRvRZG9+SBKMCGFNADpiLogsp5POw= | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 h1:U12XDtyRrmsqb/wRvRZG9+SBKMCGFNADpiLogsp5POw= | ||||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||||
|  | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb h1:dNnVYynCQhLSbtNUVQsvDIthkNgpTrAJF4dAEj08FdE= | ||||||
|  | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||||
|  | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5 h1:pmIHln0gWW+5xAB762h3WDsRkZuYLUDndvJDsGMKoOY= | ||||||
|  | git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||||
| git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI= | git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI= | ||||||
| git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0= | git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0= | ||||||
| git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= | git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= | ||||||
| @ -28,6 +32,10 @@ git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfm | |||||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= | ||||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t7ssBSZwE7BDTOkCSgOvTGATarOZ0tajZ00McEc= | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t7ssBSZwE7BDTOkCSgOvTGATarOZ0tajZ00McEc= | ||||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= | ||||||
|  | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 h1:+P+7IDkfOYII4K7BaPiZhnzYgPpJ7mv2hhUSZcxCWiI= | ||||||
|  | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8= | ||||||
|  | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3 h1:RcWNxrHmhZksZWrP/HLEwAM8uIIHYlPLQ20HnLzC+j0= | ||||||
|  | git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8= | ||||||
| github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= | github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= | ||||||
| github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= | github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= | ||||||
| github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= | github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= | ||||||
|  | |||||||
							
								
								
									
										208
									
								
								option.go
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								option.go
									
									
									
									
									
								
							| @ -9,47 +9,23 @@ package database | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/consts" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
| 	"git.zhangdeman.cn/zhangdeman/op_type" | 	"git.zhangdeman.cn/zhangdeman/op_type" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/serialize" | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| // SetOption 设置选项 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 11:46 2022/5/15 |  | ||||||
| type SetOption func(o *Option) |  | ||||||
|  |  | ||||||
| // Option 扩展选项 |  | ||||||
| // |  | ||||||
| // Author : go_developer@163.com<白茶清欢> |  | ||||||
| // |  | ||||||
| // Date : 8:05 下午 2021/8/8 |  | ||||||
| type Option struct { |  | ||||||
| 	Model    any               `json:"-"`         // 操作model |  | ||||||
| 	Table    string            `json:"table"`     // 查询的数据表 |  | ||||||
| 	Limit    int               `json:"limit"`     // 限制数量 |  | ||||||
| 	Offset   int               `json:"offset"`    // 偏移量 |  | ||||||
| 	In       map[string]any    `json:"in"`        // in语句 |  | ||||||
| 	NotIn    map[string]any    `json:"not_in"`    // not in语句 |  | ||||||
| 	Where    map[string]any    `json:"where"`     // where 条件 |  | ||||||
| 	Start    map[string]any    `json:"start"`     // >= 条件 |  | ||||||
| 	End      map[string]any    `json:"end"`       // < 条件 |  | ||||||
| 	Like     map[string]string `json:"like"`      // like 语句 |  | ||||||
| 	NotLike  map[string]string `json:"not_like"`  // not like 语句 |  | ||||||
| 	NotEqual map[string]any    `json:"not_equal"` // != 语句 |  | ||||||
| 	Order    []string          `json:"order"`     // 排序规则 |  | ||||||
| 	OR       [][]SetOption     `json:"or"`        // or 语句, or 和其他属性 and 关系, 内部 OR 关系 |  | ||||||
| } |  | ||||||
|  |  | ||||||
| // WithModel ... | // WithModel ... | ||||||
| // | // | ||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 15:18 2024/1/15 | // Date : 15:18 2024/1/15 | ||||||
| func WithModel(model any) SetOption { | func WithModel(model any) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		o.Model = model | 		o.Model = model | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -59,8 +35,8 @@ func WithModel(model any) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 20:56 2023/3/22 | // Date : 20:56 2023/3/22 | ||||||
| func WithTable(tableName string) SetOption { | func WithTable(tableName string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		o.Table = tableName | 		o.Table = tableName | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -70,8 +46,8 @@ func WithTable(tableName string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 12:17 2022/5/15 | // Date : 12:17 2022/5/15 | ||||||
| func WithWhere[T op_type.BaseType](where map[string]T) SetOption { | func WithWhere[T op_type.BaseType](where map[string]T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Where { | 		if nil == o.Where { | ||||||
| 			o.Where = make(map[string]any) | 			o.Where = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -86,8 +62,8 @@ func WithWhere[T op_type.BaseType](where map[string]T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 12:00 2022/5/15 | // Date : 12:00 2022/5/15 | ||||||
| func WithLimit[T op_type.Int](limit T, offset T) SetOption { | func WithLimit[T op_type.Int](limit T, offset T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		o.Limit = int(limit) | 		o.Limit = int(limit) | ||||||
| 		o.Offset = int(offset) | 		o.Offset = int(offset) | ||||||
| 	} | 	} | ||||||
| @ -98,8 +74,8 @@ func WithLimit[T op_type.Int](limit T, offset T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 21:42 2023/10/14 | // Date : 21:42 2023/10/14 | ||||||
| func WithLimitByPageAndSize[T op_type.Int](page T, size T) SetOption { | func WithLimitByPageAndSize[T op_type.Int](page T, size T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if size > 0 { | 		if size > 0 { | ||||||
| 			o.Limit = int(size) | 			o.Limit = int(size) | ||||||
| 		} | 		} | ||||||
| @ -115,8 +91,8 @@ func WithLimitByPageAndSize[T op_type.Int](page T, size T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 12:23 2022/5/15 | // Date : 12:23 2022/5/15 | ||||||
| func WithIn[T op_type.Array](field string, value T) SetOption { | func WithIn[T op_type.Array](field string, value T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == value { | 		if nil == value { | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| @ -135,8 +111,8 @@ func WithIn[T op_type.Array](field string, value T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 12:24 2022/5/15 | // Date : 12:24 2022/5/15 | ||||||
| func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption { | func WithBatchIn[T op_type.Array](batchIn map[string]T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.In { | 		if nil == o.In { | ||||||
| 			o.In = make(map[string]any) | 			o.In = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -151,8 +127,8 @@ func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 16:18 2022/5/15 | // Date : 16:18 2022/5/15 | ||||||
| func WithNotIn[T op_type.Array](field string, value T) SetOption { | func WithNotIn[T op_type.Array](field string, value T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotIn { | 		if nil == o.NotIn { | ||||||
| 			o.NotIn = make(map[string]any) | 			o.NotIn = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -168,8 +144,8 @@ func WithNotIn[T op_type.Array](field string, value T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 16:23 2022/5/15 | // Date : 16:23 2022/5/15 | ||||||
| func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption { | func WithBatchNotIn[T op_type.Array](data map[string]T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotIn { | 		if nil == o.NotIn { | ||||||
| 			o.NotIn = make(map[string]any) | 			o.NotIn = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -187,8 +163,8 @@ func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:01 2022/5/15 | // Date : 17:01 2022/5/15 | ||||||
| func WithStart(field string, value any) SetOption { | func WithStart(field string, value any) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Start { | 		if nil == o.Start { | ||||||
| 			o.Start = make(map[string]any) | 			o.Start = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -201,8 +177,8 @@ func WithStart(field string, value any) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:03 2022/5/15 | // Date : 17:03 2022/5/15 | ||||||
| func WithBatchStart(data map[string]any) SetOption { | func WithBatchStart(data map[string]any) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Start { | 		if nil == o.Start { | ||||||
| 			o.Start = make(map[string]any) | 			o.Start = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -217,8 +193,8 @@ func WithBatchStart(data map[string]any) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:07 2022/5/15 | // Date : 17:07 2022/5/15 | ||||||
| func WithEnd(field string, value any) SetOption { | func WithEnd(field string, value any) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.End { | 		if nil == o.End { | ||||||
| 			o.End = make(map[string]any) | 			o.End = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -231,8 +207,8 @@ func WithEnd(field string, value any) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:10 2022/5/15 | // Date : 17:10 2022/5/15 | ||||||
| func WithBatchEnd(data map[string]any) SetOption { | func WithBatchEnd(data map[string]any) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.End { | 		if nil == o.End { | ||||||
| 			o.End = make(map[string]any) | 			o.End = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -247,8 +223,8 @@ func WithBatchEnd(data map[string]any) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:16 2022/5/15 | // Date : 17:16 2022/5/15 | ||||||
| func WithLike(field string, value string) SetOption { | func WithLike(field string, value string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Like { | 		if nil == o.Like { | ||||||
| 			o.Like = make(map[string]string) | 			o.Like = make(map[string]string) | ||||||
| 		} | 		} | ||||||
| @ -264,8 +240,8 @@ func WithLike(field string, value string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:19 2022/5/15 | // Date : 17:19 2022/5/15 | ||||||
| func WithBatchLike(data map[string]string) SetOption { | func WithBatchLike(data map[string]string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Like { | 		if nil == o.Like { | ||||||
| 			o.Like = make(map[string]string) | 			o.Like = make(map[string]string) | ||||||
| 		} | 		} | ||||||
| @ -283,8 +259,8 @@ func WithBatchLike(data map[string]string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:50 2022/5/15 | // Date : 17:50 2022/5/15 | ||||||
| func WithNotLike(field string, value string) SetOption { | func WithNotLike(field string, value string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotLike { | 		if nil == o.NotLike { | ||||||
| 			o.NotLike = make(map[string]string) | 			o.NotLike = make(map[string]string) | ||||||
| 		} | 		} | ||||||
| @ -300,8 +276,8 @@ func WithNotLike(field string, value string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:52 2022/5/15 | // Date : 17:52 2022/5/15 | ||||||
| func WithBatchNotLike(data map[string]string) SetOption { | func WithBatchNotLike(data map[string]string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotLike { | 		if nil == o.NotLike { | ||||||
| 			o.NotLike = make(map[string]string) | 			o.NotLike = make(map[string]string) | ||||||
| 		} | 		} | ||||||
| @ -319,8 +295,8 @@ func WithBatchNotLike(data map[string]string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:31 2022/5/15 | // Date : 17:31 2022/5/15 | ||||||
| func WithNotEqual[T op_type.BaseType](field string, value T) SetOption { | func WithNotEqual[T op_type.BaseType](field string, value T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotEqual { | 		if nil == o.NotEqual { | ||||||
| 			o.NotEqual = make(map[string]any) | 			o.NotEqual = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -333,8 +309,8 @@ func WithNotEqual[T op_type.BaseType](field string, value T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:33 2022/5/15 | // Date : 17:33 2022/5/15 | ||||||
| func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption { | func WithBatchNotEqual[T op_type.BaseType](data map[string]T) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.NotEqual { | 		if nil == o.NotEqual { | ||||||
| 			o.NotEqual = make(map[string]any) | 			o.NotEqual = make(map[string]any) | ||||||
| 		} | 		} | ||||||
| @ -349,10 +325,10 @@ func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 20:03 2022/7/23 | // Date : 20:03 2022/7/23 | ||||||
| func WithOR(orConditionList ...SetOption) SetOption { | func WithOR(orConditionList ...define.SetOption) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.OR { | 		if nil == o.OR { | ||||||
| 			o.OR = make([][]SetOption, 0) | 			o.OR = make([][]define.SetOption, 0) | ||||||
| 		} | 		} | ||||||
| 		o.OR = append(o.OR, orConditionList) | 		o.OR = append(o.OR, orConditionList) | ||||||
| 	} | 	} | ||||||
| @ -363,8 +339,8 @@ func WithOR(orConditionList ...SetOption) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 20:15 2022/10/20 | // Date : 20:15 2022/10/20 | ||||||
| func WithOrder(orderRuleList ...string) SetOption { | func WithOrder(orderRuleList ...string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		o.Order = orderRuleList | 		o.Order = orderRuleList | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| @ -374,8 +350,8 @@ func WithOrder(orderRuleList ...string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 15:09 2023/4/3 | // Date : 15:09 2023/4/3 | ||||||
| func WithOrderDesc(field string) SetOption { | func WithOrderDesc(field string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Order { | 		if nil == o.Order { | ||||||
| 			o.Order = make([]string, 0) | 			o.Order = make([]string, 0) | ||||||
| 		} | 		} | ||||||
| @ -388,8 +364,8 @@ func WithOrderDesc(field string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 15:09 2023/4/3 | // Date : 15:09 2023/4/3 | ||||||
| func WithOrderAsc(field string) SetOption { | func WithOrderAsc(field string) define.SetOption { | ||||||
| 	return func(o *Option) { | 	return func(o *define.Option) { | ||||||
| 		if nil == o.Order { | 		if nil == o.Order { | ||||||
| 			o.Order = make([]string, 0) | 			o.Order = make([]string, 0) | ||||||
| 		} | 		} | ||||||
| @ -402,8 +378,8 @@ func WithOrderAsc(field string) SetOption { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:46 2024/8/9 | // Date : 17:46 2024/8/9 | ||||||
| func newOption(setOptionList ...SetOption) *Option { | func newOption(setOptionList ...define.SetOption) *define.Option { | ||||||
| 	o := &Option{} | 	o := &define.Option{} | ||||||
| 	for _, item := range setOptionList { | 	for _, item := range setOptionList { | ||||||
| 		item(o) | 		item(o) | ||||||
| 	} | 	} | ||||||
| @ -415,7 +391,7 @@ func newOption(setOptionList ...SetOption) *Option { | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 17:46 2024/8/9 | // Date : 17:46 2024/8/9 | ||||||
| func optionToSql(o *Option) (sqlBuildResult string, bindValue []any) { | func optionToSql(o *define.Option) (sqlBuildResult string, bindValue []any) { | ||||||
| 	bindValue = make([]any, 0) | 	bindValue = make([]any, 0) | ||||||
| 	sqlBuildResultBlockList := make([]string, 0) | 	sqlBuildResultBlockList := make([]string, 0) | ||||||
| 	// 设置where条件 | 	// 设置where条件 | ||||||
| @ -484,6 +460,17 @@ func optionToSql(o *Option) (sqlBuildResult string, bindValue []any) { | |||||||
| 		bindValue = append(bindValue, fieldValue) | 		bindValue = append(bindValue, fieldValue) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// between | ||||||
|  | 	for field, betweenVal := range o.Between { | ||||||
|  | 		sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+field+"` BETWEEN ? AND ?") | ||||||
|  | 		bindValue = append(bindValue, betweenVal[0], betweenVal[1]) | ||||||
|  | 	} | ||||||
|  | 	// not between | ||||||
|  | 	for field, notBetweenVal := range o.NotBetween { | ||||||
|  | 		sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+field+"` NOT BETWEEN ? AND ?") | ||||||
|  | 		bindValue = append(bindValue, notBetweenVal[0], notBetweenVal[1]) | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	if len(bindValue) > 0 { | 	if len(bindValue) > 0 { | ||||||
| 		sqlBuildResult = strings.Join(sqlBuildResultBlockList, " AND ") | 		sqlBuildResult = strings.Join(sqlBuildResultBlockList, " AND ") | ||||||
| 	} | 	} | ||||||
| @ -508,3 +495,66 @@ func parseInSql(fieldValue any) (string, []any) { | |||||||
| 	} | 	} | ||||||
| 	return strings.Join(placeholderList, ","), dataList | 	return strings.Join(placeholderList, ","), dataList | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // WithBetween between 语句 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 15:52 2024/8/23 | ||||||
|  | func WithBetween(field string, left any, right any) define.SetOption { | ||||||
|  | 	return func(o *define.Option) { | ||||||
|  | 		if nil == o.Between { | ||||||
|  | 			o.Between = map[string][2]any{} | ||||||
|  | 		} | ||||||
|  | 		o.Between[field] = [2]any{left, right} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithNotBetween not between 语句 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 15:52 2024/8/23 | ||||||
|  | func WithNotBetween(field string, left any, right any) define.SetOption { | ||||||
|  | 	return func(o *define.Option) { | ||||||
|  | 		if nil == o.NotBetween { | ||||||
|  | 			o.NotBetween = map[string][2]any{} | ||||||
|  | 		} | ||||||
|  | 		o.NotBetween[field] = [2]any{left, right} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // WithAnyCondition 设置任意查询条件, 仅 where 子句 in / not in / like / not like / == / != | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 14:48 2024/8/23 | ||||||
|  | func WithAnyCondition(column string, operate string, value any) (define.SetOption, error) { | ||||||
|  | 	if nil == value { | ||||||
|  | 		return nil, errors.New("value is nil") | ||||||
|  | 	} | ||||||
|  | 	switch operate { | ||||||
|  | 	case consts.WhereOperateEqual: | ||||||
|  | 		return WithWhere(map[string]any{column: value}), nil | ||||||
|  | 	case consts.WhereOperateNotEqual: | ||||||
|  | 		return WithNotEqual(column, value), nil | ||||||
|  | 	case consts.WhereOperateIn: | ||||||
|  | 		var target []any | ||||||
|  | 		if err := serialize.JSON.Transition(value, &target); nil != err { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return WithIn[[]any](column, target), nil | ||||||
|  | 	case consts.WhereOperateNotIn: | ||||||
|  | 		var target []any | ||||||
|  | 		if err := serialize.JSON.Transition(value, &target); nil != err { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 		return WithNotIn[[]any](column, target), nil | ||||||
|  | 	case consts.WhereOperateLike: | ||||||
|  | 		return WithLike(column, fmt.Sprintf("%v", value)), nil | ||||||
|  | 	case consts.WhereOperateNotLike: | ||||||
|  | 		return WithNotLike(column, fmt.Sprintf("%v", value)), nil | ||||||
|  | 	default: | ||||||
|  | 		return nil, errors.New(operate + " : operate is not support") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | |||||||
| @ -9,14 +9,12 @@ package database | |||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| func Test_optionToSql(t *testing.T) { | func Test_optionToSql(t *testing.T) { | ||||||
| 	type args struct { | 	o := &define.Option{ | ||||||
| 		o *Option |  | ||||||
| 	} |  | ||||||
| 	o := &Option{ |  | ||||||
| 		In:    nil, | 		In:    nil, | ||||||
| 		NotIn: nil, | 		NotIn: nil, | ||||||
| 		Where: map[string]any{ | 		Where: map[string]any{ | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ import ( | |||||||
|  |  | ||||||
| 	"gorm.io/driver/mysql" | 	"gorm.io/driver/mysql" | ||||||
|  |  | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
| 	"gorm.io/gorm" | 	"gorm.io/gorm" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @ -99,10 +100,10 @@ func (sd *SystemDao) GetCreateTableSQL(dbInstance *gorm.DB, table string) (strin | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 22:37 2023/8/16 | // Date : 22:37 2023/8/16 | ||||||
| func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableName string) ([]*DescTableItem, error) { | func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableName string) ([]*define.DescTableItem, error) { | ||||||
| 	var ( | 	var ( | ||||||
| 		err    error | 		err    error | ||||||
| 		result []*DescTableItem | 		result []*define.DescTableItem | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
| 	if err = dbInstance.Raw("DESC `" + tableName + "`").Scan(&result).Error; nil != err { | 	if err = dbInstance.Raw("DESC `" + tableName + "`").Scan(&result).Error; nil != err { | ||||||
| @ -125,9 +126,9 @@ func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableNam | |||||||
| // Author : go_developer@163.com<白茶清欢> | // Author : go_developer@163.com<白茶清欢> | ||||||
| // | // | ||||||
| // Date : 23:10 2023/8/16 | // Date : 23:10 2023/8/16 | ||||||
| func (sd *SystemDao) GetTableInfo(dbInstance *gorm.DB, database string, tableName string) ([]*ColumnInfo, error) { | func (sd *SystemDao) GetTableInfo(dbInstance *gorm.DB, database string, tableName string) ([]*define.ColumnInfo, error) { | ||||||
| 	var ( | 	var ( | ||||||
| 		list []*ColumnInfo | 		list []*define.ColumnInfo | ||||||
| 		err  error | 		err  error | ||||||
| 	) | 	) | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										205
									
								
								wrapper_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								wrapper_client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,205 @@ | |||||||
|  | // Package database ... | ||||||
|  | // | ||||||
|  | // Description : mysql客户端 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2021-03-01 9:20 下午 | ||||||
|  | package database | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/consts" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/abstract" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/serialize" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"path/filepath" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  |  | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	// WrapperClient 包装后的数据库客户端 | ||||||
|  | 	WrapperClient abstract.IWrapperClient | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | func init() { | ||||||
|  | 	WrapperClient = NewWrapperClient() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func NewWrapperClient() *wrapperClient { | ||||||
|  | 	return &wrapperClient{ | ||||||
|  | 		lock:        &sync.RWMutex{}, | ||||||
|  | 		clientTable: make(map[string]abstract.IWrapperDatabaseClient), | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type wrapperClient struct { | ||||||
|  | 	lock        *sync.RWMutex | ||||||
|  | 	clientTable map[string]abstract.IWrapperDatabaseClient | ||||||
|  | 	logger      *zap.Logger | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddWithConfigFile 使用文件生成新的客户端,文件名去掉后缀作为flag | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 19:19 2022/6/5 | ||||||
|  | func (c *wrapperClient) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error { | ||||||
|  | 	var ( | ||||||
|  | 		err error | ||||||
|  | 		cfg *define.CfgFile | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	if cfg, err = c.getCfg(cfgFilePath); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if nil == cfg { | ||||||
|  | 		// 不支持的配置文件格式 | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return c.AddWithConfig(cfg.Flag, logInstance, cfg.Config, extraFieldList) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // AddWithConfig ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 20:41 2023/4/18 | ||||||
|  | func (c *wrapperClient) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error { | ||||||
|  | 	dbClient := &DBClient{ | ||||||
|  | 		DbFlag:         flag, | ||||||
|  | 		LoggerInstance: logInstance, | ||||||
|  | 		ExtraFieldList: extraFieldList, | ||||||
|  | 		Cfg:            define.Driver{}, | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if err := dbClient.Init(databaseConfig, nil); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	c.lock.Lock() | ||||||
|  | 	c.clientTable[dbClient.DbFlag] = dbClient | ||||||
|  | 	c.lock.Unlock() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // BatchAddWithConfigDir 自动读取目录下配置文件, 生成客户端 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 19:19 2022/6/5 | ||||||
|  | func (c *wrapperClient) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error { | ||||||
|  | 	filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*")) | ||||||
|  | 	for i := range filepathNames { | ||||||
|  | 		if err := c.AddWithConfigFile(filepathNames[i], logInstance, extraFieldList); nil != err { | ||||||
|  | 			return err | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getCfg 读取配置 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 18:05 2022/6/11 | ||||||
|  | func (c *wrapperClient) getCfg(cfgPath string) (*define.CfgFile, error) { | ||||||
|  | 	fileArr := strings.Split(cfgPath, ".") | ||||||
|  | 	if len(fileArr) < 2 { | ||||||
|  | 		// 获取不到类型 | ||||||
|  | 		return nil, errors.New("文件格式必须是JSON或者YAML") | ||||||
|  | 	} | ||||||
|  | 	fileType := strings.ToLower(fileArr[len(fileArr)-1]) | ||||||
|  | 	fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator)) | ||||||
|  | 	result := &define.CfgFile{ | ||||||
|  | 		Path:   cfgPath, | ||||||
|  | 		Type:   "", | ||||||
|  | 		Flag:   fileFlagArr[len(fileFlagArr)-1], | ||||||
|  | 		Config: &define.Database{}, | ||||||
|  | 	} | ||||||
|  | 	var ( | ||||||
|  | 		err     error | ||||||
|  | 		cfgInfo define.Database | ||||||
|  | 	) | ||||||
|  | 	switch fileType { | ||||||
|  | 	case consts.FileTypeYaml: | ||||||
|  | 		fallthrough | ||||||
|  | 	case consts.FileTypeYml: | ||||||
|  | 		result.Type = consts.FileTypeYaml | ||||||
|  | 		if err = serialize.File.ReadYmlContent(cfgPath, &result.Config); nil != err { | ||||||
|  | 			return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error()) | ||||||
|  | 		} | ||||||
|  | 	case consts.FileTypeJson: | ||||||
|  | 		result.Type = consts.FileTypeJson | ||||||
|  | 		if err = serialize.File.ReadJSONContent(cfgPath, &cfgInfo); nil != err { | ||||||
|  | 			return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error()) | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		// 不是JSON , 也不是YML, 跳过 | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 	if len(result.Config.Master.Timezone) == 0 { | ||||||
|  | 		// 默认使用本地时区 | ||||||
|  | 		result.Config.Master.Timezone = "Local" | ||||||
|  | 	} else { | ||||||
|  | 		result.Config.Slave.Timezone = result.Config.Master.Timezone | ||||||
|  | 	} | ||||||
|  | 	return result, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetDBClient 获取db client | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 19:32 2022/6/5 | ||||||
|  | func (c *wrapperClient) GetDBClient(dbFlag string) (abstract.IWrapperDatabaseClient, error) { | ||||||
|  | 	c.lock.RLock() | ||||||
|  | 	defer c.lock.RUnlock() | ||||||
|  | 	var ( | ||||||
|  | 		exist    bool | ||||||
|  | 		dbClient abstract.IWrapperDatabaseClient | ||||||
|  | 	) | ||||||
|  | 	if dbClient, exist = c.clientTable[dbFlag]; !exist { | ||||||
|  | 		return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag) | ||||||
|  | 	} | ||||||
|  | 	return dbClient, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMasterClient 获取主库客户端 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 19:36 2022/6/5 | ||||||
|  | func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { | ||||||
|  | 	var ( | ||||||
|  | 		err      error | ||||||
|  | 		dbClient abstract.IWrapperDatabaseClient | ||||||
|  | 	) | ||||||
|  | 	if dbClient, err = c.GetDBClient(dbFlag); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return dbClient.GetMaster(ctx), nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetSlaveClient 获取从库客户端 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 19:37 2022/6/5 | ||||||
|  | func (c *wrapperClient) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { | ||||||
|  | 	var ( | ||||||
|  | 		err      error | ||||||
|  | 		dbClient abstract.IWrapperDatabaseClient | ||||||
|  | 	) | ||||||
|  | 	if dbClient, err = c.GetDBClient(dbFlag); nil != err { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return dbClient.GetSlave(ctx), nil | ||||||
|  | } | ||||||
							
								
								
									
										267
									
								
								wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,267 @@ | |||||||
|  | // Package database ... | ||||||
|  | // | ||||||
|  | // Description : define ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 2024-08-20 17:36 | ||||||
|  | package database | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/consts" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||||
|  | 	"git.zhangdeman.cn/zhangdeman/logger/wrapper" | ||||||
|  | 	"go.uber.org/zap" | ||||||
|  | 	"gorm.io/driver/mysql" | ||||||
|  | 	"gorm.io/driver/sqlite" | ||||||
|  | 	"gorm.io/gorm" | ||||||
|  | 	gormLogger "gorm.io/gorm/logger" | ||||||
|  | 	"strings" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // DBClient 包装日志实例 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 3:09 PM 2021/12/24 | ||||||
|  | type DBClient struct { | ||||||
|  | 	DbFlag                    string                            // 数据库标识 | ||||||
|  | 	LoggerInstance            *zap.Logger                       // 日志实例 | ||||||
|  | 	master                    *gorm.DB                          // 主库 | ||||||
|  | 	slave                     *gorm.DB                          // 从库 | ||||||
|  | 	ExtraFieldList            []string                          // 提取的字段 | ||||||
|  | 	Cfg                       define.Driver                     // 数据库配置 | ||||||
|  | 	cacheTableStructureConfig *define.CacheTableStructureConfig // 缓存配置 | ||||||
|  | 	lock                      *sync.RWMutex                     // 操作锁 | ||||||
|  | 	tableStructureCache       map[string][]*define.ColumnConfig // 表结构缓存 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // Init 初始化客户端 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 17:44 2024/8/20 | ||||||
|  | func (dc *DBClient) Init(databaseConfig *define.Database, cacheTableStructureConfig *define.CacheTableStructureConfig) error { | ||||||
|  | 	dc.lock = &sync.RWMutex{} | ||||||
|  | 	dc.cacheTableStructureConfig = cacheTableStructureConfig | ||||||
|  | 	var err error | ||||||
|  | 	if dc.master, err = dc.GetDatabaseClient(databaseConfig.Master, dc.LoggerInstance); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if dc.slave, err = dc.GetDatabaseClient(databaseConfig.Slave, dc.LoggerInstance); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	if err = dc.syncDbTableStructure(false); nil != err { // 同步缓存表结构 | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	// 启动异步任务 | ||||||
|  | 	go func() { | ||||||
|  | 		if !dc.CacheDataTableStructureConfig().Enable { | ||||||
|  | 			// 未启用 | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		for { | ||||||
|  | 			select { | ||||||
|  | 			case <-time.After(time.Second * time.Duration(dc.CacheDataTableStructureConfig().SyncTimeInterval)): | ||||||
|  | 				_ = dc.syncDbTableStructure(true) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	}() | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetMaster 获取主库连接 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 3:28 PM 2021/12/24 | ||||||
|  | func (dc *DBClient) GetMaster(ctx context.Context) *gorm.DB { | ||||||
|  | 	session := dc.master.Session(&gorm.Session{}) | ||||||
|  | 	session.Logger = dc.getLogger(ctx, session, "master") | ||||||
|  | 	return session | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetSlave 获取从库链接 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 3:29 PM 2021/12/24 | ||||||
|  | func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB { | ||||||
|  | 	session := dc.slave.Session(&gorm.Session{}) | ||||||
|  | 	session.Logger = dc.getLogger(ctx, session, "slave") | ||||||
|  | 	return session | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // getLogger 获取日志实例 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 3:45 PM 2021/12/24 | ||||||
|  | func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node string) gormLogger.Interface { | ||||||
|  | 	return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.LoggerInstance, dc.DbFlag+"|"+node, dc.ExtraFieldList) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetDatabaseClient 获取数据库连接 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 18:41 2022/6/11 | ||||||
|  | func (dc *DBClient) GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error) { | ||||||
|  | 	var ( | ||||||
|  | 		instance *gorm.DB | ||||||
|  | 		err      error | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	if nil == logInstance { | ||||||
|  | 		logInstance = dc.LoggerInstance | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if conf.DBType == consts.DatabaseDriverMysql { | ||||||
|  | 		if instance, err = gorm.Open(mysql.Open(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} else if conf.DBType == consts.DatabaseDriverSqlite3 { | ||||||
|  | 		if instance, err = gorm.Open(sqlite.Open(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		return nil, fmt.Errorf("%v : db driver is not support", conf.DBType) | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil) | ||||||
|  |  | ||||||
|  | 	return instance, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // buildConnectionDSN 构建连接信息 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 18:42 2022/6/11 | ||||||
|  | func (dc *DBClient) buildConnectionDSN(conf *define.Driver) string { | ||||||
|  | 	if conf.DBType == consts.DatabaseDriverSqlite3 { | ||||||
|  | 		// 兼容sqlite3 | ||||||
|  | 		return conf.Host | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf( | ||||||
|  | 		"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s", | ||||||
|  | 		conf.Username, | ||||||
|  | 		conf.Password, | ||||||
|  | 		conf.Host, | ||||||
|  | 		conf.Port, | ||||||
|  | 		conf.Database, | ||||||
|  | 		conf.Charset, | ||||||
|  | 		conf.Timezone, | ||||||
|  | 	) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // CacheDataTableStructureConfig ... | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 15:03 2024/8/21 | ||||||
|  | func (dc *DBClient) CacheDataTableStructureConfig() *define.CacheTableStructureConfig { | ||||||
|  | 	if nil == dc.cacheTableStructureConfig { | ||||||
|  | 		return &define.CacheTableStructureConfig{Enable: false, SyncTimeInterval: 3600} | ||||||
|  | 	} | ||||||
|  | 	if dc.cacheTableStructureConfig.SyncTimeInterval <= 0 { | ||||||
|  | 		dc.cacheTableStructureConfig.SyncTimeInterval = 3600 | ||||||
|  | 	} | ||||||
|  | 	return dc.cacheTableStructureConfig | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // GetTableFieldList 获取表结构配置 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 15:07 2024/8/21 | ||||||
|  | func (dc *DBClient) GetTableFieldList(tableName string) ([]*define.ColumnConfig, error) { | ||||||
|  | 	if !dc.CacheDataTableStructureConfig().Enable { | ||||||
|  | 		// 未启用缓存, 返回空list | ||||||
|  | 		return make([]*define.ColumnConfig, 0), nil | ||||||
|  | 	} | ||||||
|  | 	dc.lock.RLock() | ||||||
|  | 	defer dc.lock.RUnlock() | ||||||
|  | 	if _, exist := dc.tableStructureCache[tableName]; !exist { | ||||||
|  | 		return nil, errors.New(tableName + " : cache result not found") | ||||||
|  | 	} | ||||||
|  | 	return dc.tableStructureCache[tableName], nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // syncDbTableStructure 缓存表结构 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 15:17 2024/8/21 | ||||||
|  | func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error { | ||||||
|  | 	if !dc.CacheDataTableStructureConfig().Enable { | ||||||
|  | 		// 自动同步不可用 | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	var ( | ||||||
|  | 		err       error | ||||||
|  | 		tableList []string | ||||||
|  | 	) | ||||||
|  |  | ||||||
|  | 	c := dc.GetMaster(context.Background()) | ||||||
|  | 	systemDao := &SystemDao{} | ||||||
|  | 	if tableList, err = systemDao.GetTableList(c); nil != err { | ||||||
|  | 		return err | ||||||
|  | 	} | ||||||
|  | 	tableStructCache := make(map[string][]*define.ColumnConfig) | ||||||
|  | 	for _, itemTableName := range tableList { | ||||||
|  | 		fieldList, loadTableErr := systemDao.GetTableInfo(c, dc.Cfg.Database, itemTableName) | ||||||
|  | 		if nil != loadTableErr { | ||||||
|  | 			if ignoreErr { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			return loadTableErr | ||||||
|  | 		} | ||||||
|  | 		tableStructCache[itemTableName] = make([]*define.ColumnConfig, 0) | ||||||
|  | 		for _, itemColumn := range fieldList { | ||||||
|  | 			fieldType := "string" | ||||||
|  | 			if strings.Contains(itemColumn.ColumnType, "int") { | ||||||
|  | 				if strings.Contains(itemColumn.ColumnType, "unsigned") { | ||||||
|  | 					fieldType = "uint" | ||||||
|  | 				} else { | ||||||
|  | 					fieldType = "int" | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 			tableStructCache[itemTableName] = append(tableStructCache[itemTableName], &define.ColumnConfig{ | ||||||
|  | 				Column: itemColumn.ColumnName, | ||||||
|  | 				Alias:  itemColumn.ColumnName, | ||||||
|  | 				Type:   fieldType, | ||||||
|  | 			}) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	// 更新缓存结果 | ||||||
|  | 	dc.lock.Lock() | ||||||
|  | 	defer dc.lock.Unlock() | ||||||
|  | 	dc.tableStructureCache = tableStructCache | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // SetTableStructure 设置表结构, 一旦调用人工设置, 则将终止自动同步 | ||||||
|  | // | ||||||
|  | // Author : go_developer@163.com<白茶清欢> | ||||||
|  | // | ||||||
|  | // Date : 17:06 2024/8/23 | ||||||
|  | func (dc *DBClient) SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig) { | ||||||
|  | 	if nil != dc.cacheTableStructureConfig { | ||||||
|  | 		// 关闭自动同步 | ||||||
|  | 		dc.cacheTableStructureConfig.Enable = false | ||||||
|  | 	} | ||||||
|  | 	dc.lock.Lock() | ||||||
|  | 	if nil == dc.tableStructureCache { | ||||||
|  | 		dc.tableStructureCache = make(map[string][]*define.ColumnConfig) | ||||||
|  | 	} | ||||||
|  | 	for table, columnConfig := range tableConfigTable { | ||||||
|  | 		dc.tableStructureCache[table] = columnConfig | ||||||
|  | 	} | ||||||
|  | 	dc.lock.Unlock() | ||||||
|  | } | ||||||
		Reference in New Issue
	
	Block a user