Compare commits
	
		
			41 Commits
		
	
	
		
			b10a55f62d
			...
			feature/sh
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 4d0d526c6d | |||
| 8c75d91915 | |||
| 7d3c63175d | |||
| 1b049b5bdc | |||
| 632c9ee5d1 | |||
| a0dd3ed742 | |||
| 66bcb90ff9 | |||
| e960797bb2 | |||
| 2f573767e1 | |||
| 2b23dd7714 | |||
| 0005d1e8f7 | |||
| a4cbda626d | |||
| 27a522fbeb | |||
| 4e1b8ea8fb | |||
| 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 | ||||
|  | ||||
| import ( | ||||
| 	"git.zhangdeman.cn/zhangdeman/database" | ||||
| 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| @ -19,21 +19,23 @@ import ( | ||||
| // Date : 15:06 2023/10/14 | ||||
| type IDatabase interface { | ||||
| 	// Create 创建数据 | ||||
| 	Create(dbInstance *gorm.DB, data any, optionList ...database.SetOption) error | ||||
| 	Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error | ||||
| 	// 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(dbInstance *gorm.DB, updateData any, optionFuncList ...database.SetOption) (int64, error) | ||||
| 	UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) | ||||
| 	// List 查询数据列表 | ||||
| 	List(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error | ||||
| 	List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error | ||||
| 	// ListAndTotal 查询列表并返回满足条件数据总数 | ||||
| 	ListAndTotal(dbInstance *gorm.DB, listRes any, totalRes *int64, disableTotal bool, optionFuncList ...define.SetOption) error | ||||
| 	// 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(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error | ||||
| 	Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error | ||||
| 	// IsNotFound 错误是否为数据不存在 | ||||
| 	IsNotFound(err error) bool | ||||
| 	// Count 查询数据数量 | ||||
| 	Count(dbInstance *gorm.DB, optionFuncList ...database.SetOption) (int64, error) | ||||
| 	Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error) | ||||
| 	// Tx 执行事务 | ||||
| 	Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error | ||||
| 	// Begin 开启事务 | ||||
|  | ||||
							
								
								
									
										16
									
								
								abstract/shard_strategy.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								abstract/shard_strategy.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| // Package abstract ... | ||||
| // | ||||
| // Description : abstract ... | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 2025-06-21 15:20 | ||||
| package abstract | ||||
|  | ||||
| // IShardStrategy 自定义数据分片策略 | ||||
| type IShardStrategy interface { | ||||
| 	// ShardField 按照哪些字段分表 | ||||
| 	ShardField() []string | ||||
| 	// Calculate 计算分片值 | ||||
| 	Calculate(inputData map[string]any) (int, error) | ||||
| } | ||||
							
								
								
									
										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)) | ||||
| } | ||||
							
								
								
									
										144
									
								
								base.go
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								base.go
									
									
									
									
									
								
							| @ -7,8 +7,9 @@ package database | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||
