2024-08-21 21:00:11 +08:00
|
|
|
|
// Package api2sql ...
|
|
|
|
|
//
|
|
|
|
|
// Description : api2sql ...
|
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 2024-08-21 20:45
|
|
|
|
|
package api2sql
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"errors"
|
2024-08-23 16:50:47 +08:00
|
|
|
|
"fmt"
|
2024-08-21 21:00:11 +08:00
|
|
|
|
"git.zhangdeman.cn/zhangdeman/consts"
|
2024-08-23 16:50:47 +08:00
|
|
|
|
"git.zhangdeman.cn/zhangdeman/database"
|
2024-08-21 21:11:19 +08:00
|
|
|
|
"git.zhangdeman.cn/zhangdeman/database/abstract"
|
2024-08-21 21:00:11 +08:00
|
|
|
|
"git.zhangdeman.cn/zhangdeman/database/define"
|
2024-08-22 11:46:00 +08:00
|
|
|
|
"git.zhangdeman.cn/zhangdeman/wrapper"
|
2024-08-23 12:26:03 +08:00
|
|
|
|
"gorm.io/gorm"
|
2024-08-21 21:00:11 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
Exec = &execute{}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type execute struct {
|
2024-08-21 21:11:19 +08:00
|
|
|
|
databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例
|
2024-08-23 16:50:47 +08:00
|
|
|
|
baseDao database.BaseDao // 基础dao
|
2024-08-21 21:11:19 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// SetDatabaseClientManager 设置数据库连接管理实例
|
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 21:06 2024/8/21
|
|
|
|
|
func (e *execute) SetDatabaseClientManager(databaseClientManager abstract.IWrapperClient) {
|
|
|
|
|
e.databaseClientManager = databaseClientManager
|
2024-08-21 21:00:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Run 执行
|
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 20:48 2024/8/21
|
|
|
|
|
func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
2024-08-23 10:59:20 +08:00
|
|
|
|
if err := e.formatAndValidateInputParam(inputParam); nil != err {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-08-21 21:00:11 +08:00
|
|
|
|
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")
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-03 12:02:13 +08:00
|
|
|
|
// List 方法用于从数据库中查询数据列表。
|
|
|
|
|
//
|
|
|
|
|
// 参数:
|
|
|
|
|
// - ctx: 上下文对象,用于传递请求的上下文信息。
|
|
|
|
|
// - inputParam: 查询参数,包含 SQL 语句、列列表等信息。
|
|
|
|
|
//
|
|
|
|
|
// 返回值:
|
|
|
|
|
// - any: 查询结果,返回一个结构体切片,其中包含查询到的数据。
|
|
|
|
|
// - error: 错误信息,如果查询过程中发生错误,将返回相应的错误对象。
|
|
|
|
|
//
|
|
|
|
|
// 使用示例:
|
|
|
|
|
// result, err := e.List(ctx, inputParam)
|
|
|
|
|
// if err!= nil {
|
|
|
|
|
// // 处理错误
|
|
|
|
|
// }
|
|
|
|
|
// // 处理查询结果
|
2024-08-21 21:00:11 +08:00
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 20:52 2024/8/21
|
2024-09-06 14:49:36 +08:00
|
|
|
|
func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) ([]map[string]any, error) {
|
2024-08-23 16:50:47 +08:00
|
|
|
|
var (
|
|
|
|
|
err error
|
|
|
|
|
tx *gorm.DB
|
2024-08-23 17:54:48 +08:00
|
|
|
|
optionList []define.SetOption
|
2024-09-06 14:49:36 +08:00
|
|
|
|
val []map[string]any
|
2024-08-23 16:50:47 +08:00
|
|
|
|
)
|
|
|
|
|
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
|
2024-08-21 21:00:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
2024-09-05 14:50:28 +08:00
|
|
|
|
// Detail 方法用于获取单个数据的详细信息。
|
|
|
|
|
// 它接受一个上下文对象 ctx 和一个指向输入参数结构体 Api2SqlParam 的指针 inputParam。
|
|
|
|
|
// 首先,它会将 inputParam 的 Limit 属性设置为 1,以限制查询结果为一条记录。
|
|
|
|
|
// 然后,它会调用 e.List 方法来执行查询操作,并将结果存储在 res 变量中。
|
|
|
|
|
// 如果查询过程中发生错误,它会将错误返回。
|
|
|
|
|
// 如果查询成功但结果集为空,它会返回一个错误,表示未找到记录。
|
|
|
|
|
// 如果查询成功且结果集不为空,它会返回结果集中的第一条记录。
|
|
|
|
|
//
|
|
|
|
|
// 参数 :
|
|
|
|
|
// - ctx : 上下文对象,用于传递请求的上下文信息。
|
|
|
|
|
// - inputParam : 查询参数,包含了查询所需的条件和限制。
|
|
|
|
|
//
|
|
|
|
|
// 返回值 :
|
|
|
|
|
// - 返回查询到的单个数据的详细信息
|
|
|
|
|
// - 如果未找到数据则返回错误
|
2024-08-21 21:00:11 +08:00
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 20:52 2024/8/21
|
|
|
|
|
func (e *execute) Detail(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
2024-08-30 14:50:51 +08:00
|
|
|
|
inputParam.Limit = 1 // 限制查询一条
|
|
|
|
|
var (
|
2024-09-06 14:49:36 +08:00
|
|
|
|
list []map[string]any
|
|
|
|
|
err error
|
2024-08-30 14:50:51 +08:00
|
|
|
|
)
|
2024-09-06 14:49:36 +08:00
|
|
|
|
if list, err = e.List(ctx, inputParam); nil != err {
|
2024-08-30 14:50:51 +08:00
|
|
|
|
return nil, err
|
|
|
|
|
}
|
2024-09-06 14:49:36 +08:00
|
|
|
|
if len(list) == 0 {
|
2024-08-30 14:50:51 +08:00
|
|
|
|
return nil, gorm.ErrRecordNotFound
|
|
|
|
|
}
|
2024-09-06 14:49:36 +08:00
|
|
|
|
return list[0], nil
|
2024-08-21 21:00:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Insert 插入
|
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 20:52 2024/8/21
|
|
|
|
|
func (e *execute) Insert(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) {
|
2024-09-06 14:49:36 +08:00
|
|
|
|
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
|
2024-08-21 21:00:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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) {
|
2024-08-22 11:46:00 +08:00
|
|
|
|
if nil == e.databaseClientManager {
|
|
|
|
|
return nil, errors.New("database client is nil, please use `SetDatabaseClientManager` set instance")
|
|
|
|
|
}
|
|
|
|
|
// 格式化 inputParam
|
2024-08-21 21:00:11 +08:00
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2024-08-22 11:46:00 +08:00
|
|
|
|
|
|
|
|
|
// 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`")
|
|
|
|
|
}
|
|
|
|
|
// 操作字段列表为空
|
2024-08-22 11:51:02 +08:00
|
|
|
|
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 {
|
2024-08-23 16:50:47 +08:00
|
|
|
|
if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{
|
2024-08-22 11:46:00 +08:00
|
|
|
|
consts.SqlTypeList, consts.SqlTypeDetail,
|
|
|
|
|
}).Has(inputParam.SqlType) >= 0 {
|
2024-08-23 16:50:47 +08:00
|
|
|
|
return errors.New("column list is empty")
|
2024-08-22 11:46:00 +08:00
|
|
|
|
}
|
|
|
|
|
// 验证字段是否都正确
|
|
|
|
|
tableColumnTable := make(map[string]bool)
|
|
|
|
|
for _, itemColumn := range inputParam.TableColumnConfig {
|
2024-08-23 17:54:48 +08:00
|
|
|
|
tableColumnTable[itemColumn.Column] = true
|
2024-08-22 11:46:00 +08:00
|
|
|
|
}
|
2024-08-23 16:50:47 +08:00
|
|
|
|
for _, columnConfig := range inputParam.ColumnList {
|
|
|
|
|
if !tableColumnTable[columnConfig.Column] {
|
|
|
|
|
return errors.New(columnConfig.Column + " : input column not found in table column list")
|
2024-08-22 11:46:00 +08:00
|
|
|
|
}
|
2024-08-23 16:50:47 +08:00
|
|
|
|
if len(columnConfig.Alias) == 0 {
|
|
|
|
|
columnConfig.Alias = columnConfig.Column
|
2024-08-22 11:46:00 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-08-23 12:26:03 +08:00
|
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
|
}
|
2024-08-23 16:50:47 +08:00
|
|
|
|
|
|
|
|
|
// getOptionList 设置where条件
|
|
|
|
|
//
|
|
|
|
|
// Author : go_developer@163.com<白茶清欢>
|
|
|
|
|
//
|
|
|
|
|
// Date : 12:31 2024/8/23
|
2024-08-23 17:54:48 +08:00
|
|
|
|
func (e *execute) getOptionList(ctx context.Context, inputParam *define.Api2SqlParam) ([]define.SetOption, error) {
|
|
|
|
|
optionList := []define.SetOption{
|
2024-08-23 16:50:47 +08:00
|
|
|
|
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
|
|
|
|
|
}
|
2024-08-30 14:50:51 +08:00
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
}
|