Compare commits

...

27 Commits

Author SHA1 Message Date
e0b984e2cf Merge pull request 'api2sql基础的能力' (#8) from feature/api2sql into master
Reviewed-on: #8
2024-08-24 12:33:47 +08:00
6c8fd23f1a 调通 list 能力, 别名问题待解决 2024-08-23 18:47:04 +08:00
d2e96ecfe6 修复结构体生成的BUG 2024-08-23 18:23:37 +08:00
2be1b29234 fix NPE 2024-08-23 18:17:31 +08:00
c6b8d29b61 修复代码BUG + 循环引用 2024-08-23 17:54:48 +08:00
f359598109 支持人工设置表结构配置 2024-08-23 17:29:11 +08:00
5ed8f2007e 增加list 查询 - 待测试 2024-08-23 16:50:47 +08:00
1071c7558e 优化 or 语句 + 支持 between / not between 2024-08-23 16:17:49 +08:00
15b1d9e6b0 增加 WithAnyCondition 2024-08-23 15:22:44 +08:00
e5233b4fae 增加获取数据库连接的方法 2024-08-23 12:26:03 +08:00
21ad50b273 update input param 2024-08-23 10:59:20 +08:00
c586fa5c4f 增加 sql 模板定义 2024-08-22 12:05:46 +08:00
a4d887ecb1 输入参数增加强制读主选项 2024-08-22 11:53:49 +08:00
352fd61f92 代码优化 2024-08-22 11:51:02 +08:00
bc99c08d55 增加InputParam验证 2024-08-22 11:46:00 +08:00
ce189a9067 save 2024-08-21 21:11:19 +08:00
3b1e7c7232 输入参数增加数据库标识 2024-08-21 21:01:39 +08:00
334472815c 规划api2sql执行 2024-08-21 21:00:11 +08:00
128bf9e062 update go mod + 支持配置表字段对外输出的名字 2024-08-21 18:23:55 +08:00
7a87758a4e update consts 2024-08-21 16:35:46 +08:00
8a0efca6ee 规划支持 InputSql 2024-08-21 16:30:56 +08:00
4ee868a059 规划Api2SqlParam输入参数的结构 2024-08-21 16:25:59 +08:00
0098405793 升级 wrapper_db_client, 支持表结构缓存以及定时同步 2024-08-21 16:01:48 +08:00
c854f0ebeb db client 2024-08-20 18:15:59 +08:00
4ba14dc23d db_client 面向接口实现 2024-08-20 18:14:57 +08:00
5e87706d0a wrapper_client 面向接口实现 2024-08-20 17:56:43 +08:00
5024ae4238 迁移结构体面向接口实现 - 保存代码 2024-08-20 17:42:36 +08:00
19 changed files with 1299 additions and 440 deletions

View 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)
}

View File

@ -8,7 +8,7 @@
package abstract
import (
"git.zhangdeman.cn/zhangdeman/database"
"git.zhangdeman.cn/zhangdeman/database/define"
"gorm.io/gorm"
)
@ -19,21 +19,21 @@ import (
// Date : 15:06 2023/10/14
type IDatabase interface {
// Create 创建数据
Create(dbInstance *gorm.DB, data any, optionList ...database.SetOption) error
Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error
// Update 更新数据
Update(dbInstance *gorm.DB, updateData any, optionFuncList ...database.SetOption) (int64, error)
Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error)
// UpdateOne 更新一条数据
UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...database.SetOption) (int64, error)
UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error)
// List 查询数据列表
List(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error
List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error
// Delete 删除数据
Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...database.SetOption) (int64, error)
Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...define.SetOption) (int64, error)
// Detail 数据详情
Detail(dbInstance *gorm.DB, result any, optionFuncList ...database.SetOption) error
Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error
// IsNotFound 错误是否为数据不存在
IsNotFound(err error) bool
// Count 查询数据数量
Count(dbInstance *gorm.DB, optionFuncList ...database.SetOption) (int64, error)
Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error)
// Tx 执行事务
Tx(dbInstance *gorm.DB, txFunc func(dbInstance *gorm.DB) error) error
// Begin 开启事务

View 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)
}

View File

@ -0,0 +1,37 @@
// Package abstract ...
//
// Description : abstract ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-08-20 17:59
package abstract
import (
"context"
"git.zhangdeman.cn/zhangdeman/database/define"
"go.uber.org/zap"
"gorm.io/gorm"
)
// IWrapperDatabaseClient 包装gorm
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:00 2024/8/20
type IWrapperDatabaseClient interface {
// Init 初始化客户端连接
Init(databaseConfig *define.Database, cacheTableStructureConfig *define.CacheTableStructureConfig) error
// GetMaster 获取master连接
GetMaster(ctx context.Context) *gorm.DB
// GetSlave 获取slave连接
GetSlave(ctx context.Context) *gorm.DB
// GetDatabaseClient 获取数据库连接
GetDatabaseClient(conf *define.Driver, logInstance *zap.Logger) (*gorm.DB, error)
// CacheDataTableStructureConfig 缓存数据表结构的配置
CacheDataTableStructureConfig() *define.CacheTableStructureConfig
// GetTableFieldList 获取指定表数据字段列表
GetTableFieldList(tableName string) ([]*define.ColumnConfig, error)
// SetTableStructure 设置数据表结构
SetTableStructure(tableConfigTable map[string][]*define.ColumnConfig)
}

281
api2sql/execute.go Normal file
View File

