Compare commits
43 Commits
ca811dbdf4
...
feature/ap
Author | SHA1 | Date | |
---|---|---|---|
a7480bac13 | |||
375aed7160 | |||
38f3f763bf | |||
16e6d57d59 | |||
9de616194b | |||
b89b9b6958 | |||
6f9a560e59 | |||
c9d6c4c845 | |||
aebb943603 | |||
6c8fd23f1a | |||
d2e96ecfe6 | |||
2be1b29234 | |||
c6b8d29b61 | |||
f359598109 | |||
5ed8f2007e | |||
1071c7558e | |||
15b1d9e6b0 | |||
e5233b4fae | |||
21ad50b273 | |||
c586fa5c4f | |||
a4d887ecb1 | |||
352fd61f92 | |||
bc99c08d55 | |||
ce189a9067 | |||
3b1e7c7232 | |||
334472815c | |||
128bf9e062 | |||
7a87758a4e | |||
8a0efca6ee | |||
4ee868a059 | |||
0098405793 | |||
c854f0ebeb | |||
4ba14dc23d | |||
5e87706d0a | |||
5024ae4238 | |||
b10a55f62d | |||
61d8a086a5 | |||
dbd51ff475 | |||
0b0e6a8da5 | |||
fa5def962c | |||
7126765fd5 | |||
88f9e831d8 | |||
ce2f962784 |
24
abstract/api_sql_manager.go
Normal file
24
abstract/api_sql_manager.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-20 17:17
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// IManager 接口 => sql 数据管理实例约束
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:17 2024/8/20
|
||||
type IManager interface {
|
||||
// SetDatabaseClient 设置数据库连接的客户端
|
||||
SetDatabaseClient(database IWrapperClient)
|
||||
// Execute 执行
|
||||
Execute(ctx context.Context, databaseFlag string) (any, error)
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2023-10-14 15:06
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/database"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Base 定义base约束
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:06 2023/10/14
|
||||
type Base interface {
|
||||
// Create 创建数据
|
||||
Create(dbInstance *gorm.DB, data interface{}, optionList ...database.SetOption) error
|
||||
// Update 更新数据
|
||||
Update(dbInstance *gorm.DB, updateData interface{}, optionFuncList ...database.SetOption) (int64, error)
|
||||
// UpdateOne 更新一条数据
|
||||
UpdateOne(dbInstance *gorm.DB, updateData interface{}, optionFuncList ...database.SetOption) (int64, error)
|
||||
// List 查询数据列表
|
||||
List(dbInstance *gorm.DB, result interface{}, optionFuncList ...database.SetOption) error
|
||||
// Delete 删除数据
|
||||
Delete(dbInstance *gorm.DB, dataModel interface{}, optionFuncList ...database.SetOption) (int64, error)
|
||||
// Detail 数据详情
|
||||
Detail(dbInstance *gorm.DB, result interface{}, optionFuncList ...database.SetOption) error
|
||||
// IsNotFound 错误是否为数据不存在
|
||||
IsNotFound(err error) bool
|
||||
// Count 查询数据数量
|
||||
Count(dbInstance *gorm.DB, optionFuncList ...database.SetOption) (int64, error)
|
||||
// Tx 执行事务
|
||||
Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error
|
||||
// Begin 开启事务
|
||||
Begin(dbInstance *gorm.DB) *gorm.DB
|
||||
// Commit 提交事务
|
||||
Commit(db *gorm.DB) error
|
||||
// Rollback 回滚事务
|
||||
Rollback(db *gorm.DB) error
|
||||
}
|
47
abstract/database.go
Normal file
47
abstract/database.go
Normal file
@ -0,0 +1,47 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2023-10-14 15:06
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// IDatabase 定义base约束
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:06 2023/10/14
|
||||
type IDatabase interface {
|
||||
// Create 创建数据
|
||||
Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error
|
||||
// Update 更新数据
|
||||
Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error)
|
||||
// UpdateOne 更新一条数据
|
||||
UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error)
|
||||
// List 查询数据列表
|
||||
List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error
|
||||
// Delete 删除数据
|
||||
Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...define.SetOption) (int64, error)
|
||||
// Detail 数据详情
|
||||
Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error
|
||||
// IsNotFound 错误是否为数据不存在
|
||||
IsNotFound(err error) bool
|
||||
// Count 查询数据数量
|
||||
Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error)
|
||||
// Tx 执行事务
|
||||
Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error
|
||||
// Begin 开启事务
|
||||
Begin(dbInstance *gorm.DB) *gorm.DB
|
||||
// Commit 提交事务
|
||||
Commit(db *gorm.DB) error
|
||||
// Rollback 回滚事务
|
||||
Rollback(db *gorm.DB) error
|
||||
// DetailByPrimaryID 根据主键ID, 查询任意表的数据详情
|
||||
DetailByPrimaryID(dbInstance *gorm.DB, result any, primaryID any, primaryKey ...string) error
|
||||
}
|
35
abstract/wrapper_client.go
Normal file
35
abstract/wrapper_client.go
Normal file
@ -0,0 +1,35 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-20 17:26
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// IWrapperClient 包装的客户端, 包装 IWrapperDatabaseClient
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:26 2024/8/20
|
||||
type IWrapperClient interface {
|
||||
// AddWithConfigFile 通过配置文件增加数据库实例
|
||||
AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error
|
||||
// AddWithConfig 通过具体的配置增加数据库实例
|
||||
AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error
|
||||
// BatchAddWithConfigDir 通过具体的配置列表增加数据库实例
|
||||
BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error
|
||||
// GetDBClient 基于数据库标识获取数据库实例
|
||||
GetDBClient(dbFlag string) (IWrapperDatabaseClient, error)
|
||||
// GetMasterClient 获取主库连接
|
||||
GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error)
|
||||
// GetSlaveClient 获取从库连接
|
||||
GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error)
|
||||
}
|
37
abstract/wrapper_db_client.go
Normal file
37
abstract/wrapper_db_client.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Package abstract ...
|
||||
//
|
||||
// Description : abstract ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-20 17:59
|
||||
package abstract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// IWrapperDatabaseClient 包装gorm
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:00 2024/8/20
|
||||
type IWrapperDatabaseClient interface {
|
||||
// Init 初始化客户端连接
|
||||
Init(databaseConfig *define.Database, cacheTableStructureConfig *define.CacheTableStructureConfig) error
|
||||
// GetMaster 获取master连接
|
||||
GetMaster(ctx context.Context) *gorm.DB
|
||||
// GetSlave 获取slave连接
|
||||
GetSlave(ctx context.Context) *gorm.DB
|
||||
// GetDatabaseClient 获取数据库连接
|
||||
GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error)
|
||||
// CacheDataTableStructureConfig 缓存数据表结构的配置
|
||||
CacheDataTableStructureConfig() *define.CacheTableStructureConfig
|
||||
// GetTableFieldList 获取指定表数据字段列表
|
||||
GetTableFieldList(tableName string) ([]*define.ColumnConfig, error)
|
||||
// SetTableStructure 设置数据表结构
|
||||
SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig)
|
||||
}
|
345
api2sql/execute.go
Normal file
345
api2sql/execute.go
Normal file
@ -0,0 +1,345 @@
|
||||
// Package api2sql ...
|
||||
//
|
||||
// Description : api2sql ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-21 20:45
|
||||
package api2sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/database"
|
||||
"git.zhangdeman.cn/zhangdeman/database/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"git.zhangdeman.cn/zhangdeman/wrapper"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
Exec = &execute{}
|
||||
)
|
||||
|
||||
type execute struct {
|
||||
databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例
|
||||
baseDao database.BaseDao // 基础dao
|
||||
}
|
||||
|
||||
// SetDatabaseClientManager 设置数据库连接管理实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 21:06 2024/8/21
|
||||
func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapperClient) {
|
||||
e.databaseClientManager = databaseClientManager
|
||||
}
|
||||
|
||||
// Run 执行
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:48 2024/8/21
|
||||
func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
if err := e.formatAndValidateInputParam(inputParam); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if len(inputParam.InputSql) > 0 {
|
||||
// 基于表达式执行
|
||||
return e.Express(ctx, inputParam)
|
||||
}
|
||||
switch inputParam.SqlType {
|
||||
case consts.SqlTypeCount:
|
||||
return e.Count(ctx, inputParam)
|
||||
case consts.SqlTypeDetail:
|
||||
return e.Detail(ctx, inputParam)
|
||||
case consts.SqlTypeList:
|
||||
return e.List(ctx, inputParam)
|
||||
case consts.SqlTypeInsert:
|
||||
return e.Insert(ctx, inputParam)
|
||||
case consts.SqlTypeUpdate:
|
||||
return e.Update(ctx, inputParam)
|
||||
case consts.SqlTypeDelete:
|
||||
return e.Delete(ctx, inputParam)
|
||||
default:
|
||||
return nil, errors.New(inputParam.SqlType + " : sql type is not support")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// List 方法用于从数据库中查询数据列表。
|
||||
//
|
||||
// 参数:
|
||||
// - ctx: 上下文对象,用于传递请求的上下文信息。
|
||||
// - inputParam: 查询参数,包含 SQL 语句、列列表等信息。
|
||||
//
|
||||
// 返回值:
|
||||
// - any: 查询结果,返回一个结构体切片,其中包含查询到的数据。
|
||||
// - error: 错误信息,如果查询过程中发生错误,将返回相应的错误对象。
|
||||
//
|
||||
// 使用示例:
|
||||
// result, err := e.List(ctx, inputParam)
|
||||
// if err!= nil {
|
||||
// // 处理错误
|
||||
// }
|
||||
// // 处理查询结果
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:52 2024/8/21
|
||||
func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) ([]map[string]any, error) {
|
||||
var (
|
||||
err error
|
||||
tx *gorm.DB
|
||||
optionList []define.SetOption
|
||||
val []map[string]any
|
||||
)
|
||||
if tx, err = e.getTx(ctx, inputParam); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if optionList, err = e.getOptionList(ctx, inputParam); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if err = e.baseDao.List(tx, &val, optionList...); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// Detail 方法用于获取单个数据的详细信息。
|
||||
// 它接受一个上下文对象 ctx 和一个指向输入参数结构体 Api2SqlParam 的指针 inputParam。
|
||||
// 首先,它会将 inputParam 的 Limit 属性设置为 1,以限制查询结果为一条记录。
|
||||
// 然后,它会调用 e.List 方法来执行查询操作,并将结果存储在 res 变量中。
|
||||
// 如果查询过程中发生错误,它会将错误返回。
|
||||
// 如果查询成功但结果集为空,它会返回一个错误,表示未找到记录。
|
||||
// 如果查询成功且结果集不为空,它会返回结果集中的第一条记录。
|
||||
//
|
||||
// 参数 :
|
||||
// - ctx : 上下文对象,用于传递请求的上下文信息。
|
||||
// - inputParam : 查询参数,包含了查询所需的条件和限制。
|
||||
//
|
||||
// 返回值 :
|
||||
// - 返回查询到的单个数据的详细信息
|
||||
// - 如果未找到数据则返回错误
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:52 2024/8/21
|
||||
func (e *execute) Detail(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
inputParam.Limit = 1 // 限制查询一条
|
||||
var (
|
||||
list []map[string]any
|
||||
err error
|
||||
)
|
||||
if list, err = e.List(ctx, inputParam); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
if len(list) == 0 {
|
||||
return nil, gorm.ErrRecordNotFound
|
||||
}
|
||||
return list[0], 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) {
|
||||
var (
|
||||
err error
|
||||
tx *gorm.DB
|
||||
)
|
||||
|
||||
if tx, err = e.getTx(ctx, inputParam); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
// 动态生成结果解析结构体
|
||||
insertVal := map[string]any{}
|
||||
for _, itemColumn := range inputParam.ConditionList {
|
||||
insertVal[itemColumn.Column] = itemColumn.Value
|
||||
}
|
||||
if err = e.baseDao.Create(tx, &insertVal, database.WithTable(inputParam.Table)); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return insertVal, nil
|
||||
}
|
||||
|
||||
// Update 更新
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:52 2024/8/21
|
||||
func (e *execute) Update(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Count 数量
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:52 2024/8/21
|
||||
func (e *execute) Count(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Delete 删除
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:51 2024/8/21
|
||||
func (e *execute) Delete(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Express 基于表达式执行
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:51 2024/8/21
|
||||
func (e *execute) Express(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
||||
if nil == e.databaseClientManager {
|
||||
return nil, errors.New("database client is nil, please use `SetDatabaseClientManager` set instance")
|
||||
}
|
||||
// 格式化 inputParam
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// formatAndValidateInputParam 格式化并校验输入参数
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 10:38 2024/8/22
|
||||
func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) error {
|
||||
if nil == inputParam {
|
||||
return errors.New("inputParam is nil")
|
||||
}
|
||||
if len(inputParam.DatabaseFlag) == 0 {
|
||||
return errors.New("databaseFlag is empty")
|
||||
}
|
||||
if len(inputParam.Table) == 0 {
|
||||
return errors.New("table is empty")
|
||||
}
|
||||
if len(inputParam.SqlType) == 0 {
|
||||
return errors.New("sqlType is empty")
|
||||
}
|
||||
databaseClient, err := e.databaseClientManager.GetDBClient(inputParam.DatabaseFlag)
|
||||
if nil != err {
|
||||
return errors.New(inputParam.DatabaseFlag + " : database client get fail -> " + err.Error())
|
||||
}
|
||||
// 尝试获取表结构, api 转 sql 要求必须开启表结构缓存, 否则无法确定相关数据如何解析
|
||||
if inputParam.TableColumnConfig, err = databaseClient.GetTableFieldList(inputParam.Table); nil != err {
|
||||
return errors.New(inputParam.DatabaseFlag + " : get table field list fail -> " + err.Error())
|
||||
}
|
||||
if len(inputParam.TableColumnConfig) == 0 {
|
||||
return errors.New(inputParam.DatabaseFlag + " : table field list is empty, please enable `CacheDataTableStructureConfig` or `SetTableColumnConfig`")
|
||||
}
|
||||
// 操作字段列表为空
|
||||
if err = e.validateColumn(inputParam); nil != err {
|
||||
return err
|
||||
}
|
||||
// 验证 force no limit
|
||||
if inputParam.ForceNoLimit {
|
||||
inputParam.Limit = 0
|
||||
inputParam.WithCount = false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateColumn 验证表字段相关
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:48 2024/8/22
|
||||
func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error {
|
||||
if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{
|
||||
consts.SqlTypeList, consts.SqlTypeDetail,
|
||||
}).Has(inputParam.SqlType) >= 0 {
|
||||
return errors.New("column list is empty")
|
||||
}
|
||||
// 验证字段是否都正确
|
||||
tableColumnTable := make(map[string]bool)
|
||||
for _, itemColumn := range inputParam.TableColumnConfig {
|
||||
tableColumnTable[itemColumn.Column] = true
|
||||
}
|
||||
for _, columnConfig := range inputParam.ColumnList {
|
||||
if !tableColumnTable[columnConfig.Column] {
|
||||
return errors.New(columnConfig.Column + " : input column not found in table column list")
|
||||
}
|
||||
if len(columnConfig.Alias) == 0 {
|
||||
columnConfig.Alias = columnConfig.Column
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getTx 获取数据库连接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 12:20 2024/8/23
|
||||
func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (*gorm.DB, error) {
|
||||
if nil != inputParam.Tx {
|
||||
return inputParam.Tx, nil
|
||||
}
|
||||
if inputParam.ForceMaster || // 强制操作主库
|
||||
wrapper.ArrayType[string]([]string{consts.SqlTypeDelete, consts.SqlTypeInsert, consts.SqlTypeUpdate}).Has(inputParam.SqlType) >= 0 { // 写操作
|
||||
return e.databaseClientManager.GetMasterClient(ctx, inputParam.DatabaseFlag)
|
||||
}
|
||||
return e.databaseClientManager.GetSlaveClient(ctx, inputParam.DatabaseFlag)
|
||||
}
|
||||
|
||||
// getOptionList 设置where条件
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 12:31 2024/8/23
|
||||
func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]define.SetOption, error) {
|
||||
optionList := []define.SetOption{
|
||||
database.WithTable(inputParam.Table),
|
||||
}
|
||||
// 设置 limit offset
|
||||
if !inputParam.ForceNoLimit && inputParam.Limit > 0 {
|
||||
optionList = append(optionList, database.WithLimit(inputParam.Limit, inputParam.Offset))
|
||||
}
|
||||
for _, item := range inputParam.ConditionList {
|
||||
optionFunc, err := database.WithAnyCondition(item.Column, item.Operate, item.Value)
|
||||
if nil != err {
|
||||
return nil, err
|
||||
}
|
||||
optionList = append(optionList, optionFunc)
|
||||
}
|
||||
return optionList, nil
|
||||
}
|
||||
|
||||
// dynamicGenerateStruct 生成动态结构体
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:19 2024/8/30
|
||||
func (e *execute) dynamicGenerateStruct(columnList []*define.ColumnConfig) *wrapper.DynamicStruct {
|
||||
st := wrapper.NewDynamic()
|
||||
for _, columnConfig := range columnList {
|
||||
tag := fmt.Sprintf(`%v`, columnConfig.Alias)
|
||||
column := wrapper.String(columnConfig.Column).SnakeCaseToCamel()
|
||||
switch columnConfig.Type {
|
||||
case "int", "int8", "int16", "int32", "int64":
|
||||
st.AddInt(column, tag, "")
|
||||
case "uint", "uint8", "uint16", "uint32", "uint64":
|
||||
st.AddUint(column, tag, "")
|
||||
case "bool":
|
||||
st.AddBool(column, tag, "")
|
||||
case "float":
|
||||
st.AddBool(column, tag, "")
|
||||
case "string":
|
||||
st.AddString(column, tag, "")
|
||||
case "map":
|
||||
st.AddMap(column, tag, "")
|
||||
case "slice":
|
||||
st.AddSlice(column, tag, "")
|
||||
}
|
||||
}
|
||||
return st
|
||||
}
|
163
api2sql/execute_test.go
Normal file
163
api2sql/execute_test.go
Normal file
@ -0,0 +1,163 @@
|
||||
// Package api2sql ...
|
||||
//
|
||||
// Description : api2sql ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-23 17:36
|
||||
package api2sql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/database"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func startTest() {
|
||||
clientManager := database.NewWrapperClient()
|
||||
if err := clientManager.AddWithConfig("TEST_DATABASE", nil, &define.Database{
|
||||
Master: &define.Driver{
|
||||
DBType: "sqlite3",
|
||||
Host: "/tmp/gateway.db",
|
||||
},
|
||||
Slave: &define.Driver{
|
||||
DBType: "sqlite3",
|
||||
Host: "/tmp/gateway.db",
|
||||
},
|
||||
}, []string{}); nil != err {
|
||||
panic(err.Error())
|
||||
}
|
||||
dbClient, _ := clientManager.GetDBClient("TEST_DATABASE")
|
||||
|
||||
dbClient.SetTableStructure(map[string][]*define.ColumnConfig{
|
||||
"project": []*define.ColumnConfig{
|
||||
&define.ColumnConfig{
|
||||
Column: "flag",
|
||||
Alias: "project_flag",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
})
|
||||
Exec.SetDatabaseClientManager(clientManager)
|
||||
}
|
||||
|
||||
func Test_execute_List(t *testing.T) {
|
||||
startTest()
|
||||
res, err := Exec.List(context.Background(), &define.Api2SqlParam{
|
||||
DatabaseFlag: "TEST_DATABASE",
|
||||
Table: "project",
|
||||
ForceMaster: false,
|
||||
InputSql: "",
|
||||
TableSplitConfig: define.TableSplitConfig{},
|
||||
SqlType: consts.SqlTypeList,
|
||||
ColumnList: []*define.ColumnConfig{
|
||||
&define.ColumnConfig{
|
||||
Column: "flag",
|
||||
Alias: "project_flag",
|
||||
Type: "string",
|
||||
},
|
||||
&define.ColumnConfig{
|
||||
Column: "connect_timeout",
|
||||
Alias: "timeout",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
Limit: 0,
|
||||
Offset: 0,
|
||||
ForceNoLimit: false,
|
||||
OrderField: "id",
|
||||
OrderRule: "desc",
|
||||
WithCount: false,
|
||||
ConditionList: nil,
|
||||
TableColumnConfig: nil,
|
||||
Tx: nil,
|
||||
})
|
||||
byteData, _ := json.Marshal(res)
|
||||
fmt.Println(string(byteData), err)
|
||||
}
|
||||
|
||||
func Test_execute_Detail(t *testing.T) {
|
||||
startTest()
|
||||
res, err := Exec.Detail(context.Background(), &define.Api2SqlParam{
|
||||
DatabaseFlag: "TEST_DATABASE",
|
||||
Table: "project",
|
||||
ForceMaster: false,
|
||||
InputSql: "",
|
||||
TableSplitConfig: define.TableSplitConfig{},
|
||||
SqlType: consts.SqlTypeList,
|
||||
ColumnList: []*define.ColumnConfig{
|
||||
&define.ColumnConfig{
|
||||
Column: "flag",
|
||||
Alias: "project_flag",
|
||||
Type: "string",
|
||||
},
|
||||
&define.ColumnConfig{
|
||||
Column: "description",
|
||||
Alias: "desc",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
Limit: 0,
|
||||
Offset: 0,
|
||||
ForceNoLimit: false,
|
||||
OrderField: "id",
|
||||
OrderRule: "desc",
|
||||
WithCount: false,
|
||||
ConditionList: nil,
|
||||
TableColumnConfig: nil,
|
||||
Tx: nil,
|
||||
})
|
||||
byteData, _ := json.Marshal(res)
|
||||
fmt.Println(res, err, string(byteData))
|
||||
}
|
||||
|
||||
func Test_execute_Insert(t *testing.T) {
|
||||
startTest()
|
||||
res, err := Exec.Insert(context.Background(), &define.Api2SqlParam{
|
||||
DatabaseFlag: "TEST_DATABASE",
|
||||
Table: "project",
|
||||
ForceMaster: false,
|
||||
InputSql: "",
|
||||
TableSplitConfig: define.TableSplitConfig{},
|
||||
SqlType: consts.SqlTypeList,
|
||||
ColumnList: []*define.ColumnConfig{
|
||||
&define.ColumnConfig{
|
||||
Column: "flag",
|
||||
Alias: "project_flag",
|
||||
Type: "string",
|
||||
},
|
||||
&define.ColumnConfig{
|
||||
Column: "description",
|
||||
Alias: "desc",
|
||||
Type: "string",
|
||||
},
|
||||
},
|
||||
Limit: 0,
|
||||
Offset: 0,
|
||||
ForceNoLimit: false,
|
||||
OrderField: "id",
|
||||
OrderRule: "desc",
|
||||
WithCount: false,
|
||||
ConditionList: []define.SqlCondition{
|
||||
define.SqlCondition{
|
||||
Column: "flag",
|
||||
Operate: "",
|
||||
Value: fmt.Sprintf("unit_test_flag_%v", time.Now().Unix()),
|
||||
},
|
||||
define.SqlCondition{
|
||||
Column: "description",
|
||||
Operate: "",
|
||||
Value: "单元测试生成项目",
|
||||
},
|
||||
},
|
||||
TableColumnConfig: nil,
|
||||
Tx: nil,
|
||||
})
|
||||
byteData, _ := json.Marshal(res)
|
||||
fmt.Println(res, err, string(byteData))
|
||||
}
|
68
base.go
68
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 interface{}, 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 interface{}, optionList ...Se
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 8:12 下午 2021/8/8
|
||||
func (b *BaseDao) Update(dbInstance *gorm.DB, updateData interface{}, 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 interface{}, optionFunc
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:05 2024/1/13
|
||||
func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData interface{}, 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 interface{}, optionF
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 8:14 下午 2021/8/8
|
||||
func (b *BaseDao) List(dbInstance *gorm.DB, result interface{}, 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 interface{}, optionFuncList .
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:46 2023/2/9
|
||||
func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel interface{}, 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 interface{}, optionFuncL
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 8:25 下午 2021/8/8
|
||||
func (b *BaseDao) Detail(dbInstance *gorm.DB, result interface{}, 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
|
||||
}
|
||||
@ -94,12 +95,12 @@ func (b *BaseDao) Detail(dbInstance *gorm.DB, result interface{}, optionFuncList
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:14 2023/10/15
|
||||
func (b *BaseDao) DetailByPrimaryID(dbInstance *gorm.DB, result interface{}, primaryID interface{}, primaryKey ...string) error {
|
||||
func (b *BaseDao) DetailByPrimaryID(dbInstance *gorm.DB, result any, primaryID any, primaryKey ...string) error {
|
||||
primaryKeyField := "id"
|
||||
if len(primaryKey) > 0 {
|
||||
primaryKeyField = primaryKey[0]
|
||||
}
|
||||
return b.Detail(dbInstance, result, WithWhere(map[string]interface{}{
|
||||
return b.Detail(dbInstance, result, WithWhere(map[string]any{
|
||||
primaryKeyField: primaryID,
|
||||
}))
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
@ -192,11 +193,6 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
|
||||
tx = tx.Model(o.Model)
|
||||
}
|
||||
|
||||
// 设置where条件
|
||||
if nil != o.Where && len(o.Where) > 0 {
|
||||
tx = tx.Where(o.Where)
|
||||
}
|
||||
|
||||
// 设置 limit offset
|
||||
if o.Limit > 0 {
|
||||
tx = tx.Limit(o.Limit)
|
||||
@ -204,7 +200,6 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
|
||||
tx = tx.Offset(o.Offset)
|
||||
}
|
||||
}
|
||||
|
||||
// in 语句
|
||||
if nil != o.In {
|
||||
tx = tx.Where(o.In)
|
||||
@ -245,6 +240,15 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
@ -252,16 +256,28 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
|
||||
|
||||
// or 语句
|
||||
if nil != o.OR {
|
||||
expression := ""
|
||||
valList := make([]interface{}, 0)
|
||||
for _, o := range o.OR {
|
||||
if len(expression) > 0 {
|
||||
expression = expression + " OR "
|
||||
for _, itemOr := range o.OR {
|
||||
orOption := &define.Option{}
|
||||
for _, fn := range itemOr {
|
||||
fn(orOption)
|
||||
}
|
||||
expression = expression + " " + o.Express
|
||||
valList = append(valList, o.Value)
|
||||
orSql, orBindVal := optionToSql(orOption)
|
||||
tx.Or(orSql, orBindVal)
|
||||
}
|
||||
tx = tx.Where(expression, valList...)
|
||||
}
|
||||
/*sqlBlockList := make([]string, 0)
|
||||
bindValueList := make([]any, 0)
|
||||
sql, bindVal := optionToSql(o)
|
||||
tx.Or(sql, bindVal...)
|
||||
sqlBlockList = append(sqlBlockList, sql)
|
||||
bindValueList = append(bindValueList, bindVal)
|
||||
for _, itemOrFuncList := range o.OR {
|
||||
orOption := &Option{}
|
||||
for _, fn := range itemOrFuncList {
|
||||
fn(orOption)
|
||||
}
|
||||
orSql, orBindVal := optionToSql(orOption)
|
||||
tx.Or(orSql, orBindVal...)
|
||||
}*/
|
||||
return tx
|
||||
}
|
||||
|
327
client.go
327
client.go
@ -1,327 +0,0 @@
|
||||
// Package database ...
|
||||
//
|
||||
// Description : mysql客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2021-03-01 9:20 下午
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/logger/wrapper"
|
||||
"go.uber.org/zap"
|
||||
|
||||
gormLogger "gorm.io/gorm/logger"
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
// Client mysql客户端
|
||||
Client *client
|
||||
)
|
||||
|
||||
func init() {
|
||||
Client = &client{
|
||||
lock: &sync.RWMutex{},
|
||||
clientTable: make(map[string]*DBClient),
|
||||
}
|
||||
}
|
||||
|
||||
type client struct {
|
||||
lock *sync.RWMutex
|
||||
clientTable map[string]*DBClient
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// AddWithConfigFile 使用文件生成新的客户端,文件名去掉后缀作为flag
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:19 2022/6/5
|
||||
func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error {
|
||||
var (
|
||||
err error
|
||||
cfg *cfgFile
|
||||
)
|
||||
|
||||
if cfg, err = c.getCfg(cfgFilePath); nil != err {
|
||||
return err
|
||||
}
|
||||
if nil == cfg {
|
||||
// 不支持的配置文件格式
|
||||
return nil
|
||||
}
|
||||
return c.AddWithConfig(cfg.Flag, logInstance, cfg.Config, extraFieldList)
|
||||
}
|
||||
|
||||
// AddWithConfig ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:41 2023/4/18
|
||||
func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *Database, extraFieldList []string) error {
|
||||
dbClient := &DBClient{
|
||||
dbFlag: flag,
|
||||
loggerInstance: logInstance,
|
||||
master: nil,
|
||||
slave: nil,
|
||||
extraFieldList: extraFieldList,
|
||||
cfg: Driver{},
|
||||
}
|
||||
var err error
|
||||
if dbClient.master, err = c.GetDatabaseClient(databaseConfig.Master, logInstance); nil != err {
|
||||
return err
|
||||
}
|
||||
if dbClient.slave, err = c.GetDatabaseClient(databaseConfig.Slave, logInstance); nil != err {
|
||||
return err
|
||||
}
|
||||
c.lock.Lock()
|
||||
c.clientTable[dbClient.dbFlag] = dbClient
|
||||
c.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchAddWithConfigDir 自动读取目录下配置文件, 生成客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:19 2022/6/5
|
||||
func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error {
|
||||
filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*"))
|
||||
for i := range filepathNames {
|
||||
if err := c.AddWithConfigFile(filepathNames[i], logInstance, extraFieldList); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCfg 读取配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:05 2022/6/11
|
||||
func (c *client) getCfg(cfgPath string) (*cfgFile, error) {
|
||||
fileArr := strings.Split(cfgPath, ".")
|
||||
if len(fileArr) < 2 {
|
||||
// 获取不到类型
|
||||
return nil, errors.New("文件格式必须是JSON或者YAML")
|
||||
}
|
||||
fileType := strings.ToLower(fileArr[len(fileArr)-1])
|
||||
fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator))
|
||||
result := &cfgFile{
|
||||
Path: cfgPath,
|
||||
Type: "",
|
||||
Flag: fileFlagArr[len(fileFlagArr)-1],
|
||||
Config: &Database{},
|
||||
}
|
||||
var (
|
||||
err error
|
||||
cfgInfo Database
|
||||
)
|
||||
switch fileType {
|
||||
case consts.FileTypeYaml:
|
||||
fallthrough
|
||||
case consts.FileTypeYml:
|
||||
result.Type = consts.FileTypeYaml
|
||||
if err = serialize.File.ReadYmlContent(cfgPath, &result.Config); nil != err {
|
||||
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
|
||||
}
|
||||
case consts.FileTypeJson:
|
||||
result.Type = consts.FileTypeJson
|
||||
if err = serialize.File.ReadJSONContent(cfgPath, &cfgInfo); nil != err {
|
||||
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
|
||||
}
|
||||
default:
|
||||
// 不是JSON , 也不是YML, 跳过
|
||||
return nil, nil
|
||||
}
|
||||
if len(result.Config.Master.Timezone) == 0 {
|
||||
// 默认使用本地时区
|
||||
result.Config.Master.Timezone = "Local"
|
||||
} else {
|
||||
result.Config.Slave.Timezone = result.Config.Master.Timezone
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetDBClient 获取db client
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:32 2022/6/5
|
||||
func (c *client) GetDBClient(dbFlag string) (*DBClient, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
var (
|
||||
exist bool
|
||||
dbClient *DBClient
|
||||
)
|
||||
if dbClient, exist = c.clientTable[dbFlag]; !exist {
|
||||
return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag)
|
||||
}
|
||||
return dbClient, nil
|
||||
}
|
||||
|
||||
// GetMasterClient 获取主库客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:36 2022/6/5
|
||||
func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
|
||||
var (
|
||||
err error
|
||||
dbClient *DBClient
|
||||
)
|
||||
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := dbClient.master.Session(&gorm.Session{})
|
||||
session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-master")
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// GetSlaveClient 获取从库客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:37 2022/6/5
|
||||
func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
|
||||
var (
|
||||
err error
|
||||
dbClient *DBClient
|
||||
)
|
||||
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := dbClient.slave.Session(&gorm.Session{})
|
||||
session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-slave")
|
||||
return session, nil
|
||||
}
|
||||
|
||||
// getGormClient 获取GORM client方法
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:24 2022/6/6
|
||||
func (c *client) getGormClient() (*gorm.DB, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// GetDatabaseClient 获取数据库连接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:41 2022/6/11
|
||||
func (c *client) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm.DB, error) {
|
||||
var (
|
||||
instance *gorm.DB
|
||||
err error
|
||||
)
|
||||
|
||||
if conf.DBType == consts.DatabaseDriverMysql {
|
||||
if instance, err = gorm.Open(mysql.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else if conf.DBType == consts.DatabaseDriverSqlite3 {
|
||||
if instance, err = gorm.Open(sqlite.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("%v : db driver is not support", conf.DBType)
|
||||
}
|
||||
|
||||
instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil)
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// buildConnectionDSN 构建连接信息
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:42 2022/6/11
|
||||
func (c *client) buildConnectionDSN(conf *Driver) string {
|
||||
if conf.DBType == consts.DatabaseDriverSqlite3 {
|
||||
// 兼容sqlite3
|
||||
return conf.Host
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s",
|
||||
conf.Username,
|
||||
conf.Password,
|
||||
conf.Host,
|
||||
conf.Port,
|
||||
conf.Database,
|
||||
conf.Charset,
|
||||
conf.Timezone,
|
||||
)
|
||||
}
|
||||
|
||||
// DBClient 包装日志实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:09 PM 2021/12/24
|
||||
type DBClient struct {
|
||||
dbFlag string // 数据库标识
|
||||
loggerInstance *zap.Logger // 日志实例
|
||||
master *gorm.DB // 主库
|
||||
slave *gorm.DB // 从库
|
||||
extraFieldList []string // 提取的字段
|
||||
cfg Driver // 数据库配置
|
||||
}
|
||||
|
||||
// SetFlag 设置数据库标识
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:18 2022/6/5
|
||||
func (dc *DBClient) SetFlag(dbFlag string) {
|
||||
dc.dbFlag = dbFlag
|
||||
}
|
||||
|
||||
// GetMaster 获取主库连接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:28 PM 2021/12/24
|
||||
func (dc *DBClient) GetMaster(ctx context.Context) *gorm.DB {
|
||||
session := dc.master.Session(&gorm.Session{})
|
||||
session.Logger = dc.getLogger(ctx, session, "slave")
|
||||
return session
|
||||
}
|
||||
|
||||
// GetSlave 获取从库链接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:29 PM 2021/12/24
|
||||
func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB {
|
||||
session := dc.slave.Session(&gorm.Session{})
|
||||
session.Logger = dc.getLogger(ctx, session, "slave")
|
||||
return session
|
||||
}
|
||||
|
||||
// getLogger 获取日志实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:45 PM 2021/12/24
|
||||
func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node string) gormLogger.Interface {
|
||||
return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.loggerInstance, dc.dbFlag+"|"+node, dc.extraFieldList)
|
||||
}
|
73
define/api2sql.go
Normal file
73
define/api2sql.go
Normal file
@ -0,0 +1,73 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-21 16:05
|
||||
package define
|
||||
|
||||
import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Api2SqlParam 接口转sql的输入配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:06 2024/8/21
|
||||
//
|
||||
// 一个合法的 sql 配置格式 :
|
||||
//
|
||||
// select 语句 : SELECT {FIELD_LIST} FROM {TABLE} WHERE {WHERE} ORDER BY {ORDER_FIELD} {ORDER_RULE} LIMIT {LIMIT} OFFSET {OFFSET}
|
||||
//
|
||||
// insert 语句 : INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST})
|
||||
//
|
||||
// update 语句 : UPDATE {TABLE} SET {SET} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE}
|
||||
//
|
||||
// delete 语句 : DELETE FROM {TABLE} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE}
|
||||
//
|
||||
// count 语句 : SELECT COUNT(*) as count FROM {TABLE} WHERE {WHERE}
|
||||
type Api2SqlParam struct {
|
||||
DatabaseFlag string `json:"database_flag"` // 数据库标识
|
||||
Table string `json:"table"` // 操作的数据表
|
||||
ForceMaster bool `json:"force_master"` // 针对查询语句, 是否强制读主, 如果查询语句在事务中, 默认强制读主
|
||||
InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置
|
||||
TableSplitConfig TableSplitConfig `json:"table_split_config"` // 分表配置
|
||||
SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除
|
||||
ColumnList []*ColumnConfig `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出
|
||||
Limit int `json:"limit"` // 操作数量
|
||||
Offset int `json:"offset"` // 操作偏移量
|
||||
ForceNoLimit bool `json:"force_no_limit"` // 强制允许不限制 : 正常操作 select / delete / update 均需要指定本次操作数据量, 如果确定不限制, 此参数设置为 `true` , sqlType = list , 且 ForceNoLimit = true 时, WithCount 参数无效
|
||||
OrderField string `json:"order_field"` // 排序字段, 仅 sqlType = list 生效
|
||||
OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc
|
||||
WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效
|
||||
ConditionList []SqlCondition `json:"value_list"` // 字段列表
|
||||
TableColumnConfig []*ColumnConfig `json:"table_column_config"` // 表字段配置
|
||||
UpdateData map[string]any `json:"update_data"` // 要更新的数据, 仅对 update语句有效
|
||||
Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用
|
||||
}
|
||||
|
||||
// TableSplitConfig 分表配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:42 2024/8/23
|
||||
type TableSplitConfig struct {
|
||||
IsSplit bool `json:"is_split"` // 是否分表
|
||||
SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在
|
||||
FieldValue any `json:"field_value"` // 分表字段值
|
||||
SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略
|
||||
TableCnt int64 `json:"table_cnt"` // 一共分表多少张
|
||||
}
|
||||
|
||||
// SqlCondition sql条件
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:46 2024/8/23
|
||||
type SqlCondition struct {
|
||||
Column string `json:"column"` // 表字段
|
||||
Operate string `json:"operate"` // 操作 : == / !== / in / not in / like / not like
|
||||
Value any `json:"value"` // 数据值
|
||||
}
|
@ -1,11 +1,11 @@
|
||||
// package database...
|
||||
// Package define ...
|
||||
//
|
||||
// Description : 数据定义
|
||||
//
|
||||
// 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"` // 配置文件类型
|
39
define/sql_option.go
Normal file
39
define/sql_option.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-23 17:46
|
||||
package define
|
||||
|
||||
// SetOption 设置选项
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:46 2022/5/15
|
||||
type SetOption func(o *Option)
|
||||
|
||||
// Option 扩展选项
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 8:05 下午 2021/8/8
|
||||
type Option struct {
|
||||
Model any `json:"-"` // 操作model
|
||||
Table string `json:"table"` // 查询的数据表
|
||||
Limit int `json:"limit"` // 限制数量
|
||||
Offset int `json:"offset"` // 偏移量
|
||||
In map[string]any `json:"in"` // in语句
|
||||
NotIn map[string]any `json:"not_in"` // not in语句
|
||||
Where map[string]any `json:"where"` // where 条件
|
||||
Between map[string][2]any `json:"between"` // between 条件
|
||||
NotBetween map[string][2]any `json:"not_between"` // not between 条件
|
||||
Start map[string]any `json:"start"` // >= 条件
|
||||
End map[string]any `json:"end"` // < 条件
|
||||
Like map[string]string `json:"like"` // like 语句
|
||||
NotLike map[string]string `json:"not_like"` // not like 语句
|
||||
NotEqual map[string]any `json:"not_equal"` // != 语句
|
||||
Order []string `json:"order"` // 排序规则
|
||||
OR [][]SetOption `json:"or"` // or 语句, or 和其他属性 and 关系, 内部 OR 关系
|
||||
}
|
29
define/table.go
Normal file
29
define/table.go
Normal file
@ -0,0 +1,29 @@
|
||||
// Package define ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-21 14:56
|
||||
package define
|
||||
|
||||
// CacheTableStructureConfig 缓存表结构的配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:56 2024/8/21
|
||||
type CacheTableStructureConfig struct {
|
||||
Enable bool `json:"enable"` // 是否启用表结构缓存
|
||||
SyncTimeInterval int `json:"sync_time_interval"` // 开启的情况向, 多久同步一次表结构, 默认值 3600, 单位 : s
|
||||
}
|
||||
|
||||
// ColumnConfig ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:42 2024/8/23
|
||||
type ColumnConfig struct {
|
||||
Column string `json:"column"` // 字段名
|
||||
Alias string `json:"alias"` // 字段别名
|
||||
Type string `json:"type"` // 字段类型
|
||||
}
|
24
go.mod
24
go.mod
@ -1,15 +1,15 @@
|
||||
module git.zhangdeman.cn/zhangdeman/database
|
||||
|
||||
go 1.21
|
||||
go 1.21.0
|
||||
|
||||
toolchain go1.21.3
|
||||
toolchain go1.23.0
|
||||
|
||||
require (
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4
|
||||
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
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd
|
||||
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
|
||||
@ -22,13 +22,13 @@ require (
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/easylock v0.0.0-20230731062340-983985c12eda // indirect
|
||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240726061047-9b7e714b2932 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 // indirect
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e // indirect
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 // indirect
|
||||
github.com/BurntSushi/toml v1.4.0 // indirect
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394 // indirect
|
||||
github.com/bytedance/sonic v1.12.1 // indirect
|
||||
github.com/bytedance/sonic v1.12.2 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.4 // indirect
|
||||
github.com/cloudwego/iasm v0.2.0 // indirect
|
||||
@ -49,15 +49,15 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible // indirect
|
||||
github.com/lestrrat-go/strftime v1.0.6 // indirect
|
||||
github.com/lestrrat-go/strftime v1.1.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.22 // indirect
|
||||
github.com/mattn/go-sqlite3 v1.14.23 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mozillazg/go-pinyin v0.20.0 // indirect
|
||||
github.com/mssola/user_agent v0.6.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||
github.com/tidwall/gjson v1.17.3 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
@ -65,11 +65,11 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.9.0 // indirect
|
||||
golang.org/x/arch v0.10.0 // indirect
|
||||
golang.org/x/crypto v0.26.0 // indirect
|
||||
golang.org/x/net v0.28.0 // indirect
|
||||
golang.org/x/sys v0.24.0 // indirect
|
||||
golang.org/x/text v0.17.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/olahol/melody.v1 v1.0.0-20170518105555-d52139073376 // indirect
|
||||
|
44
go.sum
44
go.sum
@ -1,13 +1,13 @@
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyzYbZullK0aTHVASHl3UeoVr8IgytQZsuyv+yEM=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
|
||||
git.zhangdeman.cn/zhangdeman/consts v0.0.0-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=
|
||||
git.zhangdeman.cn/zhangdeman/easymap v0.0.0-20240311030808-e2a2e6a3c211/go.mod h1:SrtvrQRdzt+8KfYzvosH++gWxo2ShPTzR1m3VQ6uX7U=
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240726061047-9b7e714b2932 h1:4UoCcCoTAFmS624lypM0D0Wlb4Q5Ru1saodR8Kj/gCg=
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240726061047-9b7e714b2932/go.mod h1:CTcgmcDky34SS+KQvEt/RQgszyTVdNa9VZumGvN7Oqk=
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512 h1:j+zH8gl4aEg24GWCutddnH5hw4eAuFJLW6vvJUJiJpM=
|
||||
git.zhangdeman.cn/zhangdeman/gin v0.0.0-20240817122134-195e39123512/go.mod h1:twDDFXzPJ56qfAG1ZtPhvwXmtZG+aRlk2dnnjufh/hs=
|
||||
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a h1:22VkxGmpS58zHA8tf75lPr/3S6hmHKP00w/jUa700Ps=
|
||||
git.zhangdeman.cn/zhangdeman/logger v0.0.0-20240725055115-98eb52ae307a/go.mod h1:phaF6LMebn7Fpp8J/mOzHRYGniKuCk78k4N53T2m8NI=
|
||||
git.zhangdeman.cn/zhangdeman/network v0.0.0-20230925112156-f0eb86dd2442 h1:1eBf0C0gdpBQOqjTK3UCw/mwzQ/SCodx3iTQtidx9eE=
|
||||
@ -20,14 +20,14 @@ git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e h1:Q973S6Cc
|
||||
git.zhangdeman.cn/zhangdeman/util v0.0.0-20240618042405-6ee2c904644e/go.mod h1:VpPjBlwz8U+OxZuxzHQBv1aEEZ3pStH6bZvT21ADEbI=
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2 h1:P2kuhU2TFWk9mPbJlZCK6FMDVS7S3NGafWKD4eNqKiI=
|
||||
git.zhangdeman.cn/zhangdeman/websocket v0.0.0-20240723075210-85feada512b2/go.mod h1:7KaMpQmWgiNLpEkaV7oDtep7geI1f/VoCoPVcyvMjAE=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfmp/1srHldFpa7BCH6Cpp9iVNFM/7W7DoxHp48UU=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo=
|
||||
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-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=
|
||||
github.com/axgle/mahonia v0.0.0-20180208002826-3358181d7394/go.mod h1:Q8n74mJTIgjX4RBBcHnJ05h//6/k6foqmgE45jTQtxg=
|
||||
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
|
||||
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
|
||||
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||
@ -84,14 +84,16 @@ github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc h1:RKf14vYWi2t
|
||||
github.com/lestrrat-go/envload v0.0.0-20180220234015-a3eb8ddeffcc/go.mod h1:kopuH9ugFRkIXf3YoqHKyrJ9YfUFsckUU9S7B+XP+is=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible h1:Y6sqxHMyB1D2YSzWkLibYKgg+SwmyFU9dF2hn6MdTj4=
|
||||
github.com/lestrrat-go/file-rotatelogs v2.4.0+incompatible/go.mod h1:ZQnN8lSECaebrkQytbHj4xNgtg8CR7RYXnPok8e0EHA=
|
||||
github.com/lestrrat-go/strftime v1.0.6 h1:CFGsDEt1pOpFNU+TJB0nhz9jl+K0hZSLE205AhTIGQQ=
|
||||
github.com/lestrrat-go/strftime v1.0.6/go.mod h1:f7jQKgV5nnJpYgdEasS+/y7EsTb8ykN2z68n3TtcTaw=
|
||||
github.com/lestrrat-go/strftime v1.1.0 h1:gMESpZy44/4pXLO/m+sL0yBd1W6LjgjrrD4a68Gapyg=
|
||||
github.com/lestrrat-go/strftime v1.1.0/go.mod h1:uzeIB52CeUJenCo1syghlugshMysrqUT51HlxphXVeI=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.23 h1:gbShiuAP1W5j9UOksQ06aiiqPMxYecovVGwmTxWtuw0=
|
||||
github.com/mattn/go-sqlite3 v1.14.23/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
@ -101,8 +103,8 @@ github.com/mozillazg/go-pinyin v0.20.0 h1:BtR3DsxpApHfKReaPO1fCqF4pThRwH9uwvXzm+
|
||||
github.com/mozillazg/go-pinyin v0.20.0/go.mod h1:iR4EnMMRXkfpFVV5FMi4FNB6wGq9NV6uDWbUuPhP4Yc=
|
||||
github.com/mssola/user_agent v0.6.0 h1:uwPR4rtWlCHRFyyP9u2KOV0u8iQXmS7Z7feTrstQwk4=
|
||||
github.com/mssola/user_agent v0.6.0/go.mod h1:TTPno8LPY3wAIEKRpAtkdMT0f8SE24pLRGPahjCH4uw=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
@ -112,13 +114,11 @@ github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.17.3 h1:bwWLZU7icoKRG+C+0PNwIKC6FCJO/Q3p2pZvuP0jN94=
|
||||
@ -140,18 +140,18 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
|
||||
golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
|
||||
|
351
option.go
351
option.go
@ -8,55 +8,24 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"git.zhangdeman.cn/zhangdeman/op_type"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SetOption 设置选项
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 11:46 2022/5/15
|
||||
type SetOption func(o *Option)
|
||||
|
||||
// ORCondition OR 条件
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:07 2022/7/23
|
||||
type ORCondition struct {
|
||||
Express string `json:"express"` // 表达式
|
||||
Value interface{} `json:"value"` // 绑定值
|
||||
}
|
||||
|
||||
// Option 扩展选项
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 8:05 下午 2021/8/8
|
||||
type Option struct {
|
||||
Model interface{} `json:"-"` // 操作model
|
||||
Table string `json:"table"` // 查询的数据表
|
||||
Limit int `json:"limit"` // 限制数量
|
||||
Offset int `json:"offset"` // 偏移量
|
||||
In map[string]interface{} `json:"in"` // in语句
|
||||
NotIn map[string]interface{} `json:"not_in"` // not in语句
|
||||
Where map[string]interface{} `json:"where"` // where 条件
|
||||
Start map[string]interface{} `json:"start"` // >= 条件
|
||||
End map[string]interface{} `json:"end"` // < 条件
|
||||
Like map[string]string `json:"like"` // like 语句
|
||||
NotLike map[string]string `json:"not_like"` // not like 语句
|
||||
NotEqual map[string]interface{} `json:"not_equal"` // != 语句
|
||||
Order []string `json:"order"` // 排序规则
|
||||
OR []*ORCondition `json:"or"` // or 语句, or 和其他属性 and 关系, 内部 OR 关系
|
||||
}
|
||||
|
||||
// WithModel ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:18 2024/1/15
|
||||
func WithModel(model interface{}) SetOption {
|
||||
return func(o *Option) {
|
||||
func WithModel(model any) define.SetOption {
|
||||
return func(o *define.Option) {
|
||||
o.Model = model
|
||||
}
|
||||
}
|
||||
@ -66,8 +35,8 @@ func WithModel(model interface{}) 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
|
||||
}
|
||||
}
|
||||
@ -77,10 +46,10 @@ 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]interface{})
|
||||
o.Where = make(map[string]any)
|
||||
}
|
||||
for field, value := range where {
|
||||
o.Where[field] = value
|
||||
@ -93,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)
|
||||
}
|
||||
@ -105,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)
|
||||
}
|
||||
@ -122,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
|
||||
}
|
||||
@ -131,7 +100,7 @@ func WithIn[T op_type.Array](field string, value T) SetOption {
|
||||
return
|
||||
}
|
||||
if nil == o.In {
|
||||
o.In = make(map[string]interface{})
|
||||
o.In = make(map[string]any)
|
||||
}
|
||||
o.In[field] = value
|
||||
}
|
||||
@ -142,10 +111,10 @@ 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]interface{})
|
||||
o.In = make(map[string]any)
|
||||
}
|
||||
for field, value := range batchIn {
|
||||
WithIn(field, value)
|
||||
@ -158,10 +127,10 @@ 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]interface{})
|
||||
o.NotIn = make(map[string]any)
|
||||
}
|
||||
if value == nil || len(value) == 0 {
|
||||
return
|
||||
@ -175,10 +144,10 @@ 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]interface{})
|
||||
o.NotIn = make(map[string]any)
|
||||
}
|
||||
for field, value := range data {
|
||||
if value == nil || len(value) == 0 {
|
||||
@ -194,10 +163,10 @@ 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 interface{}) 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]interface{})
|
||||
o.Start = make(map[string]any)
|
||||
}
|
||||
o.Start[field] = value
|
||||
}
|
||||
@ -208,10 +177,10 @@ func WithStart(field string, value interface{}) SetOption {
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:03 2022/5/15
|
||||
func WithBatchStart(data map[string]interface{}) 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]interface{})
|
||||
o.Start = make(map[string]any)
|
||||
}
|
||||
for field, value := range data {
|
||||
o.Start[field] = value
|
||||
@ -224,10 +193,10 @@ func WithBatchStart(data map[string]interface{}) SetOption {
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:07 2022/5/15
|
||||
func WithEnd(field string, value interface{}) 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]interface{})
|
||||
o.End = make(map[string]any)
|
||||
}
|
||||
o.End[field] = value
|
||||
}
|
||||
@ -238,10 +207,10 @@ func WithEnd(field string, value interface{}) SetOption {
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:10 2022/5/15
|
||||
func WithBatchEnd(data map[string]interface{}) 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]interface{})
|
||||
o.End = make(map[string]any)
|
||||
}
|
||||
for field, value := range data {
|
||||
o.End[field] = value
|
||||
@ -254,8 +223,8 @@ func WithBatchEnd(data map[string]interface{}) 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)
|
||||
}
|
||||
@ -271,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)
|
||||
}
|
||||
@ -290,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)
|
||||
}
|
||||
@ -307,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)
|
||||
}
|
||||
@ -326,10 +295,10 @@ 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]interface{})
|
||||
o.NotEqual = make(map[string]any)
|
||||
}
|
||||
o.NotEqual[field] = value
|
||||
}
|
||||
@ -340,10 +309,10 @@ 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]interface{})
|
||||
o.NotEqual = make(map[string]any)
|
||||
}
|
||||
for field, value := range data {
|
||||
o.NotEqual[field] = value
|
||||
@ -356,12 +325,12 @@ 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 ...*ORCondition) SetOption {
|
||||
return func(o *Option) {
|
||||
func WithOR(orConditionList ...define.SetOption) define.SetOption {
|
||||
return func(o *define.Option) {
|
||||
if nil == o.OR {
|
||||
o.OR = make([]*ORCondition, 0)
|
||||
o.OR = make([][]define.SetOption, 0)
|
||||
}
|
||||
o.OR = append(o.OR, orConditionList...)
|
||||
o.OR = append(o.OR, orConditionList)
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,8 +339,8 @@ func WithOR(orConditionList ...*ORCondition) 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
|
||||
}
|
||||
}
|
||||
@ -381,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)
|
||||
}
|
||||
@ -395,11 +364,197 @@ 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)
|
||||
}
|
||||
o.Order = append(o.Order, field+" asc")
|
||||
}
|
||||
}
|
||||
|
||||
// newOption 生成新的option
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:46 2024/8/9
|
||||
func newOption(setOptionList ...define.SetOption) *define.Option {
|
||||
o := &define.Option{}
|
||||
for _, item := range setOptionList {
|
||||
item(o)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
// optionToSql 基于 option 配置生成sql
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:46 2024/8/9
|
||||
func optionToSql(o *define.Option) (sqlBuildResult string, bindValue []any) {
|
||||
bindValue = make([]any, 0)
|
||||
sqlBuildResultBlockList := make([]string, 0)
|
||||
// 设置where条件
|
||||
for fieldName, fieldValue := range o.Where {
|
||||
if nil == fieldValue {
|
||||
continue
|
||||
}
|
||||
if reflect.TypeOf(fieldValue).Kind() == reflect.Slice {
|
||||
// 传入数组, in语句
|
||||
placeholder, dataList := parseInSql(fieldValue)
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` IN ("+placeholder+")")
|
||||
bindValue = append(bindValue, dataList...)
|
||||
} else {
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` = ?")
|
||||
bindValue = append(bindValue, fieldValue)
|
||||
}
|
||||
}
|
||||
// 设置不等于
|
||||
for fieldName, fieldValue := range o.NotEqual {
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` != ?")
|
||||
bindValue = append(bindValue, fieldValue)
|
||||
}
|
||||
|
||||
// in 语句
|
||||
// 传入数组, in语句
|
||||
for fieldName, fieldValue := range o.In {
|
||||
placeholder, dataList := parseInSql(fieldValue)
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` IN ("+placeholder+")")
|
||||
bindValue = append(bindValue, dataList...)
|
||||
}
|
||||
|
||||
// not in 语句
|
||||
for fieldName, fieldValue := range o.NotIn {
|
||||
placeholder, dataList := parseInSql(fieldValue)
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` NOT IN ("+placeholder+")")
|
||||
bindValue = append(bindValue, dataList...)
|
||||
}
|
||||
|
||||
// like 语句
|
||||
for fieldName, fieldValue := range o.Like {
|
||||
if len(fieldValue) == 0 {
|
||||
continue
|
||||
}
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` LIKE \"%?%\"")
|
||||
bindValue = append(bindValue, fieldValue)
|
||||
}
|
||||
|
||||
// NOT LIKE 语句
|
||||
for fieldName, fieldValue := range o.NotLike {
|
||||
if len(fieldValue) == 0 {
|
||||
continue
|
||||
}
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` NOT LIKE \"%?%\"")
|
||||
bindValue = append(bindValue, fieldValue)
|
||||
}
|
||||
|
||||
// >=
|
||||
for fieldName, fieldValue := range o.Start {
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` >= ?")
|
||||
bindValue = append(bindValue, fieldValue)
|
||||
}
|
||||
|
||||
// <
|
||||
for fieldName, fieldValue := range o.End {
|
||||
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+fieldName+"` < ?")
|
||||
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 ")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// parseInSql 解析in语句需要绑定占位符以及数据
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 22:07 2024/8/9
|
||||
func parseInSql(fieldValue any) (string, []any) {
|
||||
byteData, _ := json.Marshal(fieldValue)
|
||||
var dataList []any
|
||||
decoder := json.NewDecoder(strings.NewReader(string(byteData)))
|
||||
decoder.UseNumber()
|
||||
_ = decoder.Decode(&dataList)
|
||||
// 生成占位符
|
||||
placeholderList := make([]string, 0)
|
||||
for i := 0; i < len(dataList); i++ {
|
||||
placeholderList = append(placeholderList, "?")
|
||||
}
|
||||
return strings.Join(placeholderList, ","), dataList
|
||||
}
|
||||
|
||||
// WithBetween between 语句
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:52 2024/8/23
|
||||
func WithBetween(field string, left any, right any) define.SetOption {
|
||||
return func(o *define.Option) {
|
||||
if nil == o.Between {
|
||||
o.Between = map[string][2]any{}
|
||||
}
|
||||
o.Between[field] = [2]any{left, right}
|
||||
}
|
||||
}
|
||||
|
||||
// WithNotBetween not between 语句
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:52 2024/8/23
|
||||
func WithNotBetween(field string, left any, right any) define.SetOption {
|
||||
return func(o *define.Option) {
|
||||
if nil == o.NotBetween {
|
||||
o.NotBetween = map[string][2]any{}
|
||||
}
|
||||
o.NotBetween[field] = [2]any{left, right}
|
||||
}
|
||||
}
|
||||
|
||||
// WithAnyCondition 设置任意查询条件, 仅 where 子句 in / not in / like / not like / == / !=
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 14:48 2024/8/23
|
||||
func WithAnyCondition(column string, operate string, value any) (define.SetOption, error) {
|
||||
if nil == value {
|
||||
return nil, errors.New("value is nil")
|
||||
}
|
||||
switch operate {
|
||||
case consts.WhereOperateEqual:
|
||||
return WithWhere(map[string]any{column: value}), nil
|
||||
case consts.WhereOperateNotEqual:
|
||||
return WithNotEqual(column, value), nil
|
||||
case consts.WhereOperateIn:
|
||||
var target []any
|
||||
if err := serialize.JSON.Transition(value, &target); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return WithIn[[]any](column, target), nil
|
||||
case consts.WhereOperateNotIn:
|
||||
var target []any
|
||||
if err := serialize.JSON.Transition(value, &target); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
return WithNotIn[[]any](column, target), nil
|
||||
case consts.WhereOperateLike:
|
||||
return WithLike(column, fmt.Sprintf("%v", value)), nil
|
||||
case consts.WhereOperateNotLike:
|
||||
return WithNotLike(column, fmt.Sprintf("%v", value)), nil
|
||||
default:
|
||||
return nil, errors.New(operate + " : operate is not support")
|
||||
}
|
||||
}
|
||||
|
39
option_test.go
Normal file
39
option_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
// Package database ...
|
||||
//
|
||||
// Description : database ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-09 18:19
|
||||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_optionToSql(t *testing.T) {
|
||||
o := &define.Option{
|
||||
In: nil,
|
||||
NotIn: nil,
|
||||
Where: map[string]any{
|
||||
"name": []string{"zhang", "baicha"},
|
||||
"age": 18,
|
||||
},
|
||||
Start: nil,
|
||||
End: nil,
|
||||
Like: map[string]string{
|
||||
"name": "de",
|
||||
},
|
||||
NotLike: map[string]string{
|
||||
"name": "zhang",
|
||||
},
|
||||
NotEqual: map[string]any{
|
||||
"a": 123,
|
||||
},
|
||||
Order: nil,
|
||||
OR: nil,
|
||||
}
|
||||
fmt.Println(optionToSql(o))
|
||||
}
|
@ -36,9 +36,9 @@ func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetDetailBy{PRIMARY_KEY}(dbInstance *go
|
||||
}
|
||||
|
||||
// GetList 获取数据列表
|
||||
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetList(dbInstance *gorm.DB, condition map[string]interface{}, limit int, offset int) ([]{DATA_STRUCT_NAME}, error) {
|
||||
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetList(dbInstance *gorm.DB, condition map[string]any, limit int, offset int) ([]{DATA_STRUCT_NAME}, error) {
|
||||
if nil == condition {
|
||||
condition = make(map[string]interface{})
|
||||
condition = make(map[string]any)
|
||||
}
|
||||
var (
|
||||
err error
|
||||
|
@ -90,9 +90,9 @@ func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetDetailBy{PRIMARY_KEY}(dbInstance *go
|
||||
}
|
||||
|
||||
// GetList 获取数据列表
|
||||
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetList(dbInstance *gorm.DB, condition map[string]interface{}, limit int, offset int) ([]{DATA_STRUCT_NAME}, error) {
|
||||
func ({DAO_RECEIVER} *{DATA_STRUCT_DAO}) GetList(dbInstance *gorm.DB, condition map[string]any, limit int, offset int) ([]{DATA_STRUCT_NAME}, error) {
|
||||
if nil == condition {
|
||||
condition = make(map[string]interface{})
|
||||
condition = make(map[string]any)
|
||||
}
|
||||
var (
|
||||
err error
|
||||
|
@ -47,9 +47,9 @@ func (ad *AppDao) GetDetailByID(dbInstance *gorm.DB, ID int64) (*App, error) {
|
||||
}
|
||||
|
||||
// GetList 获取数据列表
|
||||
func (ad *AppDao) GetList(dbInstance *gorm.DB, condition map[string]interface{}, limit int, offset int) ([]App, error) {
|
||||
func (ad *AppDao) GetList(dbInstance *gorm.DB, condition map[string]any, limit int, offset int) ([]App, error) {
|
||||
if nil == condition {
|
||||
condition = make(map[string]interface{})
|
||||
condition = make(map[string]any)
|
||||
}
|
||||
var (
|
||||
err error
|
||||
|
13
system.go
13
system.go
@ -12,6 +12,7 @@ import (
|
||||
|
||||
"gorm.io/driver/mysql"
|
||||
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
@ -85,7 +86,7 @@ func (sd *SystemDao) GetTableList(dbInstance *gorm.DB) ([]string, error) {
|
||||
func (sd *SystemDao) GetCreateTableSQL(dbInstance *gorm.DB, table string) (string, error) {
|
||||
var (
|
||||
err error
|
||||
result map[string]interface{}
|
||||
result map[string]any
|
||||
)
|
||||
|
||||
if err = dbInstance.Raw("SHOW CREATE TABLE `" + table + "`").Scan(&result).Error; nil != err {
|
||||
@ -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
|
||||
)
|
||||
|
||||
@ -152,6 +153,6 @@ func (sd *SystemDao) CreateTable(dbInstance *gorm.DB, createTableSql string) err
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 16:35 2023/9/30
|
||||
func (sd *SystemDao) ExecuteSql(dbInstance *gorm.DB, anySql string, value ...interface{}) error {
|
||||
func (sd *SystemDao) ExecuteSql(dbInstance *gorm.DB, anySql string, value ...any) error {
|
||||
return dbInstance.Exec(anySql, value...).Error
|
||||
}
|
||||
|
205
wrapper_client.go
Normal file
205
wrapper_client.go
Normal file
@ -0,0 +1,205 @@
|
||||
// Package database ...
|
||||
//
|
||||
// Description : mysql客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2021-03-01 9:20 下午
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/database/abstract"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"git.zhangdeman.cn/zhangdeman/serialize"
|
||||
"go.uber.org/zap"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
var (
|
||||
// WrapperClient 包装后的数据库客户端
|
||||
WrapperClient abstract.IWrapperClient
|
||||
)
|
||||
|
||||
func init() {
|
||||
WrapperClient = NewWrapperClient()
|
||||
}
|
||||
|
||||
func NewWrapperClient() *wrapperClient {
|
||||
return &wrapperClient{
|
||||
lock: &sync.RWMutex{},
|
||||
clientTable: make(map[string]abstract.IWrapperDatabaseClient),
|
||||
}
|
||||
}
|
||||
|
||||
type wrapperClient struct {
|
||||
lock *sync.RWMutex
|
||||
clientTable map[string]abstract.IWrapperDatabaseClient
|
||||
logger *zap.Logger
|
||||
}
|
||||
|
||||
// AddWithConfigFile 使用文件生成新的客户端,文件名去掉后缀作为flag
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:19 2022/6/5
|
||||
func (c *wrapperClient) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error {
|
||||
var (
|
||||
err error
|
||||
cfg *define.CfgFile
|
||||
)
|
||||
|
||||
if cfg, err = c.getCfg(cfgFilePath); nil != err {
|
||||
return err
|
||||
}
|
||||
if nil == cfg {
|
||||
// 不支持的配置文件格式
|
||||
return nil
|
||||
}
|
||||
return c.AddWithConfig(cfg.Flag, logInstance, cfg.Config, extraFieldList)
|
||||
}
|
||||
|
||||
// AddWithConfig ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 20:41 2023/4/18
|
||||
func (c *wrapperClient) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *define.Database, extraFieldList []string) error {
|
||||
dbClient := &DBClient{
|
||||
DbFlag: flag,
|
||||
LoggerInstance: logInstance,
|
||||
ExtraFieldList: extraFieldList,
|
||||
Cfg: define.Driver{},
|
||||
}
|
||||
|
||||
if err := dbClient.Init(databaseConfig, nil); nil != err {
|
||||
return err
|
||||
}
|
||||
c.lock.Lock()
|
||||
c.clientTable[dbClient.DbFlag] = dbClient
|
||||
c.lock.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
// BatchAddWithConfigDir 自动读取目录下配置文件, 生成客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:19 2022/6/5
|
||||
func (c *wrapperClient) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error {
|
||||
filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*"))
|
||||
for i := range filepathNames {
|
||||
if err := c.AddWithConfigFile(filepathNames[i], logInstance, extraFieldList); nil != err {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getCfg 读取配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:05 2022/6/11
|
||||
func (c *wrapperClient) getCfg(cfgPath string) (*define.CfgFile, error) {
|
||||
fileArr := strings.Split(cfgPath, ".")
|
||||
if len(fileArr) < 2 {
|
||||
// 获取不到类型
|
||||
return nil, errors.New("文件格式必须是JSON或者YAML")
|
||||
}
|
||||
fileType := strings.ToLower(fileArr[len(fileArr)-1])
|
||||
fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator))
|
||||
result := &define.CfgFile{
|
||||
Path: cfgPath,
|
||||
Type: "",
|
||||
Flag: fileFlagArr[len(fileFlagArr)-1],
|
||||
Config: &define.Database{},
|
||||
}
|
||||
var (
|
||||
err error
|
||||
cfgInfo define.Database
|
||||
)
|
||||
switch fileType {
|
||||
case consts.FileTypeYaml:
|
||||
fallthrough
|
||||
case consts.FileTypeYml:
|
||||
result.Type = consts.FileTypeYaml
|
||||
if err = serialize.File.ReadYmlContent(cfgPath, &result.Config); nil != err {
|
||||
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
|
||||
}
|
||||
case consts.FileTypeJson:
|
||||
result.Type = consts.FileTypeJson
|
||||
if err = serialize.File.ReadJSONContent(cfgPath, &cfgInfo); nil != err {
|
||||
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
|
||||
}
|
||||
default:
|
||||
// 不是JSON , 也不是YML, 跳过
|
||||
return nil, nil
|
||||
}
|
||||
if len(result.Config.Master.Timezone) == 0 {
|
||||
// 默认使用本地时区
|
||||
result.Config.Master.Timezone = "Local"
|
||||
} else {
|
||||
result.Config.Slave.Timezone = result.Config.Master.Timezone
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// GetDBClient 获取db client
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:32 2022/6/5
|
||||
func (c *wrapperClient) GetDBClient(dbFlag string) (abstract.IWrapperDatabaseClient, error) {
|
||||
c.lock.RLock()
|
||||
defer c.lock.RUnlock()
|
||||
var (
|
||||
exist bool
|
||||
dbClient abstract.IWrapperDatabaseClient
|
||||
)
|
||||
if dbClient, exist = c.clientTable[dbFlag]; !exist {
|
||||
return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag)
|
||||
}
|
||||
return dbClient, nil
|
||||
}
|
||||
|
||||
// GetMasterClient 获取主库客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:36 2022/6/5
|
||||
func (c *wrapperClient) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
|
||||
var (
|
||||
err error
|
||||
dbClient abstract.IWrapperDatabaseClient
|
||||
)
|
||||
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbClient.GetMaster(ctx), nil
|
||||
}
|
||||
|
||||
// GetSlaveClient 获取从库客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 19:37 2022/6/5
|
||||
func (c *wrapperClient) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
|
||||
var (
|
||||
err error
|
||||
dbClient abstract.IWrapperDatabaseClient
|
||||
)
|
||||
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbClient.GetSlave(ctx), nil
|
||||
}
|
267
wrapper_db_client.go
Normal file
267
wrapper_db_client.go
Normal file
@ -0,0 +1,267 @@
|
||||
// Package database ...
|
||||
//
|
||||
// Description : define ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 2024-08-20 17:36
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"git.zhangdeman.cn/zhangdeman/consts"
|
||||
"git.zhangdeman.cn/zhangdeman/database/define"
|
||||
"git.zhangdeman.cn/zhangdeman/logger/wrapper"
|
||||
"go.uber.org/zap"
|
||||
"gorm.io/driver/mysql"
|
||||
"gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
gormLogger "gorm.io/gorm/logger"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DBClient 包装日志实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:09 PM 2021/12/24
|
||||
type DBClient struct {
|
||||
DbFlag string // 数据库标识
|
||||
LoggerInstance *zap.Logger // 日志实例
|
||||
master *gorm.DB // 主库
|
||||
slave *gorm.DB // 从库
|
||||
ExtraFieldList []string // 提取的字段
|
||||
Cfg define.Driver // 数据库配置
|
||||
cacheTableStructureConfig *define.CacheTableStructureConfig // 缓存配置
|
||||
lock *sync.RWMutex // 操作锁
|
||||
tableStructureCache map[string][]*define.ColumnConfig // 表结构缓存
|
||||
}
|
||||
|
||||
// Init 初始化客户端
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:44 2024/8/20
|
||||
func (dc *DBClient) Init(databaseConfig *define.Database, cacheTableStructureConfig *define.CacheTableStructureConfig) error {
|
||||
dc.lock = &sync.RWMutex{}
|
||||
dc.cacheTableStructureConfig = cacheTableStructureConfig
|
||||
var err error
|
||||
if dc.master, err = dc.GetDatabaseClient(databaseConfig.Master, dc.LoggerInstance); nil != err {
|
||||
return err
|
||||
}
|
||||
if dc.slave, err = dc.GetDatabaseClient(databaseConfig.Slave, dc.LoggerInstance); nil != err {
|
||||
return err
|
||||
}
|
||||
if err = dc.syncDbTableStructure(false); nil != err { // 同步缓存表结构
|
||||
return err
|
||||
}
|
||||
// 启动异步任务
|
||||
go func() {
|
||||
if !dc.CacheDataTableStructureConfig().Enable {
|
||||
// 未启用
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-time.After(time.Second * time.Duration(dc.CacheDataTableStructureConfig().SyncTimeInterval)):
|
||||
_ = dc.syncDbTableStructure(true)
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetMaster 获取主库连接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:28 PM 2021/12/24
|
||||
func (dc *DBClient) GetMaster(ctx context.Context) *gorm.DB {
|
||||
session := dc.master.Session(&gorm.Session{})
|
||||
session.Logger = dc.getLogger(ctx, session, "master")
|
||||
return session
|
||||
}
|
||||
|
||||
// GetSlave 获取从库链接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:29 PM 2021/12/24
|
||||
func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB {
|
||||
session := dc.slave.Session(&gorm.Session{})
|
||||
session.Logger = dc.getLogger(ctx, session, "slave")
|
||||
return session
|
||||
}
|
||||
|
||||
// getLogger 获取日志实例
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 3:45 PM 2021/12/24
|
||||
func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node string) gormLogger.Interface {
|
||||
return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.LoggerInstance, dc.DbFlag+"|"+node, dc.ExtraFieldList)
|
||||
}
|
||||
|
||||
// GetDatabaseClient 获取数据库连接
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:41 2022/6/11
|
||||
func (dc *DBClient) GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error) {
|
||||
var (
|
||||
instance *gorm.DB
|
||||
err error
|
||||
)
|
||||
|
||||
if nil == logInstance {
|
||||
logInstance = dc.LoggerInstance
|
||||
}
|
||||
|
||||
if conf.DBType == consts.DatabaseDriverMysql {
|
||||
if instance, err = gorm.Open(mysql.Open(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else if conf.DBType == consts.DatabaseDriverSqlite3 {
|
||||
if instance, err = gorm.Open(sqlite.Open(dc.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
return nil, fmt.Errorf("%v : db driver is not support", conf.DBType)
|
||||
}
|
||||
|
||||
instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil)
|
||||
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// buildConnectionDSN 构建连接信息
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 18:42 2022/6/11
|
||||
func (dc *DBClient) buildConnectionDSN(conf *define.Driver) string {
|
||||
if conf.DBType == consts.DatabaseDriverSqlite3 {
|
||||
// 兼容sqlite3
|
||||
return conf.Host
|
||||
}
|
||||
return fmt.Sprintf(
|
||||
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s",
|
||||
conf.Username,
|
||||
conf.Password,
|
||||
conf.Host,
|
||||
conf.Port,
|
||||
conf.Database,
|
||||
conf.Charset,
|
||||
conf.Timezone,
|
||||
)
|
||||
}
|
||||
|
||||
// CacheDataTableStructureConfig ...
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:03 2024/8/21
|
||||
func (dc *DBClient) CacheDataTableStructureConfig() *define.CacheTableStructureConfig {
|
||||
if nil == dc.cacheTableStructureConfig {
|
||||
return &define.CacheTableStructureConfig{Enable: false, SyncTimeInterval: 3600}
|
||||
}
|
||||
if dc.cacheTableStructureConfig.SyncTimeInterval <= 0 {
|
||||
dc.cacheTableStructureConfig.SyncTimeInterval = 3600
|
||||
}
|
||||
return dc.cacheTableStructureConfig
|
||||
}
|
||||
|
||||
// GetTableFieldList 获取表结构配置
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:07 2024/8/21
|
||||
func (dc *DBClient) GetTableFieldList(tableName string) ([]*define.ColumnConfig, error) {
|
||||
if !dc.CacheDataTableStructureConfig().Enable {
|
||||
// 未启用缓存, 返回空list
|
||||
return make([]*define.ColumnConfig, 0), nil
|
||||
}
|
||||
dc.lock.RLock()
|
||||
defer dc.lock.RUnlock()
|
||||
if _, exist := dc.tableStructureCache[tableName]; !exist {
|
||||
return nil, errors.New(tableName + " : cache result not found")
|
||||
}
|
||||
return dc.tableStructureCache[tableName], nil
|
||||
}
|
||||
|
||||
// syncDbTableStructure 缓存表结构
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 15:17 2024/8/21
|
||||
func (dc *DBClient) syncDbTableStructure(ignoreErr bool) error {
|
||||
if !dc.CacheDataTableStructureConfig().Enable {
|
||||
// 自动同步不可用
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
err error
|
||||
tableList []string
|
||||
)
|
||||
|
||||
c := dc.GetMaster(context.Background())
|
||||
systemDao := &SystemDao{}
|
||||
if tableList, err = systemDao.GetTableList(c); nil != err {
|
||||
return err
|
||||
}
|
||||
tableStructCache := make(map[string][]*define.ColumnConfig)
|
||||
for _, itemTableName := range tableList {
|
||||
fieldList, loadTableErr := systemDao.GetTableInfo(c, dc.Cfg.Database, itemTableName)
|
||||
if nil != loadTableErr {
|
||||
if ignoreErr {
|
||||
continue
|
||||
}
|
||||
return loadTableErr
|
||||
}
|
||||
tableStructCache[itemTableName] = make([]*define.ColumnConfig, 0)
|
||||
for _, itemColumn := range fieldList {
|
||||
fieldType := "string"
|
||||
if strings.Contains(itemColumn.ColumnType, "int") {
|
||||
if strings.Contains(itemColumn.ColumnType, "unsigned") {
|
||||
fieldType = "uint"
|
||||
} else {
|
||||
fieldType = "int"
|
||||
}
|
||||
}
|
||||
tableStructCache[itemTableName] = append(tableStructCache[itemTableName], &define.ColumnConfig{
|
||||
Column: itemColumn.ColumnName,
|
||||
Alias: itemColumn.ColumnName,
|
||||
Type: fieldType,
|
||||
})
|
||||
}
|
||||
}
|
||||
// 更新缓存结果
|
||||
dc.lock.Lock()
|
||||
defer dc.lock.Unlock()
|
||||
dc.tableStructureCache = tableStructCache
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetTableStructure 设置表结构, 一旦调用人工设置, 则将终止自动同步
|
||||
//
|
||||
// Author : go_developer@163.com<白茶清欢>
|
||||
//
|
||||
// Date : 17:06 2024/8/23
|
||||
func (dc *DBClient) SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig) {
|
||||
if nil != dc.cacheTableStructureConfig {
|
||||
// 关闭自动同步
|
||||
dc.cacheTableStructureConfig.Enable = false
|
||||
}
|
||||
dc.lock.Lock()
|
||||
if nil == dc.tableStructureCache {
|
||||
dc.tableStructureCache = make(map[string][]*define.ColumnConfig)
|
||||
}
|
||||
for table, columnConfig := range tableConfigTable {
|
||||
dc.tableStructureCache[table] = columnConfig
|
||||
}
|
||||
dc.lock.Unlock()
|
||||
}
|
Reference in New Issue
Block a user