| 	"gorm.io/gorm" | ||||
| 	"reflect" | ||||
| ) | ||||
|  | ||||
| // BaseDao 基础dao层 | ||||
| @ -25,8 +26,8 @@ type BaseDao struct { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 8:06 下午 2021/8/8 | ||||
| func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption) error { | ||||
| 	o := &Option{} | ||||
| func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error { | ||||
| 	o := &define.Option{} | ||||
| 	for _, itemFunc := range optionList { | ||||
| 		itemFunc(o) | ||||
| 	} | ||||
| @ -42,7 +43,7 @@ func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption) | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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...) | ||||
| 	r := dbInstance.Updates(updateData) | ||||
| 	return r.RowsAffected, r.Error | ||||
| @ -53,7 +54,7 @@ func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ... | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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)) | ||||
| 	return b.Update(dbInstance, updateData, optionFuncList...) | ||||
| } | ||||
| @ -63,17 +64,51 @@ func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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...) | ||||
| 	return dbInstance.Find(result).Error | ||||
| } | ||||
|  | ||||
| // ListAndTotal 同时查询数据列表和数据总数 | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 16:04 2025/2/26 | ||||
| func (b *BaseDao) ListAndTotal(dbInstance *gorm.DB, listRes any, totalRes *int64, disableTotal bool, optionFuncList ...define.SetOption) error { | ||||
| 	var ( | ||||
| 		err error | ||||
| 	) | ||||
| 	if err = b.receiverTypeValid(listRes); nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 	if err = b.receiverTypeValid(totalRes); nil != err { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||
| 	if err = dbInstance.Find(listRes).Error; nil != err { | ||||
| 		// 列表查询失败 | ||||
| 		return err | ||||
| 	} | ||||
| 	if disableTotal { | ||||
| 		// 禁用查询总数 | ||||
| 		*totalRes = int64(reflect.ValueOf(listRes).Elem().Len()) | ||||
| 		return nil | ||||
| 	} | ||||
| 	optionFuncList = append(optionFuncList, WithClearLimit()) | ||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||
| 	if err = dbInstance.Count(totalRes).Error; nil != err { | ||||
| 		return err | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| // Delete 删除数据, 硬删除, 对应 delete语句 | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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 = b.setTxCondition(dbInstance, optionFuncList...).Delete(dataModel) | ||||
| 	return dbInstance.RowsAffected, dbInstance.Error | ||||
| @ -84,7 +119,7 @@ func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...S | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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...) | ||||
| 	return dbInstance.First(result).Error | ||||
| } | ||||
| @ -118,7 +153,8 @@ func (b *BaseDao) IsNotFound(err error) bool { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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) { | ||||
| 	optionFuncList = append(optionFuncList, WithClearLimit()) | ||||
| 	dbInstance = b.setTxCondition(dbInstance, optionFuncList...) | ||||
| 	var cnt int64 | ||||
| 	return cnt, dbInstance.Count(&cnt).Error | ||||
| @ -173,14 +209,18 @@ func (b *BaseDao) Rollback(db *gorm.DB) error { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:38 2022/5/15 | ||||
| func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm.DB { | ||||
|  | ||||
| func (b *BaseDao) setTxCondition(inputTx *gorm.DB, optionFuncList ...define.SetOption) *gorm.DB { | ||||
| 	// 构建查询条件 | ||||
| 	o := &Option{} | ||||
| 	o := &define.Option{} | ||||
| 	for _, fn := range optionFuncList { | ||||
| 		fn(o) | ||||
| 	} | ||||
|  | ||||
| 	tx := inputTx.Session(&gorm.Session{ | ||||
| 		NewDB:       true, | ||||
| 		Initialized: true, | ||||
| 	}) | ||||
|  | ||||
| 	// 指定查询的表 | ||||
| 	if len(o.Table) > 0 { | ||||
| 		tx = tx.Table(o.Table) | ||||
| @ -199,7 +239,76 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm | ||||
| 			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) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(o.Where) > 0 { | ||||
| 		tx = tx.Where(o.Where) | ||||
| 	} | ||||
|  | ||||
| 	// 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]) | ||||
| 	} | ||||
|  | ||||
| 	// 排序 | ||||
| 	if len(o.Order) == 2 { | ||||
| 		tx = tx.Order(o.Order[0] + " " + o.Order[1]) | ||||
| 	} | ||||
|  | ||||
| 	// 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) | ||||
| 	sql, bindVal := optionToSql(o) | ||||
| 	tx.Or(sql, bindVal...) | ||||
| @ -212,6 +321,13 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm | ||||
| 		} | ||||
| 		orSql, orBindVal := optionToSql(orOption) | ||||
| 		tx.Or(orSql, orBindVal...) | ||||
| 	} | ||||
| 	}*/ | ||||
| 	return tx | ||||
| } | ||||
|  | ||||
| func (b *BaseDao) receiverTypeValid(receiver any) error { | ||||
| 	if receiver == nil || reflect.TypeOf(receiver).Kind() != reflect.Ptr { | ||||
| 		return errors.New("receiver is nil or receiver is not a pointer") | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
							
								
								
									
										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,13 @@ | ||||
| // package database... | ||||
| // Package define ... | ||||
| // | ||||
| // Description : 数据定义 | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 2021-03-01 9:27 下午 | ||||
| package database | ||||
| package define | ||||
| 
 | ||||
| import "git.zhangdeman.cn/zhangdeman/consts" | ||||
| 
 | ||||
| // DBConfig 数据库连接的配置 | ||||
| // | ||||
| @ -22,16 +24,16 @@ type DBConfig struct { | ||||
| 	Connection Connection `json:"connection" yaml:"connection"` // 连接数量配置 | ||||
| } | ||||
| 
 | ||||
| // cfgFile 配置文件定义 | ||||
| // CfgFile 配置文件定义 | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 14:47 2022/6/9 | ||||
| type cfgFile struct { | ||||
| 	Flag   string    `json:"flag"`   // 数据库标识 | ||||
| 	Path   string    `json:"path"`   // 配置文件路径 | ||||
| 	Type   string    `json:"type"`   // 配置文件类型 | ||||
| 	Config *Database `json:"config"` // 解析之后的配置文件 | ||||
| type CfgFile struct { | ||||
| 	Flag   string          `json:"flag"`   // 数据库标识 | ||||
| 	Path   string          `json:"path"`   // 配置文件路径 | ||||
| 	Type   consts.FileType `json:"type"`   // 配置文件类型 | ||||
| 	Config *Database       `json:"config"` // 解析之后的配置文件 | ||||
| } | ||||
| 
 | ||||
| // Database 数据库配置 | ||||
							
								
								
									
										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"`   // 字段类型 | ||||
| } | ||||
							
								
								
									
										74
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										74
									
								
								go.mod
									
									
									
									
									
								
							| @ -1,77 +1,79 @@ | ||||
| module git.zhangdeman.cn/zhangdeman/database | ||||
|  | ||||
| go 1.21 | ||||
| go 1.24.1 | ||||
|  | ||||
| toolchain go1.21.3 | ||||
| toolchain go1.24.2 | ||||
|  | ||||
| require ( | ||||
| 	git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 | ||||
| 	git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a | ||||
| 	git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995 | ||||
| 	git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5 | ||||
| 	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/wrapper v0.0.0-20240813083016-da44ae07ab9b | ||||
| 	git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9 | ||||
| 	git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740 | ||||
| 	github.com/pkg/errors v0.9.1 | ||||
| 	github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 | ||||
| 	go.uber.org/zap v1.27.0 | ||||
| 	gorm.io/driver/mysql v1.5.7 | ||||
| 	gorm.io/driver/sqlite v1.5.6 | ||||
| 	gorm.io/gorm v1.25.11 | ||||
| 	gorm.io/driver/mysql v1.6.0 | ||||
| 	gorm.io/driver/sqlite v1.6.0 | ||||
| 	gorm.io/gorm v1.30.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	filippo.io/edwards25519 v1.1.0 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250529071316-7a3da3614e07 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/network v0.0.0-20250529151912-0e6bd9e6691d // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 // indirect | ||||
| 	github.com/BurntSushi/toml v1.4.0 // indirect | ||||
| 	git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e // indirect | ||||
| 	github.com/BurntSushi/toml v1.5.0 // indirect | ||||
| 	github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect | ||||
| 	github.com/bytedance/sonic v1.12.1 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.2.0 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.4 // indirect | ||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||
| 	github.com/bytedance/sonic v1.13.3 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.2.4 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.5 // indirect | ||||
| 	github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.5 // indirect | ||||
| 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||
| 	github.com/gin-gonic/gin v1.10.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.9 // indirect | ||||
| 	github.com/gin-contrib/sse v1.1.0 // indirect | ||||
| 	github.com/gin-gonic/gin v1.10.1 // indirect | ||||
| 	github.com/go-ini/ini v1.67.0 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.22.0 // indirect | ||||
| 	github.com/go-sql-driver/mysql v1.8.1 // indirect | ||||
| 	github.com/goccy/go-json v0.10.3 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.26.0 // indirect | ||||
| 	github.com/go-sql-driver/mysql v1.9.3 // indirect | ||||
| 	github.com/goccy/go-json v0.10.5 // indirect | ||||
| 	github.com/gorilla/websocket v1.5.3 // indirect | ||||
| 	github.com/jinzhu/inflection v1.0.0 // indirect | ||||
| 	github.com/jinzhu/now v1.1.5 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.2.8 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.2.10 // indirect | ||||
| 	github.com/leodido/go-urn v1.4.0 // indirect | ||||
| 	github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect | ||||
| 	github.com/lestrrat-go/strftime v1.0.6 // indirect | ||||
| 	github.com/lestrrat-go/strftime v1.1.0 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/mattn/go-sqlite3 v1.14.22 // indirect | ||||
| 	github.com/mattn/go-sqlite3 v1.14.28 // indirect | ||||
| 	github.com/mcuadros/go-defaults v1.2.0 // indirect | ||||
| 	github.com/mitchellh/go-homedir v1.1.0 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/mozillazg/go-pinyin v0.20.0 // indirect | ||||
| 	github.com/mssola/user_agent v0.6.0 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.4 // indirect | ||||
| 	github.com/sbabiv/xml2map v1.2.1 // indirect | ||||
| 	github.com/spaolacci/murmur3 v1.1.0 // indirect | ||||
| 	github.com/tidwall/gjson v1.17.3 // indirect | ||||
| 	github.com/tidwall/gjson v1.18.0 // indirect | ||||
| 	github.com/tidwall/match v1.1.1 // indirect | ||||
| 	github.com/tidwall/pretty v1.2.1 // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||
| 	github.com/ugorji/go/codec v1.3.0 // indirect | ||||
| 	go.uber.org/multierr v1.11.0 // indirect | ||||
| 	golang.org/x/arch v0.9.0 // indirect | ||||
| 	golang.org/x/crypto v0.26.0 // indirect | ||||
| 	golang.org/x/net v0.28.0 // indirect | ||||
| 	golang.org/x/sys v0.24.0 // indirect | ||||
| 	golang.org/x/text v0.17.0 // indirect | ||||
| 	golang.org/x/arch v0.18.0 // indirect | ||||
| 	golang.org/x/crypto v0.39.0 // indirect | ||||
| 	golang.org/x/net v0.41.0 // indirect | ||||
| 	golang.org/x/sys v0.33.0 // indirect | ||||
| 	golang.org/x/text v0.26.0 // indirect | ||||
| 	golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect | ||||
| 	google.golang.org/protobuf v1.34.2 // indirect | ||||
| 	google.golang.org/protobuf v1.36.6 // indirect | ||||
| 	gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|  | ||||
							
								
								
									
										229
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										229
									
								
								go.sum
									
									
									
									
									
								
							| @ -1,45 +1,76 @@ | ||||
| filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= | ||||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyzYbZullK0aTHVASHl3UeoVr8IgytQZsuyv+yEM= | ||||
| 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/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250215141718-8232f587a6ea h1:6b9bfq44ewsXGVOkyZ+DQ4dNaKtmNsdHOFQxHUdEQrY= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250215141718-8232f587a6ea/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8 h1:VEifPc+vkpEQoX9rj7zxmT1m+IA81XjOxe7+Z1aqWNM= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250227040546-863c03f34bb8/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250420101447-0b570213d5c7 h1:dmZ/mwRQ/AGlKXYWyk4Ci+KRiMcJwZii7nUCj2F2bpM= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250420101447-0b570213d5c7/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995 h1:LmPRAf0AsxRVFPibdpZR89ajlsz8hof2IvMMyTqiEq4= | ||||
| git.zhangdeman.cn/zhangdeman/consts v0.0.0-20250425024726-cc17224cb995/go.mod h1:5p8CEKGBxi7qPtTXDI3HDmqKAfIm5i/aBWdrbkbdNjc= | ||||
| 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/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= | ||||
| git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240726061047-9b7e714b2932 h1:4UoCcCoTAFmS624lypM0D0Wlb4Q5Ru1saodR8Kj/gCg= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240726061047-9b7e714b2932/go.mod h1:CTcgmcDky34SS+KQvEt/RQgszyTVdNa9VZumGvN7Oqk= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512 h1:j+zH8gl4aEg24GWCutddnH5hw4eAuFJLW6vvJUJiJpM= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512/go.mod h1:twDDFXzPJ56qfAG1ZtPhvwXmtZG+aRlk2dnnjufh/hs= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a h1:22VkxGmpS58zHA8tf75lPr/3S6hmHKP00w/jUa700Ps= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a/go.mod h1:phaF6LMebn7Fpp8J/mOzHRYGniKuCk78k4N53T2m8NI= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 h1:1eBf0C0gdpBQOqjTK3UCw/mwzQ/SCodx3iTQtidx9eE= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442/go.mod h1:hFYWiS+ExIuJJJdwHWy4P3pVHbd/0mpv53qlbhDNdTI= | ||||
| git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4 h1:s6d4b6yY+NaK1AzoBD1pxqsuygEHQz0Oie86c45geDw= | ||||
| git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20241101082529-28a6c68e38a4/go.mod h1:V4Dfg1v/JVIZGEKCm6/aehs8hK+Xow1dkL1yiQymXlQ= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250225092214-3432087fbd07 h1:SadUKF3SZhuRTXaCKyQWEavN9fVLNbHL/GBz/KoiL6o= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250225092214-3432087fbd07/go.mod h1:T2Q8Wcq98yTuSSaEPZVAZfs0DMSxeXMN10GOQCha5g4= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250228104311-2fd9195b77e7 h1:koL8c0do1mOLFY+wLMqSpojgHSwVRbV6sSsJVKo9WfA= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250228104311-2fd9195b77e7/go.mod h1:T2Q8Wcq98yTuSSaEPZVAZfs0DMSxeXMN10GOQCha5g4= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250413074621-24f33309b9d8 h1:8Wt/SSVJSBR8/nddY+YYERTExc0DHrmShkk6GFUdWzw= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250413074621-24f33309b9d8/go.mod h1:HRY0KP893Oo1TZLPKv3XhAIhnMdtIipiylNsmJfZzD4= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250529071316-7a3da3614e07 h1:WTPPj5o/jsweVOUduA8PMDNNNS95cI2w483MEzg5//I= | ||||
| git.zhangdeman.cn/zhangdeman/gin v0.0.0-20250529071316-7a3da3614e07/go.mod h1:vnT78cHvj/fIx6KLNuUOvJrSJy/nckgqK+vRsc9zHD0= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250224140241-afda8c14a6d1 h1:CvtW010Fy9K84vslJ97zHGKdeh/w2lZzUqgpPXiyw0g= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250224140241-afda8c14a6d1/go.mod h1:ERUkpRvsn/LQFgyf9A9UCllTV2dSwnyJKWADw23NYTI= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250228081747-6ab4dff6a19d h1:kj8JhXRr67bFkZ9kRXFicDm6usy7fq9tz8zEBP8xRbM= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250228081747-6ab4dff6a19d/go.mod h1:HkinIO0tsK5mINroFZ3Qbsmh2V/DaEBtNWiX3+6DIzM= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250305064240-fee53ce64dc9 h1:HifwSBUtAxrKppYOZfKXI+iEtcTHh2+/zVeNsrIPN+M= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250305064240-fee53ce64dc9/go.mod h1:0QtFBqrH3FDygCcsgsP/bK5lHSq0qpAulU9HyxtG1pM= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5 h1:e3N+ODeTBwBOQ4iIzXm9gO3X6Htgc/YmPBImSk7o2mw= | ||||
| git.zhangdeman.cn/zhangdeman/logger v0.0.0-20250427065227-163236205af5/go.mod h1:lTxYyi7IodBefXSdSZwPFs3Izggg5Wxa2RkHlFVwKLg= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250224022106-1c57dcf5afd9 h1:MphPBVuufQt4O2Nm+A2ldG/dMmb0LXZwAGcE/OkTRoU= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250224022106-1c57dcf5afd9/go.mod h1:vSHUJdlbSVDheL+e7KtdG3n/fgb26J/JOMVuEiXG+A8= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250417104342-5592e0a45c32 h1:/ygkDt5WIL7iEBzOVAcxLCFiT9ajE1lGIQDcDNtvNx0= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250417104342-5592e0a45c32/go.mod h1:LKktkiO4at2XvPh/i9VPuT17DxLbBcDHUGMdrRSHNO4= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250529151912-0e6bd9e6691d h1:LnsfdKrInzynhoLNMu83wmfVLbGUmGYAPIJqdG6qRfA= | ||||
| git.zhangdeman.cn/zhangdeman/network v0.0.0-20250529151912-0e6bd9e6691d/go.mod h1:uRg3BnUimmrJiv8N61P6Ir4Im2EqxXZe62m4WBw3sec= | ||||
| git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 h1:gUDlQMuJ4xNfP2Abl1Msmpa3fASLWYkNlqDFF/6GN0Y= | ||||
| git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0/go.mod h1:VHb9qmhaPDAQDcS6vUiDCamYjZ4R5lD1XtVsh55KsMI= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd h1:2Y37waOVCmVvx0Rp8VGEptE2/2JVMImtxB4dKKDk/3w= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd/go.mod h1:6+7whkCmb4sJDIfH3HxNuXRveaM0gCCNWd2uXZqNtIE= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd h1:q7GG14qgXKB4MEXQFOe7/UYebsqMfPaSX80TcPdOosI= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20241223084948-de2e49144fcd/go.mod h1:+D6uPSljwHywjVY5WSBY4TRVMj26TN5f5cFGEYMldjs= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9 h1:/GLQaFoLb+ciHOtAS2BIyPNnf4O5ME3AC5PUaJY9kfs= | ||||
| git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20250504055908-8d68e6106ea9/go.mod h1:ABJ655C5QenQNOzf7LjCe4sSB52CXvaWLX2Zg4uwDJY= | ||||
| git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5 h1:dD1Q/MIrRmIhKqfYPH+y167ca9CKwTPuQt3c1hXWGJ8= | ||||
| git.zhangdeman.cn/zhangdeman/trace v0.0.0-20250412104923-c1ecb1bfe8d5/go.mod h1:PB486NC82nuvn5yi+U2i48ogX/9EAETWAHd8O9TwY9k= | ||||
| git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6CcWr1ICZhFI1STFOJ+KUImCl2BaIXm6YppBqI= | ||||
| git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI= | ||||
| git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 h1:P2kuhU2TFWk9mPbJlZCK6FMDVS7S3NGafWKD4eNqKiI= | ||||
| git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2/go.mod h1:7KaMpQmWgiNLpEkaV7oDtep7geI1f/VoCoPVcyvMjAE= | ||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfmp/1srHldFpa7BCH6Cpp9iVNFM/7W7DoxHp48UU= | ||||
| 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/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= | ||||
| git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e h1:YE2Gi+M03UDImIpWa3I7jzSesyfu2RL8x/4ONs5v0oE= | ||||
| git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20241125101541-c5ea194c9c1e/go.mod h1:L/7JugxKZL3JP9JP/XDvPAPz0FQXG1u181Su1+u/d1c= | ||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9 h1:yF770WIDNwyiKL0nwmBGmjZvNCLXtHQL4xJyffPjTMU= | ||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250124091620-c757e551a8c9/go.mod h1:I76wxEsWq7KnMQ84elpwTjEqq4I49QFw60tp5h7iGBs= | ||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740 h1:zPUoylfJTbc0EcxW+NEzOTBmoeFZ2I/rLFBnEzxb4Wk= | ||||
| git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20250321102712-1cbfbe959740/go.mod h1:1ct92dbVc49pmXusA/iGfcQUJzcYmJ+cjAhgc3sDv1I= | ||||
| 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.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= | ||||
| github.com/BurntSushi/toml v1.5.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/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg= | ||||
| github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24= | ||||
| github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk= | ||||
| github.com/bytedance/sonic v1.12.9 h1:Od1BvK55NnewtGaJsTDeAOSnLVO2BTSLOe0+ooKokmQ= | ||||
| github.com/bytedance/sonic v1.12.9/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8= | ||||
| github.com/bytedance/sonic v1.12.10 h1:uVCQr6oS5669E9ZVW0HyksTLfNS7Q/9hV6IVS4nEMsI= | ||||
| github.com/bytedance/sonic v1.12.10/go.mod h1:uVvFidNmlt9+wa31S1urfwwthTWteBgG0hWuoKAXTx8= | ||||
| github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= | ||||
| github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= | ||||
| github.com/bytedance/sonic v1.13.3 h1:MS8gmaH16Gtirygw7jV91pDCN33NyMrPbN7qiYhEsF0= | ||||
| github.com/bytedance/sonic v1.13.3/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= | ||||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM= | ||||
| github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= | ||||
| github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||||
| github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= | ||||
| github.com/bytedance/sonic/loader v0.2.3 h1:yctD0Q3v2NOGfSWPLPvG2ggA2kV6TS6s4wioyEqssH0= | ||||
| github.com/bytedance/sonic/loader v0.2.3/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= | ||||
| github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= | ||||
| github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= | ||||
| github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= | ||||
| github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||||
| github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| @ -47,12 +78,18 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | ||||
| github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= | ||||
| github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1 h1:CaO/zOnF8VvUfEbhRatPcwKVWamvbYd8tQGRWacE9kU= | ||||
| github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4= | ||||
| github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4= | ||||
| github.com/gabriel-vasile/mimetype v1.4.5/go.mod h1:ibHel+/kbxn9x2407k1izTA1S81ku1z/DlgOW2QE0M4= | ||||
| github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= | ||||
| github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= | ||||
| github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= | ||||
| github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= | ||||
| github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= | ||||
| github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= | ||||
| github.com/gin-contrib/sse v1.0.0 h1:y3bT1mUWUxDpW4JLQg/HnTqV4rozuW4tC9eFKTxYI9E= | ||||
| github.com/gin-contrib/sse v1.0.0/go.mod h1:zNuFdwarAygJBht0NTKiSi3jRf6RbqeILZ9Sp6Slhe0= | ||||
| github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= | ||||
| github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= | ||||
| github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | ||||
| github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | ||||
| github.com/gin-gonic/gin v1.10.1 h1:T0ujvqyCSqRopADpgPgiTT63DUQVSfojyME59Ei63pQ= | ||||
| github.com/gin-gonic/gin v1.10.1/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | ||||
| github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A= | ||||
| github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= | ||||
| github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||||
| @ -61,16 +98,24 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o | ||||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||||
| github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao= | ||||
| github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-playground/validator/v10 v10.25.0 h1:5Dh7cjvzR7BRZadnsVOzPhWsrwUr0nmsZJxEAnFLNO8= | ||||
| github.com/go-playground/validator/v10 v10.25.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus= | ||||
| github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k= | ||||
| github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= | ||||
| github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= | ||||
| github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= | ||||
| github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= | ||||
| github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA= | ||||
| github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | ||||
| github.com/go-sql-driver/mysql v1.9.0 h1:Y0zIbQXhQKmQgTp44Y1dp3wTXcn804QoTptLZT1vtvo= | ||||
| github.com/go-sql-driver/mysql v1.9.0/go.mod h1:pDetrLJeA3oMujJuvXc8RJoasr589B6A9fwzD3QMrqw= | ||||
| github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU= | ||||
| github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= | ||||
| github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= | ||||
| github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= | ||||
| github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= | ||||
| github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= | ||||
| github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= | ||||
| github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= | ||||
| github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= | ||||
| github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= | ||||
| github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= | ||||
| @ -82,23 +127,33 @@ github.com/jonboulle/clockwork v0.3.0/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUB | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||
| github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= | ||||
| github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||||
| github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= | ||||
| github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= | ||||
| github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||||
| github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2ttpEmkA4aQ3j4u9dStX2t4M8UM6qqNsG8= | ||||
| github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is= | ||||
| github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4= | ||||
| github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA= | ||||
| github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ= | ||||
| github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw= | ||||
| github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg= | ||||
| github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI= | ||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= | ||||
| github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= | ||||
| github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= | ||||
| github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| github.com/mcuadros/go-defaults v1.2.0 h1:FODb8WSf0uGaY8elWJAkoLL0Ri6AlZ1bFlenk56oZtc= | ||||
| github.com/mcuadros/go-defaults v1.2.0/go.mod h1:WEZtHEVIGYVDqkKSWBdWKUVdRyKlMfulPaGDWIVeCWY= | ||||
| github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= | ||||
| github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= | ||||
| github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= | ||||
| github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| @ -108,12 +163,18 @@ github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+ | ||||
| github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc= | ||||
| github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4= | ||||
| github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= | ||||
| github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= | ||||
| github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= | ||||
| github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= | ||||
| github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= | ||||
| github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= | ||||
| github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/sbabiv/xml2map v1.2.1 h1:1lT7t0hhUvXZCkdxqtq4n8/ZCnwLWGq4rDuDv5XOoFE= | ||||
| github.com/sbabiv/xml2map v1.2.1/go.mod h1:2TPoAfcaM7+Sd4iriPvzyntb2mx7GY+kkQpB/GQa/eo= | ||||
| github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= | ||||
| github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| @ -126,10 +187,10 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94= | ||||
| github.com/tidwall/gjson v1.17.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | ||||
| github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= | ||||
| github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= | ||||
| github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= | ||||
| github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= | ||||
| github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= | ||||
| github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= | ||||
| @ -139,6 +200,8 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||||
| github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= | ||||
| github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= | ||||
| github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 h1:zzrxE1FKn5ryBNl9eKOeqQ58Y/Qpo3Q9QNxKHX5uzzQ= | ||||
| github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY= | ||||
| go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= | ||||
| @ -147,25 +210,47 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= | ||||
| go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= | ||||
| go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= | ||||
| go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= | ||||
| golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k= | ||||
| golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= | ||||
| golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= | ||||
| golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= | ||||
| golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/arch v0.14.0 h1:z9JUEZWr8x4rR0OU6c4/4t6E6jOZ8/QBS2bBYBm4tx4= | ||||
| golang.org/x/arch v0.14.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/arch v0.16.0 h1:foMtLTdyOmIniqWCHjY6+JxuC54XP1fDwx4N0ASyW+U= | ||||
| golang.org/x/arch v0.16.0/go.mod h1:JmwW7aLIoRUKgaTzhkiEFxvcEiQGyOg9BMonBJUS7EE= | ||||
| golang.org/x/arch v0.18.0 h1:WN9poc33zL4AzGxqf8VtpKUnGvMi8O9lhNyBMF/85qc= | ||||
| golang.org/x/arch v0.18.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= | ||||
| golang.org/x/crypto v0.35.0 h1:b15kiHdrGCHrP6LvwaQ3c03kgNhhiMgvlhxHQhmg2Xs= | ||||
| golang.org/x/crypto v0.35.0/go.mod h1:dy7dXNW32cAb/6/PRuTNsix8T+vJAqvuIy5Bli/x0YQ= | ||||
| golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= | ||||
| golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= | ||||
| golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= | ||||
| golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= | ||||
| golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= | ||||
| golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= | ||||
| golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= | ||||
| golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= | ||||
| golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= | ||||
| golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= | ||||
| golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= | ||||
| golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= | ||||
| golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= | ||||
| golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= | ||||
| golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= | ||||
| golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||
| golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= | ||||
| golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= | ||||
| golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= | ||||
| golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= | ||||
| golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= | ||||
| golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= | ||||
| golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= | ||||
| google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= | ||||
| google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= | ||||
| google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= | ||||
| google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= | ||||
| google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= | ||||
| gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 h1:sY2a+y0j4iDrajJcorb+a0hJIQ6uakU5gybjfLWHlXo= | ||||
| gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376/go.mod h1:BHKOc1m5wm8WwQkMqYBoo4vNxhmF7xg8+xhG8L+Cy3M= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| @ -173,9 +258,15 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= | ||||
| gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= | ||||
| gorm.io/driver/sqlite v1.5.6 h1:fO/X46qn5NUEEOZtnjJRWRzZMe8nqJiQ9E+0hi+hKQE= | ||||
| gorm.io/driver/sqlite v1.5.6/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= | ||||
| gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= | ||||
| gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= | ||||
| gorm.io/driver/sqlite v1.5.7 h1:8NvsrhP0ifM7LX9G4zPB97NwovUakUxc+2V2uuf3Z1I= | ||||
| gorm.io/driver/sqlite v1.5.7/go.mod h1:U+J8craQU6Fzkcvu8oLeAQmi50TkwPEhHDEjQZXDah4= | ||||
| gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= | ||||
| gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= | ||||
| gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= | ||||
| gorm.io/gorm v1.25.11 h1:/Wfyg1B/je1hnDx3sMkX+gAlxrlZpn6X0BXRlwXlvHg= | ||||
| gorm.io/gorm v1.25.11/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= | ||||
| gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= | ||||
| gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= | ||||
| gorm.io/gorm v1.30.0 h1:qbT5aPv1UH8gI99OsRlvDToLxW5zR7FzS9acZDOZcgs= | ||||
| gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= | ||||
| nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= | ||||
|  | ||||
							
								
								
									
										236
									
								
								option.go
									
									
									
									
									
								
							
							
						
						
									
										236
									
								
								option.go
									
									
									
									
									
								
							| @ -9,47 +9,23 @@ package database | ||||
|  | ||||
| import ( | ||||
| 	"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/serialize" | ||||
| 	"reflect" | ||||
| 	"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 ... | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 15:18 2024/1/15 | ||||
| func WithModel(model any) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithModel(model any) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		o.Model = model | ||||
| 	} | ||||
| } | ||||
| @ -59,8 +35,8 @@ func WithModel(model any) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 20:56 2023/3/22 | ||||
| func WithTable(tableName string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithTable(tableName string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		o.Table = tableName | ||||
| 	} | ||||
| } | ||||
| @ -70,8 +46,8 @@ func WithTable(tableName string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 12:17 2022/5/15 | ||||
| func WithWhere[T op_type.BaseType](where map[string]T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithWhere[T op_type.BaseType](where map[string]T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.Where { | ||||
| 			o.Where = make(map[string]any) | ||||
| 		} | ||||
| @ -86,20 +62,27 @@ func WithWhere[T op_type.BaseType](where map[string]T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 12:00 2022/5/15 | ||||
| func WithLimit[T op_type.Int](limit T, offset T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithLimit[T op_type.Int](limit T, offset T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		o.Limit = int(limit) | ||||
| 		o.Offset = int(offset) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func WithClearLimit() define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		o.Limit = 0 | ||||
| 		o.Offset = 0 | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // WithLimitByPageAndSize 通过page和size构建条件 | ||||
| // | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 21:42 2023/10/14 | ||||
| func WithLimitByPageAndSize[T op_type.Int](page T, size T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithLimitByPageAndSize[T op_type.Int](page T, size T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if size > 0 { | ||||
| 			o.Limit = int(size) | ||||
| 		} | ||||
| @ -115,8 +98,8 @@ func WithLimitByPageAndSize[T op_type.Int](page T, size T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 12:23 2022/5/15 | ||||
| func WithIn[T op_type.Array](field string, value T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithIn[T op_type.Array](field string, value T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == value { | ||||
| 			return | ||||
| 		} | ||||
| @ -135,8 +118,8 @@ func WithIn[T op_type.Array](field string, value T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 12:24 2022/5/15 | ||||
| func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchIn[T op_type.Array](batchIn map[string]T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.In { | ||||
| 			o.In = make(map[string]any) | ||||
| 		} | ||||
| @ -151,8 +134,8 @@ func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 16:18 2022/5/15 | ||||
| func WithNotIn[T op_type.Array](field string, value T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithNotIn[T op_type.Array](field string, value T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotIn { | ||||
| 			o.NotIn = make(map[string]any) | ||||
| 		} | ||||
| @ -168,8 +151,8 @@ func WithNotIn[T op_type.Array](field string, value T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 16:23 2022/5/15 | ||||
| func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchNotIn[T op_type.Array](data map[string]T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotIn { | ||||
| 			o.NotIn = make(map[string]any) | ||||
| 		} | ||||
| @ -187,8 +170,8 @@ func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:01 2022/5/15 | ||||
| func WithStart(field string, value any) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithStart(field string, value any) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.Start { | ||||
| 			o.Start = make(map[string]any) | ||||
| 		} | ||||
| @ -201,8 +184,8 @@ func WithStart(field string, value any) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:03 2022/5/15 | ||||
| func WithBatchStart(data map[string]any) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchStart(data map[string]any) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.Start { | ||||
| 			o.Start = make(map[string]any) | ||||
| 		} | ||||
| @ -217,8 +200,8 @@ func WithBatchStart(data map[string]any) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:07 2022/5/15 | ||||
| func WithEnd(field string, value any) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithEnd(field string, value any) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.End { | ||||
| 			o.End = make(map[string]any) | ||||
| 		} | ||||
| @ -231,8 +214,8 @@ func WithEnd(field string, value any) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:10 2022/5/15 | ||||
| func WithBatchEnd(data map[string]any) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchEnd(data map[string]any) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.End { | ||||
| 			o.End = make(map[string]any) | ||||
| 		} | ||||
| @ -247,8 +230,8 @@ func WithBatchEnd(data map[string]any) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:16 2022/5/15 | ||||
| func WithLike(field string, value string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithLike(field string, value string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.Like { | ||||
| 			o.Like = make(map[string]string) | ||||
| 		} | ||||
| @ -264,8 +247,8 @@ func WithLike(field string, value string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:19 2022/5/15 | ||||
| func WithBatchLike(data map[string]string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchLike(data map[string]string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.Like { | ||||
| 			o.Like = make(map[string]string) | ||||
| 		} | ||||
| @ -283,8 +266,8 @@ func WithBatchLike(data map[string]string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:50 2022/5/15 | ||||
| func WithNotLike(field string, value string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithNotLike(field string, value string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotLike { | ||||
| 			o.NotLike = make(map[string]string) | ||||
| 		} | ||||
| @ -300,8 +283,8 @@ func WithNotLike(field string, value string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:52 2022/5/15 | ||||
| func WithBatchNotLike(data map[string]string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchNotLike(data map[string]string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotLike { | ||||
| 			o.NotLike = make(map[string]string) | ||||
| 		} | ||||
| @ -319,8 +302,8 @@ func WithBatchNotLike(data map[string]string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:31 2022/5/15 | ||||
| func WithNotEqual[T op_type.BaseType](field string, value T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithNotEqual[T op_type.BaseType](field string, value T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotEqual { | ||||
| 			o.NotEqual = make(map[string]any) | ||||
| 		} | ||||
| @ -333,8 +316,8 @@ func WithNotEqual[T op_type.BaseType](field string, value T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:33 2022/5/15 | ||||
| func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithBatchNotEqual[T op_type.BaseType](data map[string]T) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.NotEqual { | ||||
| 			o.NotEqual = make(map[string]any) | ||||
| 		} | ||||
| @ -349,10 +332,10 @@ func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 20:03 2022/7/23 | ||||
| func WithOR(orConditionList ...SetOption) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithOR(orConditionList ...define.SetOption) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if nil == o.OR { | ||||
| 			o.OR = make([][]SetOption, 0) | ||||
| 			o.OR = make([][]define.SetOption, 0) | ||||
| 		} | ||||
| 		o.OR = append(o.OR, orConditionList) | ||||
| 	} | ||||
| @ -363,8 +346,19 @@ func WithOR(orConditionList ...SetOption) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 20:15 2022/10/20 | ||||
| func WithOrder(orderRuleList ...string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| func WithOrder(orderRuleList ...string) define.SetOption { | ||||
| 	return func(o *define.Option) { | ||||
| 		if len(orderRuleList) != 2 { | ||||
| 			return | ||||
| 		} | ||||
| 		if len(orderRuleList[0]) == 0 { | ||||
| 			// 未指定排序字段 | ||||
| 			return | ||||
| 		} | ||||
| 		if len(orderRuleList[1]) == 0 { | ||||
| 			// 未指定排序规则, 默认倒序 | ||||
| 			orderRuleList[1] = "DESC" | ||||
| 		} | ||||
| 		o.Order = orderRuleList | ||||
| 	} | ||||
| } | ||||
| @ -374,13 +368,8 @@ func WithOrder(orderRuleList ...string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 15:09 2023/4/3 | ||||
| func WithOrderDesc(field string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| 		if nil == o.Order { | ||||
| 			o.Order = make([]string, 0) | ||||
| 		} | ||||
| 		o.Order = append(o.Order, field+" desc") | ||||
| 	} | ||||
| func WithOrderDesc(field string) define.SetOption { | ||||
| 	return WithOrder(field, "DESC") | ||||
| } | ||||
|  | ||||
| // WithOrderAsc 升序排序 | ||||
| @ -388,13 +377,8 @@ func WithOrderDesc(field string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 15:09 2023/4/3 | ||||
| func WithOrderAsc(field string) SetOption { | ||||
| 	return func(o *Option) { | ||||
| 		if nil == o.Order { | ||||
| 			o.Order = make([]string, 0) | ||||
| 		} | ||||
| 		o.Order = append(o.Order, field+" asc") | ||||
| 	} | ||||
| func WithOrderAsc(field string) define.SetOption { | ||||
| 	return WithOrder(field, "ASC") | ||||
| } | ||||
|  | ||||
| // newOption 生成新的option | ||||
| @ -402,8 +386,8 @@ func WithOrderAsc(field string) SetOption { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // Date : 17:46 2024/8/9 | ||||
| func newOption(setOptionList ...SetOption) *Option { | ||||
| 	o := &Option{} | ||||
| func newOption(setOptionList ...define.SetOption) *define.Option { | ||||
| 	o := &define.Option{} | ||||
| 	for _, item := range setOptionList { | ||||
| 		item(o) | ||||
| 	} | ||||
| @ -415,7 +399,7 @@ func newOption(setOptionList ...SetOption) *Option { | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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) | ||||
| 	sqlBuildResultBlockList := make([]string, 0) | ||||
| 	// 设置where条件 | ||||
| @ -484,6 +468,17 @@ func optionToSql(o *Option) (sqlBuildResult string, bindValue []any) { | ||||
| 		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 { | ||||
| 		sqlBuildResult = strings.Join(sqlBuildResultBlockList, " AND ") | ||||
| 	} | ||||
| @ -508,3 +503,66 @@ func parseInSql(fieldValue any) (string, []any) { | ||||
| 	} | ||||
| 	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 ( | ||||
| 	"fmt" | ||||
| 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||
| 	"testing" | ||||
| ) | ||||
|  | ||||
| func Test_optionToSql(t *testing.T) { | ||||
| 	type args struct { | ||||
| 		o *Option | ||||
| 	} | ||||
| 	o := &Option{ | ||||
| 	o := &define.Option{ | ||||
| 		In:    nil, | ||||
| 		NotIn: nil, | ||||
| 		Where: map[string]any{ | ||||
|  | ||||
| @ -12,6 +12,7 @@ import ( | ||||
|  | ||||
| 	"gorm.io/driver/mysql" | ||||
|  | ||||
| 	"git.zhangdeman.cn/zhangdeman/database/define" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| @ -99,10 +100,10 @@ func (sd *SystemDao) GetCreateTableSQL(dbInstance *gorm.DB, table string) (strin | ||||
| // Author : go_developer@163.com<白茶清欢> | ||||
| // | ||||
| // 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 ( | ||||
| 		err    error | ||||
| 		result []*DescTableItem | ||||
| 		result []*define.DescTableItem | ||||
| 	) | ||||
|  | ||||
| 	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<白茶清欢> | ||||
| // | ||||
| // 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 ( | ||||
| 		list []*ColumnInfo | ||||
| 		list []*define.ColumnInfo | ||||
| 		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 := consts.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 | ||||
| } | ||||
							
								
								
									
										276
									
								
								wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										276
									
								
								wrapper_db_client.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,276 @@ | ||||
| // 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) | ||||
| 	} | ||||
|  | ||||
| 	dbInstance, _ := instance.DB() | ||||
| 	if nil == conf.Connection { | ||||
| 		conf.Connection = &define.Connection{ | ||||
| 			MaxOpen: 100, | ||||
| 			MaxIdle: 100, | ||||
| 		} | ||||
| 	} | ||||
| 	dbInstance.SetMaxIdleConns(conf.Connection.MaxIdle) | ||||
| 	dbInstance.SetMaxOpenConns(conf.Connection.MaxOpen) | ||||
| 	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