database/api2sql/execute.go

282 lines
8.0 KiB
Go

// 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(`%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, "")
}
}
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
}