@ -0,0 +1,281 @@
// Package api2sql ...
//
// Description : api2sql ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-08-21 20:45
package api2sql
import (
"context"
"errors"
"fmt"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/database"
"git.zhangdeman.cn/zhangdeman/database/abstract"
"git.zhangdeman.cn/zhangdeman/database/define"
"git.zhangdeman.cn/zhangdeman/wrapper"
"gorm.io/gorm"
)
var (
Exec = &execute{}
)
type execute struct {
databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例
baseDao database.BaseDao // 基础dao
}
// SetDatabaseClientManager 设置数据库连接管理实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 21:06 2024/8/21
func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapperClient) {
e.databaseClientManager = databaseClientManager
}
// Run 执行
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:48 2024/8/21
func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
if err := e.formatAndValidateInputParam(inputParam); nil != err {
return nil, err
}
if len(inputParam.InputSql) > 0 {
// 基于表达式执行
return e.Express(ctx, inputParam)
}
switch inputParam.SqlType {
case consts.SqlTypeCount:
return e.Count(ctx, inputParam)
case consts.SqlTypeDetail:
return e.Detail(ctx, inputParam)
case consts.SqlTypeList:
return e.List(ctx, inputParam)
case consts.SqlTypeInsert:
return e.Insert(ctx, inputParam)
case consts.SqlTypeUpdate:
return e.Update(ctx, inputParam)
case consts.SqlTypeDelete:
return e.Delete(ctx, inputParam)
default:
return nil, errors.New(inputParam.SqlType + " : sql type is not support")
}
}
// List 列表
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2024/8/21
func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
var (
err error
tx *gorm.DB
optionList []define.SetOption
)
if tx, err = e.getTx(ctx, inputParam); nil != err {
return nil, err
}
if optionList, err = e.getOptionList(ctx, inputParam); nil != err {
return nil, err
}
// 动态生成结果解析结构体
st := wrapper.NewDynamic()
for _, columnConfig := range inputParam.ColumnList {
tag := fmt.Sprintf(`gorm:"%v" json:"%v"`, columnConfig.Column, columnConfig.Alias)
column := wrapper.String(columnConfig.Column).SnakeCaseToCamel()
switch columnConfig.Type {
case "int", "int8", "int16", "int32", "int64":
st.AddInt(column, tag, "")
case "uint", "uint8", "uint16", "uint32", "uint64":
st.AddUint(column, tag, "")
case "bool":
st.AddBool(column, tag, "")
case "float":
st.AddBool(column, tag, "")
case "string":
st.AddString(column, tag, "")
case "map":
st.AddMap(column, tag, "")
case "slice":
st.AddSlice(column, tag, "")
}
}
val := st.ToStructDefaultSliceValue()
if err = e.baseDao.List(tx, &val, optionList...); nil != err {
return nil, err
}
return val, nil
}
// Detail 详情
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2024/8/21
func (e *execute) Detail(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
return nil, nil
}
// Insert 插入
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2024/8/21
func (e *execute) Insert(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
return nil, nil
}
// Update 更新
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2024/8/21
func (e *execute) Update(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
return nil, nil
}
// Count 数量
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:52 2024/8/21
func (e *execute) Count(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
return nil, nil
}
// Delete 删除
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:51 2024/8/21
func (e *execute) Delete(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
return nil, nil
}
// Express 基于表达式执行
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:51 2024/8/21
func (e *execute) Express(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
if nil == e.databaseClientManager {
return nil, errors.New("database client is nil, please use `SetDatabaseClientManager` set instance")
}
// 格式化 inputParam
return nil, nil
}
// formatAndValidateInputParam 格式化并校验输入参数
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 10:38 2024/8/22
func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) error {
if nil == inputParam {
return errors.New("inputParam is nil")
}
if len(inputParam.DatabaseFlag) == 0 {
return errors.New("databaseFlag is empty")
}
if len(inputParam.Table) == 0 {
return errors.New("table is empty")
}
if len(inputParam.SqlType) == 0 {
return errors.New("sqlType is empty")
}
databaseClient, err := e.databaseClientManager.GetDBClient(inputParam.DatabaseFlag)
if nil != err {
return errors.New(inputParam.DatabaseFlag + " : database client get fail -> " + err.Error())
}
// 尝试获取表结构, api 转 sql 要求必须开启表结构缓存, 否则无法确定相关数据如何解析
if inputParam.TableColumnConfig, err = databaseClient.GetTableFieldList(inputParam.Table); nil != err {
return errors.New(inputParam.DatabaseFlag + " : get table field list fail -> " + err.Error())
}
if len(inputParam.TableColumnConfig) == 0 {
return errors.New(inputParam.DatabaseFlag + " : table field list is empty, please enable `CacheDataTableStructureConfig` or `SetTableColumnConfig`")
}
// 操作字段列表为空
if err = e.validateColumn(inputParam); nil != err {
return err
}
// 验证 force no limit
if inputParam.ForceNoLimit {
inputParam.Limit = 0
inputParam.WithCount = false
}
return nil
}
// validateColumn 验证表字段相关
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:48 2024/8/22
func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error {
if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{
consts.SqlTypeList, consts.SqlTypeDetail,
}).Has(inputParam.SqlType) >= 0 {
return errors.New("column list is empty")
}
// 验证字段是否都正确
tableColumnTable := make(map[string]bool)
for _, itemColumn := range inputParam.TableColumnConfig {
tableColumnTable[itemColumn.Column] = true
}
for _, columnConfig := range inputParam.ColumnList {
if !tableColumnTable[columnConfig.Column] {
return errors.New(columnConfig.Column + " : input column not found in table column list")
}
if len(columnConfig.Alias) == 0 {
columnConfig.Alias = columnConfig.Column
}
}
return nil
}
// getTx 获取数据库连接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:20 2024/8/23
func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (*gorm.DB, error) {
if nil != inputParam.Tx {
return inputParam.Tx, nil
}
if inputParam.ForceMaster || // 强制操作主库
wrapper.ArrayType[string]([]string{consts.SqlTypeDelete, consts.SqlTypeInsert, consts.SqlTypeUpdate}).Has(inputParam.SqlType) >= 0 { // 写操作
return e.databaseClientManager.GetMasterClient(ctx, inputParam.DatabaseFlag)
}
return e.databaseClientManager.GetSlaveClient(ctx, inputParam.DatabaseFlag)
}
// getOptionList 设置where条件
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:31 2024/8/23
func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]define.SetOption, error) {
optionList := []define.SetOption{
database.WithTable(inputParam.Table),
}
// 设置 limit offset
if !inputParam.ForceNoLimit && inputParam.Limit > 0 {
optionList = append(optionList, database.WithLimit(inputParam.Limit, inputParam.Offset))
}
for _, item := range inputParam.ConditionList {
optionFunc, err := database.WithAnyCondition(item.Column, item.Operate, item.Value)
if nil != err {
return nil, err
}
optionList = append(optionList, optionFunc)
}
return optionList, nil
}

