// 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" "reflect" ) 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) (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 := e.dynamicGenerateStruct(inputParam.ColumnList) val := st.ToStructDefaultSliceValue() 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 ( res any err error ) if res, err = e.List(ctx, inputParam); nil != err { return nil, err } reflectVal := reflect.ValueOf(res) if reflectVal.Len() == 0 { return nil, gorm.ErrRecordNotFound } return reflectVal.Index(0).Interface(), 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 } // 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 }