From 5024ae42388bde6f4c32edca5144953dfc23c8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 20 Aug 2024 17:42:36 +0800 Subject: [PATCH 01/26] =?UTF-8?q?=E8=BF=81=E7=A7=BB=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93=E9=9D=A2=E5=90=91=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=20-=20=E4=BF=9D=E5=AD=98=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/api_sql_manager.go | 24 ++++++++ abstract/wrapper_client.go | 27 +++++++++ client.go | 104 +++++++--------------------------- define/db_client.go | 70 +++++++++++++++++++++++ define.go => define/define.go | 8 +-- 5 files changed, 147 insertions(+), 86 deletions(-) create mode 100644 abstract/api_sql_manager.go create mode 100644 abstract/wrapper_client.go create mode 100644 define/db_client.go rename define.go => define/define.go (98%) diff --git a/abstract/api_sql_manager.go b/abstract/api_sql_manager.go new file mode 100644 index 0000000..1ec1cd9 --- /dev/null +++ b/abstract/api_sql_manager.go @@ -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) +} diff --git a/abstract/wrapper_client.go b/abstract/wrapper_client.go new file mode 100644 index 0000000..b046dab --- /dev/null +++ b/abstract/wrapper_client.go @@ -0,0 +1,27 @@ +// Package abstract ... +// +// Description : abstract ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-08-20 17:26 +package abstract + +import ( + "git.zhangdeman.cn/zhangdeman/database/define" + "go.uber.org/zap" +) + +// IWrapperClient 包装的客户端 +// +// 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 +} diff --git a/client.go b/client.go index 1e910c5..b5de29c 100644 --- a/client.go +++ b/client.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "git.zhangdeman.cn/zhangdeman/consts" + "git.zhangdeman.cn/zhangdeman/database/define" "git.zhangdeman.cn/zhangdeman/serialize" "path/filepath" "strings" @@ -20,8 +21,6 @@ import ( "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" @@ -35,13 +34,13 @@ var ( func init() { Client = &client{ lock: &sync.RWMutex{}, - clientTable: make(map[string]*DBClient), + clientTable: make(map[string]*define.DBClient), } } type client struct { lock *sync.RWMutex - clientTable map[string]*DBClient + clientTable map[string]*define.DBClient logger *zap.Logger } @@ -53,7 +52,7 @@ type client struct { func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error { var ( err error - cfg *cfgFile + cfg *define.CfgFile ) if cfg, err = c.getCfg(cfgFilePath); nil != err { @@ -71,14 +70,14 @@ func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, // 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, +func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error { + dbClient := &define.DBClient{ + DbFlag: flag, + LoggerInstance: logInstance, master: nil, slave: nil, - extraFieldList: extraFieldList, - cfg: Driver{}, + ExtraFieldList: extraFieldList, + Cfg: define.Driver{}, } var err error if dbClient.master, err = c.GetDatabaseClient(databaseConfig.Master, logInstance); nil != err { @@ -113,7 +112,7 @@ func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, e // Author : go_developer@163.com<白茶清欢> // // Date : 18:05 2022/6/11 -func (c *client) getCfg(cfgPath string) (*cfgFile, error) { +func (c *client) getCfg(cfgPath string) (*define.CfgFile, error) { fileArr := strings.Split(cfgPath, ".") if len(fileArr) < 2 { // 获取不到类型 @@ -121,15 +120,15 @@ func (c *client) getCfg(cfgPath string) (*cfgFile, error) { } fileType := strings.ToLower(fileArr[len(fileArr)-1]) fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator)) - result := &cfgFile{ + result := &define.CfgFile{ Path: cfgPath, Type: "", Flag: fileFlagArr[len(fileFlagArr)-1], - Config: &Database{}, + Config: &define.Database{}, } var ( err error - cfgInfo Database + cfgInfo define.Database ) switch fileType { case consts.FileTypeYaml: @@ -162,12 +161,12 @@ func (c *client) getCfg(cfgPath string) (*cfgFile, error) { // Author : go_developer@163.com<白茶清欢> // // Date : 19:32 2022/6/5 -func (c *client) GetDBClient(dbFlag string) (*DBClient, error) { +func (c *client) GetDBClient(dbFlag string) (*define.DBClient, error) { c.lock.RLock() defer c.lock.RUnlock() var ( exist bool - dbClient *DBClient + dbClient *define.DBClient ) if dbClient, exist = c.clientTable[dbFlag]; !exist { return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag) @@ -183,15 +182,12 @@ func (c *client) GetDBClient(dbFlag string) (*DBClient, error) { func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error - dbClient *DBClient + dbClient *define.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 + return dbClient.GetMaster(ctx), nil } // GetSlaveClient 获取从库客户端 @@ -202,15 +198,13 @@ func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error - dbClient *DBClient + dbClient *define.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 + return dbClient.GetSlave(ctx), nil } // getGormClient 获取GORM client方法 @@ -227,7 +221,7 @@ func (c *client) getGormClient() (*gorm.DB, error) { // Author : go_developer@163.com<白茶清欢> // // Date : 18:41 2022/6/11 -func (c *client) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm.DB, error) { +func (c *client) GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error) { var ( instance *gorm.DB err error @@ -255,7 +249,7 @@ func (c *client) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm // Author : go_developer@163.com<白茶清欢> // // Date : 18:42 2022/6/11 -func (c *client) buildConnectionDSN(conf *Driver) string { +func (c *client) buildConnectionDSN(conf *define.Driver) string { if conf.DBType == consts.DatabaseDriverSqlite3 { // 兼容sqlite3 return conf.Host @@ -271,57 +265,3 @@ func (c *client) buildConnectionDSN(conf *Driver) string { 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) -} diff --git a/define/db_client.go b/define/db_client.go new file mode 100644 index 0000000..c9603e0 --- /dev/null +++ b/define/db_client.go @@ -0,0 +1,70 @@ +// Package define ... +// +// Description : define ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-08-20 17:36 +package define + +import ( + "context" + "git.zhangdeman.cn/zhangdeman/logger/wrapper" + "go.uber.org/zap" + "gorm.io/gorm" + gormLogger "gorm.io/gorm/logger" +) + +// 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, "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) +} diff --git a/define.go b/define/define.go similarity index 98% rename from define.go rename to define/define.go index 1c09d03..49076c7 100644 --- a/define.go +++ b/define/define.go @@ -1,11 +1,11 @@ -// package database... +// Package define ... // // Description : 数据定义 // // Author : go_developer@163.com<白茶清欢> // // Date : 2021-03-01 9:27 下午 -package database +package define // DBConfig 数据库连接的配置 // @@ -22,12 +22,12 @@ 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 { +type CfgFile struct { Flag string `json:"flag"` // 数据库标识 Path string `json:"path"` // 配置文件路径 Type string `json:"type"` // 配置文件类型 From 5e87706d0a18d73b972e512ff53ef107a1adf289 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 20 Aug 2024 17:56:43 +0800 Subject: [PATCH 02/26] =?UTF-8?q?wrapper=5Fclient=20=E9=9D=A2=E5=90=91?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/wrapper_client.go | 8 +++ define/db_client.go | 74 ++++++++++++++++++++++++++- client.go => wrapper_client.go | 93 ++++++++-------------------------- 3 files changed, 100 insertions(+), 75 deletions(-) rename client.go => wrapper_client.go (62%) diff --git a/abstract/wrapper_client.go b/abstract/wrapper_client.go index b046dab..3c7090a 100644 --- a/abstract/wrapper_client.go +++ b/abstract/wrapper_client.go @@ -8,8 +8,10 @@ package abstract import ( + "context" "git.zhangdeman.cn/zhangdeman/database/define" "go.uber.org/zap" + "gorm.io/gorm" ) // IWrapperClient 包装的客户端 @@ -24,4 +26,10 @@ type IWrapperClient interface { 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) (*define.DBClient, error) + // GetMasterClient 获取主库连接 + GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) + // GetSlaveClient 获取从库连接 + GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) } diff --git a/define/db_client.go b/define/db_client.go index c9603e0..8039393 100644 --- a/define/db_client.go +++ b/define/db_client.go @@ -9,8 +9,12 @@ package define import ( "context" + "fmt" + "git.zhangdeman.cn/zhangdeman/consts" "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" ) @@ -29,13 +33,29 @@ type DBClient struct { Cfg Driver // 数据库配置 } +// Init 初始化客户端 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 17:44 2024/8/20 +func (dc *DBClient) Init(databaseConfig *Database) error { + 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 + } + return nil +} + // SetFlag 设置数据库标识 // // Author : go_developer@163.com<白茶清欢> // // Date : 16:18 2022/6/5 func (dc *DBClient) SetFlag(dbFlag string) { - dc.dbFlag = dbFlag + dc.DbFlag = dbFlag } // GetMaster 获取主库连接 @@ -66,5 +86,55 @@ func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB { // // 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) + 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 *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(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { + return nil, err + } + } else if conf.DBType == consts.DatabaseDriverSqlite3 { + if instance, err = gorm.Open(sqlite.Open(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err { + return nil, err + } + } else { + return nil, fmt.Errorf("%v : db driver is not support", conf.DBType) + } + + instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil) + + return instance, nil +} + +// buildConnectionDSN 构建连接信息 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 18:42 2022/6/11 +func (dc *DBClient) buildConnectionDSN(conf *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, + ) } diff --git a/client.go b/wrapper_client.go similarity index 62% rename from client.go rename to wrapper_client.go index b5de29c..a199469 100644 --- a/client.go +++ b/wrapper_client.go @@ -12,33 +12,35 @@ import ( "errors" "fmt" "git.zhangdeman.cn/zhangdeman/consts" + "git.zhangdeman.cn/zhangdeman/database/abstract" "git.zhangdeman.cn/zhangdeman/database/define" "git.zhangdeman.cn/zhangdeman/serialize" "path/filepath" "strings" "sync" - "git.zhangdeman.cn/zhangdeman/logger/wrapper" "go.uber.org/zap" - "gorm.io/driver/mysql" - "gorm.io/driver/sqlite" "gorm.io/gorm" ) var ( - // Client mysql客户端 - Client *client + // WrapperClient 包装后的数据库客户端 + WrapperClient abstract.IWrapperClient ) func init() { - Client = &client{ + WrapperClient = NewWrapperClient() +} + +func NewWrapperClient() *wrapperClient { + return &wrapperClient{ lock: &sync.RWMutex{}, clientTable: make(map[string]*define.DBClient), } } -type client struct { +type wrapperClient struct { lock *sync.RWMutex clientTable map[string]*define.DBClient logger *zap.Logger @@ -49,7 +51,7 @@ type client struct { // Author : go_developer@163.com<白茶清欢> // // Date : 19:19 2022/6/5 -func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error { +func (c *wrapperClient) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error { var ( err error cfg *define.CfgFile @@ -70,24 +72,19 @@ func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, // Author : go_developer@163.com<白茶清欢> // // Date : 20:41 2023/4/18 -func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error { +func (c *wrapperClient) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error { dbClient := &define.DBClient{ DbFlag: flag, LoggerInstance: logInstance, - master: nil, - slave: nil, ExtraFieldList: extraFieldList, Cfg: define.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 { + + if err := dbClient.Init(databaseConfig); nil != err { return err } c.lock.Lock() - c.clientTable[dbClient.dbFlag] = dbClient + c.clientTable[dbClient.DbFlag] = dbClient c.lock.Unlock() return nil } @@ -97,7 +94,7 @@ func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseCon // Author : go_developer@163.com<白茶清欢> // // Date : 19:19 2022/6/5 -func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error { +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 { @@ -112,7 +109,7 @@ func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, e // Author : go_developer@163.com<白茶清欢> // // Date : 18:05 2022/6/11 -func (c *client) getCfg(cfgPath string) (*define.CfgFile, error) { +func (c *wrapperClient) getCfg(cfgPath string) (*define.CfgFile, error) { fileArr := strings.Split(cfgPath, ".") if len(fileArr) < 2 { // 获取不到类型 @@ -161,7 +158,7 @@ func (c *client) getCfg(cfgPath string) (*define.CfgFile, error) { // Author : go_developer@163.com<白茶清欢> // // Date : 19:32 2022/6/5 -func (c *client) GetDBClient(dbFlag string) (*define.DBClient, error) { +func (c *wrapperClient) GetDBClient(dbFlag string) (*define.DBClient, error) { c.lock.RLock() defer c.lock.RUnlock() var ( @@ -179,7 +176,7 @@ func (c *client) GetDBClient(dbFlag string) (*define.DBClient, error) { // Author : go_developer@163.com<白茶清欢> // // Date : 19:36 2022/6/5 -func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { +func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error dbClient *define.DBClient @@ -195,7 +192,7 @@ func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, // Author : go_developer@163.com<白茶清欢> // // Date : 19:37 2022/6/5 -func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { +func (c *wrapperClient) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error dbClient *define.DBClient @@ -212,56 +209,6 @@ func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, e // Author : go_developer@163.com<白茶清欢> // // Date : 11:24 2022/6/6 -func (c *client) getGormClient() (*gorm.DB, error) { +func (c *wrapperClient) getGormClient() (*gorm.DB, error) { return nil, nil } - -// GetDatabaseClient 获取数据库连接 -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 18:41 2022/6/11 -func (c *client) GetDatabaseClient(conf *define.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 *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, - ) -} From 4ba14dc23d5ba88f896c546d5f65085a1def8fc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 20 Aug 2024 18:14:57 +0800 Subject: [PATCH 03/26] =?UTF-8?q?db=5Fclient=20=E9=9D=A2=E5=90=91=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/wrapper_client.go | 4 +-- abstract/wrapper_db_client.go | 31 +++++++++++++++++++ wrapper_client.go | 14 ++++----- define/db_client.go => wrapper_db_client.go | 34 +++++++++------------ 4 files changed, 55 insertions(+), 28 deletions(-) create mode 100644 abstract/wrapper_db_client.go rename define/db_client.go => wrapper_db_client.go (81%) diff --git a/abstract/wrapper_client.go b/abstract/wrapper_client.go index 3c7090a..5f12266 100644 --- a/abstract/wrapper_client.go +++ b/abstract/wrapper_client.go @@ -14,7 +14,7 @@ import ( "gorm.io/gorm" ) -// IWrapperClient 包装的客户端 +// IWrapperClient 包装的客户端, 包装 IWrapperDatabaseClient // // Author : go_developer@163.com<白茶清欢> // @@ -27,7 +27,7 @@ type IWrapperClient interface { // BatchAddWithConfigDir 通过具体的配置列表增加数据库实例 BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error // GetDBClient 基于数据库标识获取数据库实例 - GetDBClient(dbFlag string) (*define.DBClient, error) + GetDBClient(dbFlag string) (IWrapperDatabaseClient, error) // GetMasterClient 获取主库连接 GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) // GetSlaveClient 获取从库连接 diff --git a/abstract/wrapper_db_client.go b/abstract/wrapper_db_client.go new file mode 100644 index 0000000..118719e --- /dev/null +++ b/abstract/wrapper_db_client.go @@ -0,0 +1,31 @@ +// 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) 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) +} diff --git a/wrapper_client.go b/wrapper_client.go index a199469..6698d91 100644 --- a/wrapper_client.go +++ b/wrapper_client.go @@ -36,13 +36,13 @@ func init() { func NewWrapperClient() *wrapperClient { return &wrapperClient{ lock: &sync.RWMutex{}, - clientTable: make(map[string]*define.DBClient), + clientTable: make(map[string]abstract.IWrapperDatabaseClient), } } type wrapperClient struct { lock *sync.RWMutex - clientTable map[string]*define.DBClient + clientTable map[string]abstract.IWrapperDatabaseClient logger *zap.Logger } @@ -73,7 +73,7 @@ func (c *wrapperClient) AddWithConfigFile(cfgFilePath string, logInstance *zap.L // // Date : 20:41 2023/4/18 func (c *wrapperClient) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error { - dbClient := &define.DBClient{ + dbClient := &DBClient{ DbFlag: flag, LoggerInstance: logInstance, ExtraFieldList: extraFieldList, @@ -158,12 +158,12 @@ func (c *wrapperClient) getCfg(cfgPath string) (*define.CfgFile, error) { // Author : go_developer@163.com<白茶清欢> // // Date : 19:32 2022/6/5 -func (c *wrapperClient) GetDBClient(dbFlag string) (*define.DBClient, error) { +func (c *wrapperClient) GetDBClient(dbFlag string) (abstract.IWrapperDatabaseClient, error) { c.lock.RLock() defer c.lock.RUnlock() var ( exist bool - dbClient *define.DBClient + dbClient abstract.IWrapperDatabaseClient ) if dbClient, exist = c.clientTable[dbFlag]; !exist { return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag) @@ -179,7 +179,7 @@ func (c *wrapperClient) GetDBClient(dbFlag string) (*define.DBClient, error) { func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error - dbClient *define.DBClient + dbClient abstract.IWrapperDatabaseClient ) if dbClient, err = c.GetDBClient(dbFlag); nil != err { return nil, err @@ -195,7 +195,7 @@ func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*go func (c *wrapperClient) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) { var ( err error - dbClient *define.DBClient + dbClient abstract.IWrapperDatabaseClient ) if dbClient, err = c.GetDBClient(dbFlag); nil != err { return nil, err diff --git a/define/db_client.go b/wrapper_db_client.go similarity index 81% rename from define/db_client.go rename to wrapper_db_client.go index 8039393..958ffd0 100644 --- a/define/db_client.go +++ b/wrapper_db_client.go @@ -5,12 +5,13 @@ // Author : go_developer@163.com<白茶清欢> // // Date : 2024-08-20 17:36 -package define +package database import ( "context" "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" @@ -25,12 +26,12 @@ import ( // // 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 // 数据库配置 + DbFlag string // 数据库标识 + LoggerInstance *zap.Logger // 日志实例 + master *gorm.DB // 主库 + slave *gorm.DB // 从库 + ExtraFieldList []string // 提取的字段 + Cfg define.Driver // 数据库配置 } // Init 初始化客户端 @@ -38,7 +39,7 @@ type DBClient struct { // Author : go_developer@163.com<白茶清欢> // // Date : 17:44 2024/8/20 -func (dc *DBClient) Init(databaseConfig *Database) error { +func (dc *DBClient) Init(databaseConfig *define.Database) error { var err error if dc.master, err = dc.GetDatabaseClient(databaseConfig.Master, dc.LoggerInstance); nil != err { return err @@ -49,15 +50,6 @@ func (dc *DBClient) Init(databaseConfig *Database) error { return nil } -// 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<白茶清欢> @@ -94,12 +86,16 @@ func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node strin // Author : go_developer@163.com<白茶清欢> // // Date : 18:41 2022/6/11 -func (dc *DBClient) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm.DB, error) { +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 @@ -122,7 +118,7 @@ func (dc *DBClient) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*g // Author : go_developer@163.com<白茶清欢> // // Date : 18:42 2022/6/11 -func (dc *DBClient) buildConnectionDSN(conf *Driver) string { +func (dc *DBClient) buildConnectionDSN(conf *define.Driver) string { if conf.DBType == consts.DatabaseDriverSqlite3 { // 兼容sqlite3 return conf.Host From c854f0ebeb8435fa31902112c1a0b0f73e1dbcfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Tue, 20 Aug 2024 18:15:59 +0800 Subject: [PATCH 04/26] db client --- wrapper_client.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/wrapper_client.go b/wrapper_client.go index 6698d91..24000f6 100644 --- a/wrapper_client.go +++ b/wrapper_client.go @@ -203,12 +203,3 @@ func (c *wrapperClient) GetSlaveClient(ctx context.Context, dbFlag string) (*gor return dbClient.GetSlave(ctx), nil } - -// getGormClient 获取GORM client方法 -// -// Author : go_developer@163.com<白茶清欢> -// -// Date : 11:24 2022/6/6 -func (c *wrapperClient) getGormClient() (*gorm.DB, error) { - return nil, nil -} From 0098405793c802e8d5351b3bb3c8ff696fe20e93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 16:01:48 +0800 Subject: [PATCH 05/26] =?UTF-8?q?=E5=8D=87=E7=BA=A7=20wrapper=5Fdb=5Fclien?= =?UTF-8?q?t,=20=E6=94=AF=E6=8C=81=E8=A1=A8=E7=BB=93=E6=9E=84=E7=BC=93?= =?UTF-8?q?=E5=AD=98=E4=BB=A5=E5=8F=8A=E5=AE=9A=E6=97=B6=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/wrapper_db_client.go | 6 +- define/table.go | 18 ++++++ system.go | 9 +-- wrapper_db_client.go | 107 +++++++++++++++++++++++++++++++--- 4 files changed, 127 insertions(+), 13 deletions(-) create mode 100644 define/table.go diff --git a/abstract/wrapper_db_client.go b/abstract/wrapper_db_client.go index 118719e..104646f 100644 --- a/abstract/wrapper_db_client.go +++ b/abstract/wrapper_db_client.go @@ -21,11 +21,15 @@ import ( // Date : 18:00 2024/8/20 type IWrapperDatabaseClient interface { // Init 初始化客户端连接 - Init(databaseConfig *define.Database) error + 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.ColumnInfo, error) } diff --git a/define/table.go b/define/table.go new file mode 100644 index 0000000..91edc05 --- /dev/null +++ b/define/table.go @@ -0,0 +1,18 @@ +// 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 +} diff --git a/system.go b/system.go index ae9cecd..670cf71 100644 --- a/system.go +++ b/system.go @@ -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 ) diff --git a/wrapper_db_client.go b/wrapper_db_client.go index 958ffd0..ee0885d 100644 --- a/wrapper_db_client.go +++ b/wrapper_db_client.go @@ -1,4 +1,4 @@ -// Package define ... +// Package database ... // // Description : define ... // @@ -9,6 +9,7 @@ package database import ( "context" + "errors" "fmt" "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/database/define" @@ -18,6 +19,8 @@ import ( "gorm.io/driver/sqlite" "gorm.io/gorm" gormLogger "gorm.io/gorm/logger" + "sync" + "time" ) // DBClient 包装日志实例 @@ -26,12 +29,15 @@ import ( // // 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 // 数据库配置 + 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.ColumnInfo // 表结构缓存 } // Init 初始化客户端 @@ -39,7 +45,9 @@ type DBClient struct { // Author : go_developer@163.com<白茶清欢> // // Date : 17:44 2024/8/20 -func (dc *DBClient) Init(databaseConfig *define.Database) error { +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 @@ -47,6 +55,22 @@ func (dc *DBClient) Init(databaseConfig *define.Database) error { 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 } @@ -134,3 +158,70 @@ func (dc *DBClient) buildConnectionDSN(conf *define.Driver) string { 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.ColumnInfo, error) { + if !dc.CacheDataTableStructureConfig().Enable { + // 未启用缓存, 返回空list + return make([]*define.ColumnInfo, 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 { + 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.ColumnInfo) + for _, itemTableName := range tableList { + fieldList, loadTableErr := systemDao.GetTableInfo(c, dc.Cfg.Database, itemTableName) + if nil != loadTableErr { + if ignoreErr { + continue + } + return loadTableErr + } + tableStructCache[itemTableName] = fieldList + } + // 更新缓存结果 + dc.lock.Lock() + defer dc.lock.Unlock() + dc.tableStructureCache = tableStructCache + return nil +} From 4ee868a0590be124f9b9f39f26c27c7b5908e1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 16:25:59 +0800 Subject: [PATCH 06/26] =?UTF-8?q?=E8=A7=84=E5=88=92Api2SqlParam=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E5=8F=82=E6=95=B0=E7=9A=84=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 define/api2sql.go diff --git a/define/api2sql.go b/define/api2sql.go new file mode 100644 index 0000000..e450c66 --- /dev/null +++ b/define/api2sql.go @@ -0,0 +1,36 @@ +// Package define ... +// +// Description : define ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-08-21 16:05 +package define + +// Api2SqlParam 接口转sql的输入配置 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:06 2024/8/21 +type Api2SqlParam struct { + TableSplit bool `json:"table_split"` // 是否分表 + SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 + SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 + SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 + OrderField string `json:"order_field"` // 排序字段, 仅 sqlType = list 生效 + OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc + WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效 + ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 +} + +// Api2SqlParamValue ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 16:11 2024/8/21 +type Api2SqlParamValue struct { + Field string `json:"field"` // 表字段 + Value any `json:"value"` // 数据字段的值 + Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 + DataMask any `json:"-"` // 数据脱敏策略 +} From 8a0efca6eeaab4d996df8cd9f425dd7ef9abdb20 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 16:30:56 +0800 Subject: [PATCH 07/26] =?UTF-8?q?=E8=A7=84=E5=88=92=E6=94=AF=E6=8C=81=20In?= =?UTF-8?q?putSql?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 1 + 1 file changed, 1 insertion(+) diff --git a/define/api2sql.go b/define/api2sql.go index e450c66..97072db 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -13,6 +13,7 @@ package define // // Date : 16:06 2024/8/21 type Api2SqlParam struct { + InputSql string `json:"input_sql"` // 输入的sql模板, 若指定了sql, 则下面的配置均不生效, 仅依赖 ValueList 解析字段值 TableSplit bool `json:"table_split"` // 是否分表 SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 From 7a87758a4e3b57befad34be843fb42a41ff5a6bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 16:35:46 +0800 Subject: [PATCH 08/26] update consts --- define/api2sql.go | 2 +- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/define/api2sql.go b/define/api2sql.go index 97072db..0982558 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -13,7 +13,7 @@ package define // // Date : 16:06 2024/8/21 type Api2SqlParam struct { - InputSql string `json:"input_sql"` // 输入的sql模板, 若指定了sql, 则下面的配置均不生效, 仅依赖 ValueList 解析字段值 + InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置 TableSplit bool `json:"table_split"` // 是否分表 SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 diff --git a/go.mod b/go.mod index f31d51f..fe6dd54 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.3 require ( - git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 + git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd diff --git a/go.sum b/go.sum index 22fec99..c3608a4 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyz git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= git.zhangdeman.cn/zhangdeman/consts v0.0.0-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-20240821082758-8bf75bab08fb h1:dNnVYynCQhLSbtNUVQsvDIthkNgpTrAJF4dAEj08FdE= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI= git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0= git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= From 128bf9e06290d949269326f52d9dbcf5b88e1099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 18:23:55 +0800 Subject: [PATCH 09/26] =?UTF-8?q?update=20go=20mod=20+=20=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E9=85=8D=E7=BD=AE=E8=A1=A8=E5=AD=97=E6=AE=B5=E5=AF=B9?= =?UTF-8?q?=E5=A4=96=E8=BE=93=E5=87=BA=E7=9A=84=E5=90=8D=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 9 +++++---- go.mod | 2 +- go.sum | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/define/api2sql.go b/define/api2sql.go index 0982558..8601f27 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -30,8 +30,9 @@ type Api2SqlParam struct { // // Date : 16:11 2024/8/21 type Api2SqlParamValue struct { - Field string `json:"field"` // 表字段 - Value any `json:"value"` // 数据字段的值 - Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 - DataMask any `json:"-"` // 数据脱敏策略 + Field string `json:"field"` // 表字段 + Value any `json:"value"` // 数据字段的值 + OutputName string `json:"output_name"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致 + Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 + DataMask any `json:"-"` // 数据脱敏策略 } diff --git a/go.mod b/go.mod index fe6dd54..2b93cdd 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd - git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b + git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 github.com/pkg/errors v0.9.1 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 go.uber.org/zap v1.27.0 diff --git a/go.sum b/go.sum index c3608a4..4b1bd57 100644 --- a/go.sum +++ b/go.sum @@ -30,6 +30,8 @@ git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfm git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t7ssBSZwE7BDTOkCSgOvTGATarOZ0tajZ00McEc= git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 h1:+P+7IDkfOYII4K7BaPiZhnzYgPpJ7mv2hhUSZcxCWiI= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8= 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/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ= From 334472815c6ebf97719b8586a5af90be43bd8232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 21:00:11 +0800 Subject: [PATCH 10/26] =?UTF-8?q?=E8=A7=84=E5=88=92api2sql=E6=89=A7?= =?UTF-8?q?=E8=A1=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 114 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 api2sql/execute.go diff --git a/api2sql/execute.go b/api2sql/execute.go new file mode 100644 index 0000000..38eb558 --- /dev/null +++ b/api2sql/execute.go @@ -0,0 +1,114 @@ +// Package api2sql ... +// +// Description : api2sql ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-08-21 20:45 +package api2sql + +import ( + "context" + "errors" + "git.zhangdeman.cn/zhangdeman/consts" + "git.zhangdeman.cn/zhangdeman/database/define" +) + +var ( + Exec = &execute{} +) + +type execute struct { +} + +// 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 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) { + return nil, 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) { + return nil, nil +} From 3b1e7c72329654c719ef1a7adfad1d8870dc451b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 21:01:39 +0800 Subject: [PATCH 11/26] =?UTF-8?q?=E8=BE=93=E5=85=A5=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=95=B0=E6=8D=AE=E5=BA=93=E6=A0=87=E8=AF=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 1 + 1 file changed, 1 insertion(+) diff --git a/define/api2sql.go b/define/api2sql.go index 8601f27..c8e9d81 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -13,6 +13,7 @@ package define // // Date : 16:06 2024/8/21 type Api2SqlParam struct { + DatabaseFlag string `json:"database_flag"` // 数据库标识 InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置 TableSplit bool `json:"table_split"` // 是否分表 SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 From ce189a9067039788dd54bffbe154b7d348244f08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Wed, 21 Aug 2024 21:11:19 +0800 Subject: [PATCH 12/26] save --- api2sql/execute.go | 11 +++++++++++ define/api2sql.go | 1 + 2 files changed, 12 insertions(+) diff --git a/api2sql/execute.go b/api2sql/execute.go index 38eb558..827554d 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -11,6 +11,7 @@ import ( "context" "errors" "git.zhangdeman.cn/zhangdeman/consts" + "git.zhangdeman.cn/zhangdeman/database/abstract" "git.zhangdeman.cn/zhangdeman/database/define" ) @@ -19,6 +20,16 @@ var ( ) type execute struct { + databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例 +} + +// SetDatabaseClientManager 设置数据库连接管理实例 +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 21:06 2024/8/21 +func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapperClient) { + e.databaseClientManager = databaseClientManager } // Run 执行 diff --git a/define/api2sql.go b/define/api2sql.go index c8e9d81..37f9254 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -19,6 +19,7 @@ type Api2SqlParam struct { SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 + FieldList []string `json:"field_list"` // 仅针对 select / detail 有效, 查询的字段列表 OrderField string `json:"order_field"` // 排序字段, 仅 sqlType = list 生效 OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效 From bc99c08d55064f6c02a5a8cdd37bbd0d8b8b0297 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 22 Aug 2024 11:46:00 +0800 Subject: [PATCH 13/26] =?UTF-8?q?=E5=A2=9E=E5=8A=A0InputParam=E9=AA=8C?= =?UTF-8?q?=E8=AF=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 65 ++++++++++++++++++++++++++++++++++++++++++++++ define/api2sql.go | 49 ++++++++++++++++++++++------------ 2 files changed, 98 insertions(+), 16 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index 827554d..3ee0fc7 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -13,6 +13,7 @@ import ( "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/database/abstract" "git.zhangdeman.cn/zhangdeman/database/define" + "git.zhangdeman.cn/zhangdeman/wrapper" ) var ( @@ -121,5 +122,69 @@ func (e *execute) Delete(ctx context.Context, inputParam *define.Api2SqlParam) ( // // 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 len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{ + consts.SqlTypeList, consts.SqlTypeDetail, + }).Has(inputParam.SqlType) >= 0 { + for _, itemParam := range inputParam.TableColumnConfig { + inputParam.ColumnList = append(inputParam.ColumnList, itemParam.ColumnName) + } + } + // 验证字段是否都正确 + tableColumnTable := make(map[string]bool) + for _, itemColumn := range inputParam.TableColumnConfig { + tableColumnTable[itemColumn.ColumnName] = true + } + for _, item := range inputParam.ColumnList { + if !tableColumnTable[item] { + return errors.New(item + " : input column not found in table column list") + } + } + for _, item := range inputParam.ValueList { + if !tableColumnTable[item.Column] { + return errors.New(item.Column + " : input column in `ValueList` is not found in table column list") + } + } + // 验证 force no limit + if inputParam.ForceNoLimit { + inputParam.Limit = 0 + inputParam.WithCount = false + } + return nil +} diff --git a/define/api2sql.go b/define/api2sql.go index 37f9254..f5a7219 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -12,18 +12,35 @@ package define // 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(*) FROM {TABLE} WHERE {WHERE} type Api2SqlParam struct { - DatabaseFlag string `json:"database_flag"` // 数据库标识 - InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置 - TableSplit bool `json:"table_split"` // 是否分表 - SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 - SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 - SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 - FieldList []string `json:"field_list"` // 仅针对 select / detail 有效, 查询的字段列表 - OrderField string `json:"order_field"` // 排序字段, 仅 sqlType = list 生效 - OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc - WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效 - ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 + DatabaseFlag string `json:"database_flag"` // 数据库标识 + Table string `json:"table"` // 操作的数据表 + InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置 + TableSplit bool `json:"table_split"` // 是否分表 + SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 + SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 + SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 + ColumnList []string `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表 + Limit int64 `json:"limit"` // 操作数据量 + Offset int64 `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 生效 + ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 + TableColumnConfig []*ColumnInfo `json:"table_column_config"` // 表字段配置 } // Api2SqlParamValue ... @@ -32,9 +49,9 @@ type Api2SqlParam struct { // // Date : 16:11 2024/8/21 type Api2SqlParamValue struct { - Field string `json:"field"` // 表字段 - Value any `json:"value"` // 数据字段的值 - OutputName string `json:"output_name"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致 - Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 - DataMask any `json:"-"` // 数据脱敏策略 + Column string `json:"column"` // 表字段 + Value any `json:"value"` // 数据字段的值 + Alias string `json:"alias"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致, 仅查询语句生效 + Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 + DataMask any `json:"-"` // 数据脱敏策略 } From 352fd61f92e70a0919ce2a4bcc3b1c3b0a4101a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 22 Aug 2024 11:51:02 +0800 Subject: [PATCH 14/26] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index 3ee0fc7..3925024 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -159,6 +159,23 @@ func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) e 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 { @@ -181,10 +198,5 @@ func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) e return errors.New(item.Column + " : input column in `ValueList` is not found in table column list") } } - // 验证 force no limit - if inputParam.ForceNoLimit { - inputParam.Limit = 0 - inputParam.WithCount = false - } return nil } From a4d887ecb11b753c04a34e6a042d7141705f226b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 22 Aug 2024 11:53:49 +0800 Subject: [PATCH 15/26] =?UTF-8?q?=E8=BE=93=E5=85=A5=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=BC=BA=E5=88=B6=E8=AF=BB=E4=B8=BB=E9=80=89?= =?UTF-8?q?=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 1 + 1 file changed, 1 insertion(+) diff --git a/define/api2sql.go b/define/api2sql.go index f5a7219..60238c6 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -27,6 +27,7 @@ package define 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 相关解析分表配置 TableSplit bool `json:"table_split"` // 是否分表 SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 From c586fa5c4f4b727aa6ebc49a223aae0cbd7485ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Thu, 22 Aug 2024 12:05:46 +0800 Subject: [PATCH 16/26] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20sql=20=E6=A8=A1?= =?UTF-8?q?=E6=9D=BF=E5=AE=9A=E4=B9=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- define/api2sql.go | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/define/api2sql.go b/define/api2sql.go index 60238c6..1e833ea 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -23,7 +23,7 @@ package define // // delete 语句 : DELETE FROM {TABLE} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE} // -// count 语句 : SELECT COUNT(*) FROM {TABLE} WHERE {WHERE} +// count 语句 : SELECT COUNT(*) as count FROM {TABLE} WHERE {WHERE} type Api2SqlParam struct { DatabaseFlag string `json:"database_flag"` // 数据库标识 Table string `json:"table"` // 操作的数据表 @@ -56,3 +56,15 @@ type Api2SqlParamValue struct { Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 DataMask any `json:"-"` // 数据脱敏策略 } + +const ( + SqlSelectBaseTpl = `SELECT {FIELD_LIST} FROM {TABLE}` // select 语句基础模板 + SqlInsertBaseTpl = `INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST})` // Insert语句基础模板 + SqlUpdateBaseTpl = `UPDATE {TABLE} SET {SET}` // Update语句基础模板 + SqlDeleteBaseTpl = `DELETE FROM {TABLE}` // Delete语句基础模板 + SqlCountBaseTpl = `SELECT COUNT(*) as count FROM {TABLE}` // Count语句基础模板 + SqlWhereTpl = "WHERE {WHERE}" // where 语句 + SqlOrder = "ORDER BY {ORDER_FIELD} {ORDER_RULE}" // 排序语句 + SqlLimit = "LIMIT {LIMIT}" // limit 语句 + SqlOffset = "OFFSET {OFFSET}" // offset 语句 +) From 21ad50b2738ad9aecde81922bf0df02efbf1908a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 10:59:20 +0800 Subject: [PATCH 17/26] update input param --- api2sql/execute.go | 15 +++++++++++++++ define/api2sql.go | 13 +++++++------ 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index 3925024..c47cf47 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -14,6 +14,7 @@ import ( "git.zhangdeman.cn/zhangdeman/database/abstract" "git.zhangdeman.cn/zhangdeman/database/define" "git.zhangdeman.cn/zhangdeman/wrapper" + "strings" ) var ( @@ -39,6 +40,9 @@ func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapp // // 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) @@ -68,6 +72,17 @@ func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any // // Date : 20:52 2024/8/21 func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { + replaceTable := map[string]string{ + "{FIELD_LIST}": "`" + strings.Join(inputParam.ColumnList, "` , `"), + "{TABLE}": inputParam.Table, + } + sqlTplList := []string{ + define.SqlSelectBaseTpl, + } + if len(inputParam.ValueList) > 0 { + // where 条件 + sqlTplList = append(sqlTplList, define.SqlWhereTpl) + } return nil, nil } diff --git a/define/api2sql.go b/define/api2sql.go index 1e833ea..5879c6b 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -33,7 +33,7 @@ type Api2SqlParam struct { SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 - ColumnList []string `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表 + ColumnTable map[string]string `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出 Limit int64 `json:"limit"` // 操作数据量 Offset int64 `json:"offset"` // 操作偏移量 ForceNoLimit bool `json:"force_no_limit"` // 强制允许不限制 : 正常操作 select / delete / update 均需要指定本次操作数据量, 如果确定不限制, 此参数设置为 `true` , sqlType = list , 且 ForceNoLimit = true 时, WithCount 参数无效 @@ -50,11 +50,12 @@ type Api2SqlParam struct { // // Date : 16:11 2024/8/21 type Api2SqlParamValue struct { - Column string `json:"column"` // 表字段 - Value any `json:"value"` // 数据字段的值 - Alias string `json:"alias"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致, 仅查询语句生效 - Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 - DataMask any `json:"-"` // 数据脱敏策略 + Column string `json:"column"` // 表字段 + Value any `json:"value"` // 数据字段的值 + Operate string `json:"operate"` // 操作符 : Equal , NotEqual , In , NotIn , Like, NotLike + Alias string `json:"alias"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致, 仅查询语句生效 + Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 + DataMask any `json:"-"` // 数据脱敏策略 } const ( From e5233b4fae0a29c0c2b66279e26fe6774189a476 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 12:26:03 +0800 Subject: [PATCH 18/26] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=8E=B7=E5=8F=96?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E8=BF=9E=E6=8E=A5=E7=9A=84=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 40 +++++++++++++++++++++++----------------- define/api2sql.go | 3 +++ go.mod | 2 +- go.sum | 2 ++ 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index c47cf47..f76a8bf 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -14,7 +14,7 @@ import ( "git.zhangdeman.cn/zhangdeman/database/abstract" "git.zhangdeman.cn/zhangdeman/database/define" "git.zhangdeman.cn/zhangdeman/wrapper" - "strings" + "gorm.io/gorm" ) var ( @@ -72,17 +72,7 @@ func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any // // Date : 20:52 2024/8/21 func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { - replaceTable := map[string]string{ - "{FIELD_LIST}": "`" + strings.Join(inputParam.ColumnList, "` , `"), - "{TABLE}": inputParam.Table, - } - sqlTplList := []string{ - define.SqlSelectBaseTpl, - } - if len(inputParam.ValueList) > 0 { - // where 条件 - sqlTplList = append(sqlTplList, define.SqlWhereTpl) - } + return nil, nil } @@ -191,11 +181,11 @@ func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) e // // Date : 11:48 2024/8/22 func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { - if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{ + if len(inputParam.ColumnTable) == 0 && wrapper.ArrayType[string]([]string{ consts.SqlTypeList, consts.SqlTypeDetail, }).Has(inputParam.SqlType) >= 0 { for _, itemParam := range inputParam.TableColumnConfig { - inputParam.ColumnList = append(inputParam.ColumnList, itemParam.ColumnName) + inputParam.ColumnTable[itemParam.ColumnName] = itemParam.ColumnName } } // 验证字段是否都正确 @@ -203,9 +193,9 @@ func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { for _, itemColumn := range inputParam.TableColumnConfig { tableColumnTable[itemColumn.ColumnName] = true } - for _, item := range inputParam.ColumnList { - if !tableColumnTable[item] { - return errors.New(item + " : input column not found in table column list") + for columnName, _ := range inputParam.ColumnTable { + if !tableColumnTable[columnName] { + return errors.New(columnName + " : input column not found in table column list") } } for _, item := range inputParam.ValueList { @@ -215,3 +205,19 @@ func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { } 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) +} diff --git a/define/api2sql.go b/define/api2sql.go index 5879c6b..10e2476 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -7,6 +7,8 @@ // Date : 2024-08-21 16:05 package define +import "gorm.io/gorm" + // Api2SqlParam 接口转sql的输入配置 // // Author : go_developer@163.com<白茶清欢> @@ -42,6 +44,7 @@ type Api2SqlParam struct { WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效 ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 TableColumnConfig []*ColumnInfo `json:"table_column_config"` // 表字段配置 + Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 } // Api2SqlParamValue ... diff --git a/go.mod b/go.mod index 2b93cdd..2565c09 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.21 toolchain go1.21.3 require ( - git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb + git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5 git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd diff --git a/go.sum b/go.sum index 4b1bd57..9dfadee 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 h1:U12XDt git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb h1:dNnVYynCQhLSbtNUVQsvDIthkNgpTrAJF4dAEj08FdE= git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5 h1:pmIHln0gWW+5xAB762h3WDsRkZuYLUDndvJDsGMKoOY= +git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240823041145-d4df71cf37e5/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k= git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda h1:bMD6r9gjRy7cO+T4zRQVYAesgIblBdTnhzT1vN5wjvI= git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda/go.mod h1:dT0rmHcJ9Z9IqWeMIt7YzR88nKkNV2V3dfG0j9Q6lK0= git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 h1:I/wOsRpCSRkU9vo1u703slQsmK0wnNeZzsWQOGtIAG0= From 15b1d9e6b0ee447a8000c5359b054fa5a4efacb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 15:22:44 +0800 Subject: [PATCH 19/26] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20WithAnyCondition?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- option.go | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/option.go b/option.go index a253d34..a72046d 100644 --- a/option.go +++ b/option.go @@ -9,7 +9,11 @@ package database import ( "encoding/json" + "errors" + "fmt" + "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/op_type" + "git.zhangdeman.cn/zhangdeman/serialize" "reflect" "strings" ) @@ -508,3 +512,38 @@ func parseInSql(fieldValue any) (string, []any) { } return strings.Join(placeholderList, ","), dataList } + +// 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) (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") + } +} From 1071c7558eb87a5c74ec0e65fa58035bb5f5bf4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 16:17:49 +0800 Subject: [PATCH 20/26] =?UTF-8?q?=E4=BC=98=E5=8C=96=20or=20=E8=AF=AD?= =?UTF-8?q?=E5=8F=A5=20+=20=E6=94=AF=E6=8C=81=20between=20/=20not=20betwee?= =?UTF-8?q?n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- base.go | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++-- option.go | 69 ++++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 122 insertions(+), 16 deletions(-) diff --git a/base.go b/base.go index 0b20801..7fba777 100644 --- a/base.go +++ b/base.go @@ -199,7 +199,72 @@ 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) + } + } + + // between + for field, betweenVal := range o.Between { + tx = tx.Where("`"+field+"` BETWEEN ? AND ?", betweenVal[0], betweenVal[1]) + } + // not between + for field, notBetweenVal := range o.NotBetween { + tx = tx.Where("`"+field+"` NOT BETWEEN ? AND ?", notBetweenVal[0], notBetweenVal[1]) + } + + // 排序 + for _, orderRule := range o.Order { + tx = tx.Order(orderRule) + } + + // or 语句 + if nil != o.OR { + for _, itemOr := range o.OR { + orOption := &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 +277,6 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm } orSql, orBindVal := optionToSql(orOption) tx.Or(orSql, orBindVal...) - } + }*/ return tx } diff --git a/option.go b/option.go index a72046d..42e4a03 100644 --- a/option.go +++ b/option.go @@ -31,20 +31,22 @@ type SetOption func(o *Option) // // 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 关系 + 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 关系 } // WithModel ... @@ -488,6 +490,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 ") } @@ -513,6 +526,34 @@ 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) SetOption { + return func(o *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) SetOption { + return func(o *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<白茶清欢> From 5ed8f2007e8c797c57157a3707a656a9ee06123a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 16:50:47 +0800 Subject: [PATCH 21/26] =?UTF-8?q?=E5=A2=9E=E5=8A=A0list=20=E6=9F=A5?= =?UTF-8?q?=E8=AF=A2=20-=20=E5=BE=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 83 ++++++++++++++++++++++++++++++++++++------- define/api2sql.go | 87 +++++++++++++++++++++++++--------------------- 2 files changed, 118 insertions(+), 52 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index f76a8bf..66bd615 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -10,7 +10,9 @@ 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" @@ -23,6 +25,7 @@ var ( type execute struct { databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例 + baseDao database.BaseDao // 基础dao } // SetDatabaseClientManager 设置数据库连接管理实例 @@ -72,8 +75,43 @@ func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any // // Date : 20:52 2024/8/21 func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { - - return nil, nil + var ( + err error + tx *gorm.DB + optionList []database.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) + switch columnConfig.Type { + case "int", "int8", "int16", "int32", "int64": + st.AddInt(columnConfig.Column, tag, "") + case "uint", "uint8", "uint16", "uint32", "uint64": + st.AddUint(columnConfig.Column, tag, "") + case "bool": + st.AddBool(columnConfig.Column, tag, "") + case "float": + st.AddBool(columnConfig.Column, tag, "") + case "string": + st.AddString(columnConfig.Column, tag, "") + case "map": + st.AddMap(columnConfig.Column, tag, "") + case "slice": + st.AddSlice(columnConfig.Column, tag, "") + } + } + val := st.ToStructDefaultSliceValue() + if err = e.baseDao.List(tx, &val, optionList...); nil != err { + return nil, err + } + return val, nil } // Detail 详情 @@ -181,26 +219,22 @@ func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) e // // Date : 11:48 2024/8/22 func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { - if len(inputParam.ColumnTable) == 0 && wrapper.ArrayType[string]([]string{ + if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{ consts.SqlTypeList, consts.SqlTypeDetail, }).Has(inputParam.SqlType) >= 0 { - for _, itemParam := range inputParam.TableColumnConfig { - inputParam.ColumnTable[itemParam.ColumnName] = itemParam.ColumnName - } + return errors.New("column list is empty") } // 验证字段是否都正确 tableColumnTable := make(map[string]bool) for _, itemColumn := range inputParam.TableColumnConfig { tableColumnTable[itemColumn.ColumnName] = true } - for columnName, _ := range inputParam.ColumnTable { - if !tableColumnTable[columnName] { - return errors.New(columnName + " : input column not found in table column list") + for _, columnConfig := range inputParam.ColumnList { + if !tableColumnTable[columnConfig.Column] { + return errors.New(columnConfig.Column + " : input column not found in table column list") } - } - for _, item := range inputParam.ValueList { - if !tableColumnTable[item.Column] { - return errors.New(item.Column + " : input column in `ValueList` is not found in table column list") + if len(columnConfig.Alias) == 0 { + columnConfig.Alias = columnConfig.Column } } return nil @@ -221,3 +255,26 @@ func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (* } 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) ([]database.SetOption, error) { + optionList := []database.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 +} diff --git a/define/api2sql.go b/define/api2sql.go index 10e2476..0e20c28 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -7,7 +7,9 @@ // Date : 2024-08-21 16:05 package define -import "gorm.io/gorm" +import ( + "gorm.io/gorm" +) // Api2SqlParam 接口转sql的输入配置 // @@ -27,48 +29,55 @@ import "gorm.io/gorm" // // 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 相关解析分表配置 - TableSplit bool `json:"table_split"` // 是否分表 - SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 - SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 - SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 - ColumnTable map[string]string `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出 - Limit int64 `json:"limit"` // 操作数据量 - Offset int64 `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 生效 - ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 - TableColumnConfig []*ColumnInfo `json:"table_column_config"` // 表字段配置 - Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 + 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 []*ColumnInfo `json:"table_column_config"` // 表字段配置 + Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 } -// Api2SqlParamValue ... +// TableSplitConfig 分表配置 // // Author : go_developer@163.com<白茶清欢> // -// Date : 16:11 2024/8/21 -type Api2SqlParamValue struct { - Column string `json:"column"` // 表字段 - Value any `json:"value"` // 数据字段的值 - Operate string `json:"operate"` // 操作符 : Equal , NotEqual , In , NotIn , Like, NotLike - Alias string `json:"alias"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致, 仅查询语句生效 - Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 - DataMask any `json:"-"` // 数据脱敏策略 +// 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"` // 一共分表多少张 } -const ( - SqlSelectBaseTpl = `SELECT {FIELD_LIST} FROM {TABLE}` // select 语句基础模板 - SqlInsertBaseTpl = `INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST})` // Insert语句基础模板 - SqlUpdateBaseTpl = `UPDATE {TABLE} SET {SET}` // Update语句基础模板 - SqlDeleteBaseTpl = `DELETE FROM {TABLE}` // Delete语句基础模板 - SqlCountBaseTpl = `SELECT COUNT(*) as count FROM {TABLE}` // Count语句基础模板 - SqlWhereTpl = "WHERE {WHERE}" // where 语句 - SqlOrder = "ORDER BY {ORDER_FIELD} {ORDER_RULE}" // 排序语句 - SqlLimit = "LIMIT {LIMIT}" // limit 语句 - SqlOffset = "OFFSET {OFFSET}" // offset 语句 -) +// 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"` // 数据值 +} + +// 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"` // 字段类型 +} From f359598109d8119b477a9f44eb4e0bbec7596c0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 17:29:11 +0800 Subject: [PATCH 22/26] =?UTF-8?q?=E6=94=AF=E6=8C=81=E4=BA=BA=E5=B7=A5?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=A1=A8=E7=BB=93=E6=9E=84=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/wrapper_db_client.go | 4 ++- define/api2sql.go | 11 -------- define/table.go | 11 ++++++++ wrapper_db_client.go | 47 +++++++++++++++++++++++++++++++---- 4 files changed, 56 insertions(+), 17 deletions(-) diff --git a/abstract/wrapper_db_client.go b/abstract/wrapper_db_client.go index 104646f..4ab2bb6 100644 --- a/abstract/wrapper_db_client.go +++ b/abstract/wrapper_db_client.go @@ -31,5 +31,7 @@ type IWrapperDatabaseClient interface { // CacheDataTableStructureConfig 缓存数据表结构的配置 CacheDataTableStructureConfig() *define.CacheTableStructureConfig // GetTableFieldList 获取指定表数据字段列表 - GetTableFieldList(tableName string) ([]*define.ColumnInfo, error) + GetTableFieldList(tableName string) ([]*define.ColumnConfig, error) + // SetTableStructure 设置数据表结构 + SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig) } diff --git a/define/api2sql.go b/define/api2sql.go index 0e20c28..989388f 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -70,14 +70,3 @@ type SqlCondition struct { Operate string `json:"operate"` // 操作 : == / !== / in / not in / like / not like Value any `json:"value"` // 数据值 } - -// 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"` // 字段类型 -} diff --git a/define/table.go b/define/table.go index 91edc05..32138b9 100644 --- a/define/table.go +++ b/define/table.go @@ -16,3 +16,14 @@ 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"` // 字段类型 +} diff --git a/wrapper_db_client.go b/wrapper_db_client.go index ee0885d..5bc1c8c 100644 --- a/wrapper_db_client.go +++ b/wrapper_db_client.go @@ -19,6 +19,7 @@ import ( "gorm.io/driver/sqlite" "gorm.io/gorm" gormLogger "gorm.io/gorm/logger" + "strings" "sync" "time" ) @@ -37,7 +38,7 @@ type DBClient struct { Cfg define.Driver // 数据库配置 cacheTableStructureConfig *define.CacheTableStructureConfig // 缓存配置 lock *sync.RWMutex // 操作锁 - tableStructureCache map[string][]*define.ColumnInfo // 表结构缓存 + tableStructureCache map[string][]*define.ColumnConfig // 表结构缓存 } // Init 初始化客户端 @@ -179,10 +180,10 @@ func (dc *DBClient) CacheDataTableStructureConfig() *define.CacheTableStructureC // Author : go_developer@163.com<白茶清欢> // // Date : 15:07 2024/8/21 -func (dc *DBClient) GetTableFieldList(tableName string) ([]*define.ColumnInfo, error) { +func (dc *DBClient) GetTableFieldList(tableName string) ([]*define.ColumnConfig, error) { if !dc.CacheDataTableStructureConfig().Enable { // 未启用缓存, 返回空list - return make([]*define.ColumnInfo, 0), nil + return make([]*define.ColumnConfig, 0), nil } dc.lock.RLock() defer dc.lock.RUnlock() @@ -198,6 +199,10 @@ func (dc *DBClient) GetTableFieldList(tableName string) ([]*define.ColumnInfo, e // // Date : 15:17 2024/8/21 func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error { + if !dc.CacheDataTableStructureConfig().Enable { + // 自动同步不可用 + return nil + } var ( err error tableList []string @@ -208,7 +213,7 @@ func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error { if tableList, err = systemDao.GetTableList(c); nil != err { return err } - tableStructCache := make(map[string][]*define.ColumnInfo) + tableStructCache := make(map[string][]*define.ColumnConfig) for _, itemTableName := range tableList { fieldList, loadTableErr := systemDao.GetTableInfo(c, dc.Cfg.Database, itemTableName) if nil != loadTableErr { @@ -217,7 +222,22 @@ func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error { } return loadTableErr } - tableStructCache[itemTableName] = fieldList + 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() @@ -225,3 +245,20 @@ func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error { 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() + for table, columnConfig := range tableConfigTable { + dc.tableStructureCache[table] = columnConfig + } + dc.lock.Unlock() +} From c6b8d29b610cbd22dda8ed909fb079b74fb3db9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 17:54:48 +0800 Subject: [PATCH 23/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E4=BB=A3=E7=A0=81BUG?= =?UTF-8?q?=20+=20=E5=BE=AA=E7=8E=AF=E5=BC=95=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abstract/database.go | 16 ++--- api2sql/execute.go | 8 +-- api2sql/execute_test.go | 31 +++++++++ base.go | 23 +++---- define/api2sql.go | 2 +- define/sql_option.go | 39 +++++++++++ option.go | 142 ++++++++++++++++------------------------ option_test.go | 6 +- wrapper_client.go | 2 +- 9 files changed, 154 insertions(+), 115 deletions(-) create mode 100644 api2sql/execute_test.go create mode 100644 define/sql_option.go diff --git a/abstract/database.go b/abstract/database.go index 7b0d052..51b546b 100644 --- a/abstract/database.go +++ b/abstract/database.go @@ -8,7 +8,7 @@ package abstract import ( - "git.zhangdeman.cn/zhangdeman/database" + "git.zhangdeman.cn/zhangdeman/database/define" "gorm.io/gorm" ) @@ -19,21 +19,21 @@ 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 // 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 开启事务 diff --git a/api2sql/execute.go b/api2sql/execute.go index 66bd615..98a7fd0 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -78,7 +78,7 @@ func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (an var ( err error tx *gorm.DB - optionList []database.SetOption + optionList []define.SetOption ) if tx, err = e.getTx(ctx, inputParam); nil != err { return nil, err @@ -227,7 +227,7 @@ func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { // 验证字段是否都正确 tableColumnTable := make(map[string]bool) for _, itemColumn := range inputParam.TableColumnConfig { - tableColumnTable[itemColumn.ColumnName] = true + tableColumnTable[itemColumn.Column] = true } for _, columnConfig := range inputParam.ColumnList { if !tableColumnTable[columnConfig.Column] { @@ -261,8 +261,8 @@ func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (* // Author : go_developer@163.com<白茶清欢> // // Date : 12:31 2024/8/23 -func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]database.SetOption, error) { - optionList := []database.SetOption{ +func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]define.SetOption, error) { + optionList := []define.SetOption{ database.WithTable(inputParam.Table), } // 设置 limit offset diff --git a/api2sql/execute_test.go b/api2sql/execute_test.go new file mode 100644 index 0000000..2172c22 --- /dev/null +++ b/api2sql/execute_test.go @@ -0,0 +1,31 @@ +// Package api2sql ... +// +// Description : api2sql ... +// +// Author : go_developer@163.com<白茶清欢> +// +// Date : 2024-08-23 17:36 +package api2sql + +import ( + "git.zhangdeman.cn/zhangdeman/database" + "git.zhangdeman.cn/zhangdeman/database/define" + "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()) + } + Exec.SetDatabaseClientManager(clientManager) +} diff --git a/base.go b/base.go index 7fba777..b6ae157 100644 --- a/base.go +++ b/base.go @@ -7,6 +7,7 @@ package database import ( "errors" + "git.zhangdeman.cn/zhangdeman/database/define" "gorm.io/gorm" ) @@ -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,7 +64,7 @@ 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 } @@ -73,7 +74,7 @@ func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...SetOpt // 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 +85,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 +119,7 @@ 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) { dbInstance = b.setTxCondition(dbInstance, optionFuncList...) var cnt int64 return cnt, dbInstance.Count(&cnt).Error @@ -173,10 +174,10 @@ 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(tx *gorm.DB, optionFuncList ...define.SetOption) *gorm.DB { // 构建查询条件 - o := &Option{} + o := &define.Option{} for _, fn := range optionFuncList { fn(o) } @@ -256,7 +257,7 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm // or 语句 if nil != o.OR { for _, itemOr := range o.OR { - orOption := &Option{} + orOption := &define.Option{} for _, fn := range itemOr { fn(orOption) } diff --git a/define/api2sql.go b/define/api2sql.go index 989388f..b96c7a8 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -43,7 +43,7 @@ type Api2SqlParam struct { OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效 ConditionList []SqlCondition `json:"value_list"` // 字段列表 - TableColumnConfig []*ColumnInfo `json:"table_column_config"` // 表字段配置 + TableColumnConfig []*ColumnConfig `json:"table_column_config"` // 表字段配置 Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 } diff --git a/define/sql_option.go b/define/sql_option.go new file mode 100644 index 0000000..7518d2f --- /dev/null +++ b/define/sql_option.go @@ -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 关系 +} diff --git a/option.go b/option.go index 42e4a03..3da670a 100644 --- a/option.go +++ b/option.go @@ -12,50 +12,20 @@ import ( "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 条件 - 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 关系 -} - // 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 } } @@ -65,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 } } @@ -76,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) } @@ -92,8 +62,8 @@ 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) } @@ -104,8 +74,8 @@ func WithLimit[T op_type.Int](limit T, offset T) SetOption { // 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) } @@ -121,8 +91,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 } @@ -141,8 +111,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) } @@ -157,8 +127,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) } @@ -174,8 +144,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) } @@ -193,8 +163,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) } @@ -207,8 +177,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) } @@ -223,8 +193,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) } @@ -237,8 +207,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) } @@ -253,8 +223,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) } @@ -270,8 +240,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) } @@ -289,8 +259,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) } @@ -306,8 +276,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) } @@ -325,8 +295,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) } @@ -339,8 +309,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) } @@ -355,10 +325,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) } @@ -369,8 +339,8 @@ 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) { o.Order = orderRuleList } } @@ -380,8 +350,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) { +func WithOrderDesc(field string) define.SetOption { + return func(o *define.Option) { if nil == o.Order { o.Order = make([]string, 0) } @@ -394,8 +364,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) { +func WithOrderAsc(field string) define.SetOption { + return func(o *define.Option) { if nil == o.Order { o.Order = make([]string, 0) } @@ -408,8 +378,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) } @@ -421,7 +391,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条件 @@ -531,8 +501,8 @@ func parseInSql(fieldValue any) (string, []any) { // Author : go_developer@163.com<白茶清欢> // // Date : 15:52 2024/8/23 -func WithBetween(field string, left any, right any) SetOption { - return func(o *Option) { +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{} } @@ -545,8 +515,8 @@ func WithBetween(field string, left any, right any) SetOption { // Author : go_developer@163.com<白茶清欢> // // Date : 15:52 2024/8/23 -func WithNotBetween(field string, left any, right any) SetOption { - return func(o *Option) { +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{} } @@ -559,7 +529,7 @@ func WithNotBetween(field string, left any, right any) SetOption { // Author : go_developer@163.com<白茶清欢> // // Date : 14:48 2024/8/23 -func WithAnyCondition(column string, operate string, value any) (SetOption, error) { +func WithAnyCondition(column string, operate string, value any) (define.SetOption, error) { if nil == value { return nil, errors.New("value is nil") } diff --git a/option_test.go b/option_test.go index db8b325..db067a7 100644 --- a/option_test.go +++ b/option_test.go @@ -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{ diff --git a/wrapper_client.go b/wrapper_client.go index 24000f6..a52ef3f 100644 --- a/wrapper_client.go +++ b/wrapper_client.go @@ -80,7 +80,7 @@ func (c *wrapperClient) AddWithConfig(flag string, logInstance *zap.Logger, data Cfg: define.Driver{}, } - if err := dbClient.Init(databaseConfig); nil != err { + if err := dbClient.Init(databaseConfig, nil); nil != err { return err } c.lock.Lock() From 2be1b292348100ba57c4e1aea315c09c39dd6599 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 18:17:31 +0800 Subject: [PATCH 24/26] fix NPE --- api2sql/execute_test.go | 33 +++++++++++++++++++++++++++++++++ wrapper_client.go | 4 ++-- wrapper_db_client.go | 3 +++ 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/api2sql/execute_test.go b/api2sql/execute_test.go index 2172c22..effbcbd 100644 --- a/api2sql/execute_test.go +++ b/api2sql/execute_test.go @@ -8,6 +8,9 @@ package api2sql import ( + "context" + "fmt" + "git.zhangdeman.cn/zhangdeman/consts" "git.zhangdeman.cn/zhangdeman/database" "git.zhangdeman.cn/zhangdeman/database/define" "testing" @@ -27,5 +30,35 @@ func Test_execute_Run(t *testing.T) { }, []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: nil, + Limit: 0, + Offset: 0, + ForceNoLimit: false, + OrderField: "id", + OrderRule: "desc", + WithCount: false, + ConditionList: nil, + TableColumnConfig: nil, + Tx: nil, + }) + fmt.Println(res, err) } diff --git a/wrapper_client.go b/wrapper_client.go index a52ef3f..113ae6e 100644 --- a/wrapper_client.go +++ b/wrapper_client.go @@ -15,12 +15,11 @@ import ( "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" - "go.uber.org/zap" - "gorm.io/gorm" ) @@ -184,6 +183,7 @@ func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*go if dbClient, err = c.GetDBClient(dbFlag); nil != err { return nil, err } + return dbClient.GetMaster(ctx), nil } diff --git a/wrapper_db_client.go b/wrapper_db_client.go index 5bc1c8c..6ea26d6 100644 --- a/wrapper_db_client.go +++ b/wrapper_db_client.go @@ -257,6 +257,9 @@ func (dc *DBClient) SetTableStructure(tableConfigTable map[string][]*define.Colu 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 } From d2e96ecfe6f0bb1abd009b2558fd5ada125d6f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 18:23:37 +0800 Subject: [PATCH 25/26] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E4=BD=93=E7=94=9F=E6=88=90=E7=9A=84BUG?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 15 ++++++++------- api2sql/execute_test.go | 20 +++++++++++++------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index 98a7fd0..50dedf6 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -90,21 +90,22 @@ func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (an 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(columnConfig.Column, tag, "") + st.AddInt(column, tag, "") case "uint", "uint8", "uint16", "uint32", "uint64": - st.AddUint(columnConfig.Column, tag, "") + st.AddUint(column, tag, "") case "bool": - st.AddBool(columnConfig.Column, tag, "") + st.AddBool(column, tag, "") case "float": - st.AddBool(columnConfig.Column, tag, "") + st.AddBool(column, tag, "") case "string": - st.AddString(columnConfig.Column, tag, "") + st.AddString(column, tag, "") case "map": - st.AddMap(columnConfig.Column, tag, "") + st.AddMap(column, tag, "") case "slice": - st.AddSlice(columnConfig.Column, tag, "") + st.AddSlice(column, tag, "") } } val := st.ToStructDefaultSliceValue() diff --git a/api2sql/execute_test.go b/api2sql/execute_test.go index effbcbd..767857c 100644 --- a/api2sql/execute_test.go +++ b/api2sql/execute_test.go @@ -43,13 +43,19 @@ func Test_execute_Run(t *testing.T) { }) 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: nil, + 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, From 6c8fd23f1a270ce8cca785b87bc0d10d1f20e0a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E8=8C=B6=E6=B8=85=E6=AC=A2?= Date: Fri, 23 Aug 2024 18:47:04 +0800 Subject: [PATCH 26/26] =?UTF-8?q?=E8=B0=83=E9=80=9A=20list=20=E8=83=BD?= =?UTF-8?q?=E5=8A=9B,=20=E5=88=AB=E5=90=8D=E9=97=AE=E9=A2=98=E5=BE=85?= =?UTF-8?q?=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api2sql/execute.go | 2 +- api2sql/execute_test.go | 6 +++++- go.mod | 2 +- go.sum | 2 ++ 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/api2sql/execute.go b/api2sql/execute.go index 50dedf6..4dbb4b2 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -89,7 +89,7 @@ func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (an // 动态生成结果解析结构体 st := wrapper.NewDynamic() for _, columnConfig := range inputParam.ColumnList { - tag := fmt.Sprintf(`gorm:%v json:%v`, columnConfig.Column, columnConfig.Alias) + 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": diff --git a/api2sql/execute_test.go b/api2sql/execute_test.go index 767857c..ceb4192 100644 --- a/api2sql/execute_test.go +++ b/api2sql/execute_test.go @@ -9,10 +9,12 @@ 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" ) @@ -66,5 +68,7 @@ func Test_execute_Run(t *testing.T) { TableColumnConfig: nil, Tx: nil, }) - fmt.Println(res, err) + byteData, _ := json.Marshal(res) + tt := reflect.TypeOf(res) + fmt.Println(tt.String(), res, err, string(byteData)) } diff --git a/go.mod b/go.mod index 2565c09..4d06dc9 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ require ( git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a git.zhangdeman.cn/zhangdeman/op_type v0.0.0-20240122104027-4928421213c0 git.zhangdeman.cn/zhangdeman/serialize v0.0.0-20240618035451-8d48a6bd39dd - git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 + git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3 github.com/pkg/errors v0.9.1 github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 go.uber.org/zap v1.27.0 diff --git a/go.sum b/go.sum index 9dfadee..35d64db 100644 --- a/go.sum +++ b/go.sum @@ -34,6 +34,8 @@ git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo= git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 h1:+P+7IDkfOYII4K7BaPiZhnzYgPpJ7mv2hhUSZcxCWiI= git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3 h1:RcWNxrHmhZksZWrP/HLEwAM8uIIHYlPLQ20HnLzC+j0= +git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240823103024-c38d16dc28d3/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 h1:OYA+5W64v3OgClL+IrOD63t4i/RW7RqrAVl9LTZ9UqQ=