74
api2sql/execute_test.go Normal file
View File

@ -0,0 +1,74 @@
// Package api2sql ...
//
// Description : api2sql ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-08-23 17:36
package api2sql
import (
"context"
"encoding/json"
"fmt"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/database"
"git.zhangdeman.cn/zhangdeman/database/define"
"reflect"
"testing"
)
func Test_execute_Run(t *testing.T) {
clientManager := database.NewWrapperClient()
if err := clientManager.AddWithConfig("TEST_DATABASE", nil, &define.Database{
Master: &define.Driver{
DBType: "sqlite3",
Host: "/tmp/gateway.db",
},
Slave: &define.Driver{
DBType: "sqlite3",
Host: "/tmp/gateway.db",
},
}, []string{}); nil != err {
panic(err.Error())
}
dbClient, _ := clientManager.GetDBClient("TEST_DATABASE")
dbClient.SetTableStructure(map[string][]*define.ColumnConfig{
"project": []*define.ColumnConfig{
&define.ColumnConfig{
Column: "flag",
Alias: "project_flag",
Type: "string",
},
},
})
Exec.SetDatabaseClientManager(clientManager)
res, err := Exec.List(context.Background(), &define.Api2SqlParam{
DatabaseFlag: "TEST_DATABASE",
Table: "project",
ForceMaster: false,
InputSql: "",
TableSplitConfig: define.TableSplitConfig{},
SqlType: consts.SqlTypeList,
ColumnList: []*define.ColumnConfig{
&define.ColumnConfig{
Column: "flag",
Alias: "project_flag",
Type: "string",
},
},
Limit: 0,
Offset: 0,
ForceNoLimit: false,
OrderField: "id",
OrderRule: "desc",
WithCount: false,
ConditionList: nil,
TableColumnConfig: nil,
Tx: nil,
})
byteData, _ := json.Marshal(res)
tt := reflect.TypeOf(res)
fmt.Println(tt.String(), res, err, string(byteData))
}

90
base.go
View File

@ -7,6 +7,7 @@ package database
import (
"errors"
"git.zhangdeman.cn/zhangdeman/database/define"
"gorm.io/gorm"
)
@ -25,8 +26,8 @@ type BaseDao struct {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:06 下午 2021/8/8
func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption) error {
o := &Option{}
func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...define.SetOption) error {
o := &define.Option{}
for _, itemFunc := range optionList {
itemFunc(o)
}
@ -42,7 +43,7 @@ func (b *BaseDao) Create(dbInstance *gorm.DB, data any, optionList ...SetOption)
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:12 下午 2021/8/8
func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...SetOption) (int64, error) {
func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) {
dbInstance = b.setTxCondition(dbInstance, optionFuncList...)
r := dbInstance.Updates(updateData)
return r.RowsAffected, r.Error
@ -53,7 +54,7 @@ func (b *BaseDao) Update(dbInstance *gorm.DB, updateData any, optionFuncList ...
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:05 2024/1/13
func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...SetOption) (int64, error) {
func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList ...define.SetOption) (int64, error) {
optionFuncList = append(optionFuncList, WithLimit(1, 0))
return b.Update(dbInstance, updateData, optionFuncList...)
}
@ -63,7 +64,7 @@ func (b *BaseDao) UpdateOne(dbInstance *gorm.DB, updateData any, optionFuncList
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:14 下午 2021/8/8
func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...SetOption) error {
func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error {
dbInstance = b.setTxCondition(dbInstance, optionFuncList...)
return dbInstance.Find(result).Error
}
@ -73,7 +74,7 @@ func (b *BaseDao) List(dbInstance *gorm.DB, result any, optionFuncList ...SetOpt
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:46 2023/2/9
func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...SetOption) (int64, error) {
func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...define.SetOption) (int64, error) {
dbInstance = dbInstance.Model(dataModel)
dbInstance = b.setTxCondition(dbInstance, optionFuncList...).Delete(dataModel)
return dbInstance.RowsAffected, dbInstance.Error
@ -84,7 +85,7 @@ func (b *BaseDao) Delete(dbInstance *gorm.DB, dataModel any, optionFuncList ...S
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:25 下午 2021/8/8
func (b *BaseDao) Detail(dbInstance *gorm.DB, result any, optionFuncList ...SetOption) error {
func (b *BaseDao) Detail(dbInstance *gorm.DB, result any, optionFuncList ...define.SetOption) error {
dbInstance = b.setTxCondition(dbInstance, optionFuncList...)
return dbInstance.First(result).Error
}
@ -118,7 +119,7 @@ func (b *BaseDao) IsNotFound(err error) bool {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:25 下午 2021/8/8
func (b *BaseDao) Count(dbInstance *gorm.DB, optionFuncList ...SetOption) (int64, error) {
func (b *BaseDao) Count(dbInstance *gorm.DB, optionFuncList ...define.SetOption) (int64, error) {
dbInstance = b.setTxCondition(dbInstance, optionFuncList...)
var cnt int64
return cnt, dbInstance.Count(&cnt).Error
@ -173,10 +174,10 @@ func (b *BaseDao) Rollback(db *gorm.DB) error {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:38 2022/5/15
func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm.DB {
func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...define.SetOption) *gorm.DB {
// 构建查询条件
o := &Option{}
o := &define.Option{}
for _, fn := range optionFuncList {
fn(o)
}
@ -199,7 +200,72 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
tx = tx.Offset(o.Offset)
}
}
sqlBlockList := make([]string, 0)
// in 语句
if nil != o.In {
tx = tx.Where(o.In)
}
// not in 语句
if nil != o.NotIn {
for field, value := range o.NotIn {
tx = tx.Where(field+" NOT IN ? ", value)
}
}
// like 语句
if nil != o.Like {
for field, value := range o.Like {
tx = tx.Where(field+" LIKE ? ", "%"+value+"%")
}
}
// NOT LIKE 语句
if nil != o.NotLike {
for field, value := range o.NotLike {
tx = tx.Where(field+" NOT LIKE ? ", "%"+value+"%")
}
}
// >=
if nil != o.Start {
for field, value := range o.Start {
tx = tx.Where(field+" >= ?", value)
}
}
// <
if nil != o.End {
for field, value := range o.End {
tx = tx.Where(field+" < ?", value)
}
}
// between
for field, betweenVal := range o.Between {
tx = tx.Where("`"+field+"` BETWEEN ? AND ?", betweenVal[0], betweenVal[1])
}
// not between
for field, notBetweenVal := range o.NotBetween {
tx = tx.Where("`"+field+"` NOT BETWEEN ? AND ?", notBetweenVal[0], notBetweenVal[1])
}
// 排序
for _, orderRule := range o.Order {
tx = tx.Order(orderRule)
}
// or 语句
if nil != o.OR {
for _, itemOr := range o.OR {
orOption := &define.Option{}
for _, fn := range itemOr {
fn(orOption)
}
orSql, orBindVal := optionToSql(orOption)
tx.Or(orSql, orBindVal)
}
}
/*sqlBlockList := make([]string, 0)
bindValueList := make([]any, 0)
sql, bindVal := optionToSql(o)
tx.Or(sql, bindVal...)
@ -212,6 +278,6 @@ func (b *BaseDao) setTxCondition(tx *gorm.DB, optionFuncList ...SetOption) *gorm
}
orSql, orBindVal := optionToSql(orOption)
tx.Or(orSql, orBindVal...)
}
}*/
return tx
}

327
client.go
View File

@ -1,327 +0,0 @@
// Package database ...
//
// Description : mysql客户端
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2021-03-01 9:20 下午
package database
import (
"context"
"errors"
"fmt"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/serialize"
"path/filepath"
"strings"
"sync"
"git.zhangdeman.cn/zhangdeman/logger/wrapper"
"go.uber.org/zap"
gormLogger "gorm.io/gorm/logger"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
var (
// Client mysql客户端
Client *client
)
func init() {
Client = &client{
lock: &sync.RWMutex{},
clientTable: make(map[string]*DBClient),
}
}
type client struct {
lock *sync.RWMutex
clientTable map[string]*DBClient
logger *zap.Logger
}
// AddWithConfigFile 使用文件生成新的客户端文件名去掉后缀作为flag
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:19 2022/6/5
func (c *client) AddWithConfigFile(cfgFilePath string, logInstance *zap.Logger, extraFieldList []string) error {
var (
err error
cfg *cfgFile
)
if cfg, err = c.getCfg(cfgFilePath); nil != err {
return err
}
if nil == cfg {
// 不支持的配置文件格式
return nil
}
return c.AddWithConfig(cfg.Flag, logInstance, cfg.Config, extraFieldList)
}
// AddWithConfig ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:41 2023/4/18
func (c *client) AddWithConfig(flag string, logInstance *zap.Logger, databaseConfig *Database, extraFieldList []string) error {
dbClient := &DBClient{
dbFlag: flag,
loggerInstance: logInstance,
master: nil,
slave: nil,
extraFieldList: extraFieldList,
cfg: Driver{},
}
var err error
if dbClient.master, err = c.GetDatabaseClient(databaseConfig.Master, logInstance); nil != err {
return err
}
if dbClient.slave, err = c.GetDatabaseClient(databaseConfig.Slave, logInstance); nil != err {
return err
}
c.lock.Lock()
c.clientTable[dbClient.dbFlag] = dbClient
c.lock.Unlock()
return nil
}
// BatchAddWithConfigDir 自动读取目录下配置文件, 生成客户端
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:19 2022/6/5
func (c *client) BatchAddWithConfigDir(cfgDir string, logInstance *zap.Logger, extraFieldList []string) error {
filepathNames, _ := filepath.Glob(filepath.Join(cfgDir, "*"))
for i := range filepathNames {
if err := c.AddWithConfigFile(filepathNames[i], logInstance, extraFieldList); nil != err {
return err
}
}
return nil
}
// getCfg 读取配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:05 2022/6/11
func (c *client) getCfg(cfgPath string) (*cfgFile, error) {
fileArr := strings.Split(cfgPath, ".")
if len(fileArr) < 2 {
// 获取不到类型
return nil, errors.New("文件格式必须是JSON或者YAML")
}
fileType := strings.ToLower(fileArr[len(fileArr)-1])
fileFlagArr := strings.Split(fileArr[0], string(filepath.Separator))
result := &cfgFile{
Path: cfgPath,
Type: "",
Flag: fileFlagArr[len(fileFlagArr)-1],
Config: &Database{},
}
var (
err error
cfgInfo Database
)
switch fileType {
case consts.FileTypeYaml:
fallthrough
case consts.FileTypeYml:
result.Type = consts.FileTypeYaml
if err = serialize.File.ReadYmlContent(cfgPath, &result.Config); nil != err {
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
}
case consts.FileTypeJson:
result.Type = consts.FileTypeJson
if err = serialize.File.ReadJSONContent(cfgPath, &cfgInfo); nil != err {
return nil, fmt.Errorf("%s 配置文件解析失败, 原因 : %s", cfgPath, err.Error())
}
default:
// 不是JSON , 也不是YML, 跳过
return nil, nil
}
if len(result.Config.Master.Timezone) == 0 {
// 默认使用本地时区
result.Config.Master.Timezone = "Local"
} else {
result.Config.Slave.Timezone = result.Config.Master.Timezone
}
return result, nil
}
// GetDBClient 获取db client
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:32 2022/6/5
func (c *client) GetDBClient(dbFlag string) (*DBClient, error) {
c.lock.RLock()
defer c.lock.RUnlock()
var (
exist bool
dbClient *DBClient
)
if dbClient, exist = c.clientTable[dbFlag]; !exist {
return nil, fmt.Errorf("%s 标识的数据库实例不存在! ", dbFlag)
}
return dbClient, nil
}
// GetMasterClient 获取主库客户端
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:36 2022/6/5
func (c *client) GetMasterClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
var (
err error
dbClient *DBClient
)
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
return nil, err
}
session := dbClient.master.Session(&gorm.Session{})
session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-master")
return session, nil
}
// GetSlaveClient 获取从库客户端
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 19:37 2022/6/5
func (c *client) GetSlaveClient(ctx context.Context, dbFlag string) (*gorm.DB, error) {
var (
err error
dbClient *DBClient
)
if dbClient, err = c.GetDBClient(dbFlag); nil != err {
return nil, err
}
session := dbClient.slave.Session(&gorm.Session{})
session.Logger = dbClient.getLogger(ctx, session, dbFlag+"-slave")
return session, nil
}
// getGormClient 获取GORM client方法
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:24 2022/6/6
func (c *client) getGormClient() (*gorm.DB, error) {
return nil, nil
}
// GetDatabaseClient 获取数据库连接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:41 2022/6/11
func (c *client) GetDatabaseClient(conf *Driver, logInstance *zap.Logger) (*gorm.DB, error) {
var (
instance *gorm.DB
err error
)
if conf.DBType == consts.DatabaseDriverMysql {
if instance, err = gorm.Open(mysql.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
return nil, err
}
} else if conf.DBType == consts.DatabaseDriverSqlite3 {
if instance, err = gorm.Open(sqlite.Open(c.buildConnectionDSN(conf)), &gorm.Config{}); nil != err {
return nil, err
}
} else {
return nil, fmt.Errorf("%v : db driver is not support", conf.DBType)
}
instance.Logger = wrapper.NewGormLoggerWithInstance(nil, instance, logInstance, "", nil)
return instance, nil
}
// buildConnectionDSN 构建连接信息
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 18:42 2022/6/11
func (c *client) buildConnectionDSN(conf *Driver) string {
if conf.DBType == consts.DatabaseDriverSqlite3 {
// 兼容sqlite3
return conf.Host
}
return fmt.Sprintf(
"%s:%s@tcp(%s:%d)/%s?charset=%s&parseTime=True&loc=%s",
conf.Username,
conf.Password,
conf.Host,
conf.Port,
conf.Database,
conf.Charset,
conf.Timezone,
)
}
// DBClient 包装日志实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:09 PM 2021/12/24
type DBClient struct {
dbFlag string // 数据库标识
loggerInstance *zap.Logger // 日志实例
master *gorm.DB // 主库
slave *gorm.DB // 从库
extraFieldList []string // 提取的字段
cfg Driver // 数据库配置
}
// SetFlag 设置数据库标识
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:18 2022/6/5
func (dc *DBClient) SetFlag(dbFlag string) {
dc.dbFlag = dbFlag
}
// GetMaster 获取主库连接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:28 PM 2021/12/24
func (dc *DBClient) GetMaster(ctx context.Context) *gorm.DB {
session := dc.master.Session(&gorm.Session{})
session.Logger = dc.getLogger(ctx, session, "slave")
return session
}
// GetSlave 获取从库链接
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:29 PM 2021/12/24
func (dc *DBClient) GetSlave(ctx context.Context) *gorm.DB {
session := dc.slave.Session(&gorm.Session{})
session.Logger = dc.getLogger(ctx, session, "slave")
return session
}
// getLogger 获取日志实例
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 3:45 PM 2021/12/24
func (dc *DBClient) getLogger(ctx context.Context, dbClient *gorm.DB, node string) gormLogger.Interface {
return wrapper.NewGormLoggerWithInstance(ctx, dbClient, dc.loggerInstance, dc.dbFlag+"|"+node, dc.extraFieldList)
}

72
define/api2sql.go Normal file
View File

@ -0,0 +1,72 @@
// Package define ...
//
// Description : define ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-08-21 16:05
package define
import (
"gorm.io/gorm"
)
// Api2SqlParam 接口转sql的输入配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:06 2024/8/21
//
// 一个合法的 sql 配置格式 :
//
// select 语句 : SELECT {FIELD_LIST} FROM {TABLE} WHERE {WHERE} ORDER BY {ORDER_FIELD} {ORDER_RULE} LIMIT {LIMIT} OFFSET {OFFSET}
//
// insert 语句 : INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST})
//
// update 语句 : UPDATE {TABLE} SET {SET} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE}
//
// delete 语句 : DELETE FROM {TABLE} WHERE {WHERE} LIMIT {LIMIT} OFFSET {OFFSET} ORDER BY {ORDER_FIELD} {ORDER_RULE}
//
// count 语句 : SELECT COUNT(*) as count FROM {TABLE} WHERE {WHERE}
type Api2SqlParam struct {
DatabaseFlag string `json:"database_flag"` // 数据库标识
Table string `json:"table"` // 操作的数据表
ForceMaster bool `json:"force_master"` // 针对查询语句, 是否强制读主, 如果查询语句在事务中, 默认强制读主
InputSql string `json:"input_sql"` // 输入的sql模板, 仅依赖 ValueList 解析字段值, 依赖 split 相关解析分表配置
TableSplitConfig TableSplitConfig `json:"table_split_config"` // 分表配置
SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除
ColumnList []*ColumnConfig `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出
Limit int `json:"limit"` // 操作数量
Offset int `json:"offset"` // 操作偏移量
ForceNoLimit bool `json:"force_no_limit"` // 强制允许不限制 : 正常操作 select / delete / update 均需要指定本次操作数据量, 如果确定不限制, 此参数设置为 `true` , sqlType = list , 且 ForceNoLimit = true 时, WithCount 参数无效
OrderField string `json:"order_field"` // 排序字段, 仅 sqlType = list 生效
OrderRule string `json:"order_rule"` // 排序规则, Asc / Desc
WithCount bool `json:"with_count"` // 是否返回数据总量, 仅 sqlType = list 生效
ConditionList []SqlCondition `json:"value_list"` // 字段列表
TableColumnConfig []*ColumnConfig `json:"table_column_config"` // 表字段配置
Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用
}
// TableSplitConfig 分表配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:42 2024/8/23
type TableSplitConfig struct {
IsSplit bool `json:"is_split"` // 是否分表
SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在
FieldValue any `json:"field_value"` // 分表字段值
SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略
TableCnt int64 `json:"table_cnt"` // 一共分表多少张
}
// SqlCondition sql条件
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:46 2024/8/23
type SqlCondition struct {
Column string `json:"column"` // 表字段
Operate string `json:"operate"` // 操作 : == / !== / in / not in / like / not like
Value any `json:"value"` // 数据值
}

View File

@ -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
View 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
View File

@ -0,0 +1,29 @@
// Package define ...
//
// Description : define ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 2024-08-21 14:56
package define
// CacheTableStructureConfig 缓存表结构的配置
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:56 2024/8/21
type CacheTableStructureConfig struct {
Enable bool `json:"enable"` // 是否启用表结构缓存
SyncTimeInterval int `json:"sync_time_interval"` // 开启的情况向, 多久同步一次表结构, 默认值 3600, 单位 : s
}
// ColumnConfig ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:42 2024/8/23
type ColumnConfig struct {
Column string `json:"column"` // 字段名
Alias string `json:"alias"` // 字段别名
Type string `json:"type"` // 字段类型
}

4
go.mod
View File

@ -5,11 +5,11 @@ go 1.21
toolchain go1.21.3
require (
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0
git.zhangdeman.cn/zhangdeman/consts v0.0.0-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-20240813083016-da44ae07ab9b
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

8
go.sum
View File

@ -4,6 +4,10 @@ git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4 h1:mibnyz
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240726024939-e424db29c5c4/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0 h1:U12XDtyRrmsqb/wRvRZG9+SBKMCGFNADpiLogsp5POw=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240817091513-491f455a23c0/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb h1:dNnVYynCQhLSbtNUVQsvDIthkNgpTrAJF4dAEj08FdE=
git.zhangdeman.cn/zhangdeman/consts v0.0.0-20240821082758-8bf75bab08fb/go.mod h1:IXXaZkb7vGzGnGM5RRWrASAuwrVSNxuoe0DmeXx5g6k=
git.zhangdeman.cn/zhangdeman/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=
@ -28,6 +32,10 @@ git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd h1:zcmfm
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240806072320-3533617196fd/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b h1:CcO2t7ssBSZwE7BDTOkCSgOvTGATarOZ0tajZ00McEc=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240813083016-da44ae07ab9b/go.mod h1:gnaF3v9/om6gaxFKeNVuKeFTYM61gHyW7vign7vrwyo=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5 h1:+P+7IDkfOYII4K7BaPiZhnzYgPpJ7mv2hhUSZcxCWiI=
git.zhangdeman.cn/zhangdeman/wrapper v0.0.0-20240821101656-59ba4ebfa5c5/go.mod h1:KcojKP22mv9/IZrQWlIBfa1EuBxtEOqfWMgN3SYK2N8=
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=

208
option.go
View File

@ -9,47 +9,23 @@ package database
import (
"encoding/json"
"errors"
"fmt"
"git.zhangdeman.cn/zhangdeman/consts"
"git.zhangdeman.cn/zhangdeman/database/define"
"git.zhangdeman.cn/zhangdeman/op_type"
"git.zhangdeman.cn/zhangdeman/serialize"
"reflect"
"strings"
)
// SetOption 设置选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 11:46 2022/5/15
type SetOption func(o *Option)
// Option 扩展选项
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 8:05 下午 2021/8/8
type Option struct {
Model any `json:"-"` // 操作model
Table string `json:"table"` // 查询的数据表
Limit int `json:"limit"` // 限制数量
Offset int `json:"offset"` // 偏移量
In map[string]any `json:"in"` // in语句
NotIn map[string]any `json:"not_in"` // not in语句
Where map[string]any `json:"where"` // where 条件
Start map[string]any `json:"start"` // >= 条件
End map[string]any `json:"end"` // < 条件
Like map[string]string `json:"like"` // like 语句
NotLike map[string]string `json:"not_like"` // not like 语句
NotEqual map[string]any `json:"not_equal"` // != 语句
Order []string `json:"order"` // 排序规则
OR [][]SetOption `json:"or"` // or 语句, or 和其他属性 and 关系, 内部 OR 关系
}
// WithModel ...
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:18 2024/1/15
func WithModel(model any) SetOption {
return func(o *Option) {
func WithModel(model any) define.SetOption {
return func(o *define.Option) {
o.Model = model
}
}
@ -59,8 +35,8 @@ func WithModel(model any) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:56 2023/3/22
func WithTable(tableName string) SetOption {
return func(o *Option) {
func WithTable(tableName string) define.SetOption {
return func(o *define.Option) {
o.Table = tableName
}
}
@ -70,8 +46,8 @@ func WithTable(tableName string) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:17 2022/5/15
func WithWhere[T op_type.BaseType](where map[string]T) SetOption {
return func(o *Option) {
func WithWhere[T op_type.BaseType](where map[string]T) define.SetOption {
return func(o *define.Option) {
if nil == o.Where {
o.Where = make(map[string]any)
}
@ -86,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)
}
@ -98,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)
}
@ -115,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
}
@ -135,8 +111,8 @@ func WithIn[T op_type.Array](field string, value T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 12:24 2022/5/15
func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption {
return func(o *Option) {
func WithBatchIn[T op_type.Array](batchIn map[string]T) define.SetOption {
return func(o *define.Option) {
if nil == o.In {
o.In = make(map[string]any)
}
@ -151,8 +127,8 @@ func WithBatchIn[T op_type.Array](batchIn map[string]T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:18 2022/5/15
func WithNotIn[T op_type.Array](field string, value T) SetOption {
return func(o *Option) {
func WithNotIn[T op_type.Array](field string, value T) define.SetOption {
return func(o *define.Option) {
if nil == o.NotIn {
o.NotIn = make(map[string]any)
}
@ -168,8 +144,8 @@ func WithNotIn[T op_type.Array](field string, value T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 16:23 2022/5/15
func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption {
return func(o *Option) {
func WithBatchNotIn[T op_type.Array](data map[string]T) define.SetOption {
return func(o *define.Option) {
if nil == o.NotIn {
o.NotIn = make(map[string]any)
}
@ -187,8 +163,8 @@ func WithBatchNotIn[T op_type.Array](data map[string]T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:01 2022/5/15
func WithStart(field string, value any) SetOption {
return func(o *Option) {
func WithStart(field string, value any) define.SetOption {
return func(o *define.Option) {
if nil == o.Start {
o.Start = make(map[string]any)
}
@ -201,8 +177,8 @@ func WithStart(field string, value any) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:03 2022/5/15
func WithBatchStart(data map[string]any) SetOption {
return func(o *Option) {
func WithBatchStart(data map[string]any) define.SetOption {
return func(o *define.Option) {
if nil == o.Start {
o.Start = make(map[string]any)
}
@ -217,8 +193,8 @@ func WithBatchStart(data map[string]any) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:07 2022/5/15
func WithEnd(field string, value any) SetOption {
return func(o *Option) {
func WithEnd(field string, value any) define.SetOption {
return func(o *define.Option) {
if nil == o.End {
o.End = make(map[string]any)
}
@ -231,8 +207,8 @@ func WithEnd(field string, value any) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:10 2022/5/15
func WithBatchEnd(data map[string]any) SetOption {
return func(o *Option) {
func WithBatchEnd(data map[string]any) define.SetOption {
return func(o *define.Option) {
if nil == o.End {
o.End = make(map[string]any)
}
@ -247,8 +223,8 @@ func WithBatchEnd(data map[string]any) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:16 2022/5/15
func WithLike(field string, value string) SetOption {
return func(o *Option) {
func WithLike(field string, value string) define.SetOption {
return func(o *define.Option) {
if nil == o.Like {
o.Like = make(map[string]string)
}
@ -264,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)
}
@ -283,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)
}
@ -300,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)
}
@ -319,8 +295,8 @@ func WithBatchNotLike(data map[string]string) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:31 2022/5/15
func WithNotEqual[T op_type.BaseType](field string, value T) SetOption {
return func(o *Option) {
func WithNotEqual[T op_type.BaseType](field string, value T) define.SetOption {
return func(o *define.Option) {
if nil == o.NotEqual {
o.NotEqual = make(map[string]any)
}
@ -333,8 +309,8 @@ func WithNotEqual[T op_type.BaseType](field string, value T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:33 2022/5/15
func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption {
return func(o *Option) {
func WithBatchNotEqual[T op_type.BaseType](data map[string]T) define.SetOption {
return func(o *define.Option) {
if nil == o.NotEqual {
o.NotEqual = make(map[string]any)
}
@ -349,10 +325,10 @@ func WithBatchNotEqual[T op_type.BaseType](data map[string]T) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:03 2022/7/23
func WithOR(orConditionList ...SetOption) SetOption {
return func(o *Option) {
func WithOR(orConditionList ...define.SetOption) define.SetOption {
return func(o *define.Option) {
if nil == o.OR {
o.OR = make([][]SetOption, 0)
o.OR = make([][]define.SetOption, 0)
}
o.OR = append(o.OR, orConditionList)
}
@ -363,8 +339,8 @@ func WithOR(orConditionList ...SetOption) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 20:15 2022/10/20
func WithOrder(orderRuleList ...string) SetOption {
return func(o *Option) {
func WithOrder(orderRuleList ...string) define.SetOption {
return func(o *define.Option) {
o.Order = orderRuleList
}
}
@ -374,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)
}
@ -388,8 +364,8 @@ func WithOrderDesc(field string) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:09 2023/4/3
func WithOrderAsc(field string) SetOption {
return func(o *Option) {
func WithOrderAsc(field string) define.SetOption {
return func(o *define.Option) {
if nil == o.Order {
o.Order = make([]string, 0)
}
@ -402,8 +378,8 @@ func WithOrderAsc(field string) SetOption {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:46 2024/8/9
func newOption(setOptionList ...SetOption) *Option {
o := &Option{}
func newOption(setOptionList ...define.SetOption) *define.Option {
o := &define.Option{}
for _, item := range setOptionList {
item(o)
}
@ -415,7 +391,7 @@ func newOption(setOptionList ...SetOption) *Option {
// Author : go_developer@163.com<白茶清欢>
//
// Date : 17:46 2024/8/9
func optionToSql(o *Option) (sqlBuildResult string, bindValue []any) {
func optionToSql(o *define.Option) (sqlBuildResult string, bindValue []any) {
bindValue = make([]any, 0)
sqlBuildResultBlockList := make([]string, 0)
// 设置where条件
@ -484,6 +460,17 @@ func optionToSql(o *Option) (sqlBuildResult string, bindValue []any) {
bindValue = append(bindValue, fieldValue)
}
// between
for field, betweenVal := range o.Between {
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+field+"` BETWEEN ? AND ?")
bindValue = append(bindValue, betweenVal[0], betweenVal[1])
}
// not between
for field, notBetweenVal := range o.NotBetween {
sqlBuildResultBlockList = append(sqlBuildResultBlockList, "`"+field+"` NOT BETWEEN ? AND ?")
bindValue = append(bindValue, notBetweenVal[0], notBetweenVal[1])
}
if len(bindValue) > 0 {
sqlBuildResult = strings.Join(sqlBuildResultBlockList, " AND ")
}
@ -508,3 +495,66 @@ func parseInSql(fieldValue any) (string, []any) {
}
return strings.Join(placeholderList, ","), dataList
}
// WithBetween between 语句
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:52 2024/8/23
func WithBetween(field string, left any, right any) define.SetOption {
return func(o *define.Option) {
if nil == o.Between {
o.Between = map[string][2]any{}
}
o.Between[field] = [2]any{left, right}
}
}
// WithNotBetween not between 语句
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 15:52 2024/8/23
func WithNotBetween(field string, left any, right any) define.SetOption {
return func(o *define.Option) {
if nil == o.NotBetween {
o.NotBetween = map[string][2]any{}
}
o.NotBetween[field] = [2]any{left, right}
}
}
// WithAnyCondition 设置任意查询条件, 仅 where 子句 in / not in / like / not like / == / !=
//
// Author : go_developer@163.com<白茶清欢>
//
// Date : 14:48 2024/8/23
func WithAnyCondition(column string, operate string, value any) (define.SetOption, error) {
if nil == value {
return nil, errors.New("value is nil")
}
switch operate {
case consts.WhereOperateEqual:
return WithWhere(map[string]any{column: value}), nil
case consts.WhereOperateNotEqual:
return WithNotEqual(column, value), nil
case consts.WhereOperateIn:
var target []any
if err := serialize.JSON.Transition(value, &target); nil != err {
return nil, err
}
return WithIn[[]any](column, target), nil
case consts.WhereOperateNotIn:
var target []any
if err := serialize.JSON.Transition(value, &target); nil != err {
return nil, err
}
return WithNotIn[[]any](column, target), nil
case consts.WhereOperateLike:
return WithLike(column, fmt.Sprintf("%v", value)), nil
case consts.WhereOperateNotLike:
return WithNotLike(column, fmt.Sprintf("%v", value)), nil
default:
return nil, errors.New(operate + " : operate is not support")
}
}

View File

@ -9,14 +9,12 @@ package database
import (
"fmt"
"git.zhangdeman.cn/zhangdeman/database/define"
"testing"
)
func Test_optionToSql(t *testing.T) {
type args struct {
o *Option
}
o := &Option{
o := &define.Option{
In: nil,
NotIn: nil,
Where: map[string]any{

View File

@ -12,6 +12,7 @@ import (
"gorm.io/driver/mysql"
"git.zhangdeman.cn/zhangdeman/database/define"
"gorm.io/gorm"
)
@ -99,10 +100,10 @@ func (sd *SystemDao) GetCreateTableSQL(dbInstance *gorm.DB, table string) (strin
// Author : go_developer@163.com<白茶清欢>
//
// Date : 22:37 2023/8/16
func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableName string) ([]*DescTableItem, error) {
func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableName string) ([]*define.DescTableItem, error) {
var (
err error
result []*DescTableItem
result []*define.DescTableItem
)
if err = dbInstance.Raw("DESC `" + tableName + "`").Scan(&result).Error; nil != err {
@ -125,9 +126,9 @@ func (sd *SystemDao) GetTableDesc(dbInstance *gorm.DB, database string, tableNam
// Author : go_developer@163.com<白茶清欢>
//
// Date : 23:10 2023/8/16
func (sd *SystemDao) GetTableInfo(dbInstance *gorm.DB, database string, tableName string) ([]*ColumnInfo, error) {
func (sd *SystemDao) GetTableInfo(dbInstance *gorm.DB, database string, tableName string) ([]*define.ColumnInfo, error) {
var (
list []*ColumnInfo
list []*define.ColumnInfo
err error
)

205
wrapper_client.go Normal file
View 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
View 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()
}