diff --git a/api2sql/execute.go b/api2sql/execute.go index f76a8bf..66bd615 100644 --- a/api2sql/execute.go +++ b/api2sql/execute.go @@ -10,7 +10,9 @@ 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" @@ -23,6 +25,7 @@ var ( type execute struct { databaseClientManager abstract.IWrapperClient // 全部数据库管理的实例 + baseDao database.BaseDao // 基础dao } // SetDatabaseClientManager 设置数据库连接管理实例 @@ -72,8 +75,43 @@ func (e *execute) Run(ctx context.Context, inputParam *define.Api2SqlParam) (any // // Date : 20:52 2024/8/21 func (e *execute) List(ctx context.Context, inputParam *define.Api2SqlParam) (any, error) { - - return nil, nil + var ( + err error + tx *gorm.DB + optionList []database.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) + switch columnConfig.Type { + case "int", "int8", "int16", "int32", "int64": + st.AddInt(columnConfig.Column, tag, "") + case "uint", "uint8", "uint16", "uint32", "uint64": + st.AddUint(columnConfig.Column, tag, "") + case "bool": + st.AddBool(columnConfig.Column, tag, "") + case "float": + st.AddBool(columnConfig.Column, tag, "") + case "string": + st.AddString(columnConfig.Column, tag, "") + case "map": + st.AddMap(columnConfig.Column, tag, "") + case "slice": + st.AddSlice(columnConfig.Column, tag, "") + } + } + val := st.ToStructDefaultSliceValue() + if err = e.baseDao.List(tx, &val, optionList...); nil != err { + return nil, err + } + return val, nil } // Detail 详情 @@ -181,26 +219,22 @@ func (e *execute) formatAndValidateInputParam(inputParam *define.Api2SqlParam) e // // Date : 11:48 2024/8/22 func (e *execute) validateColumn(inputParam *define.Api2SqlParam) error { - if len(inputParam.ColumnTable) == 0 && wrapper.ArrayType[string]([]string{ + if len(inputParam.ColumnList) == 0 && wrapper.ArrayType[string]([]string{ consts.SqlTypeList, consts.SqlTypeDetail, }).Has(inputParam.SqlType) >= 0 { - for _, itemParam := range inputParam.TableColumnConfig { - inputParam.ColumnTable[itemParam.ColumnName] = itemParam.ColumnName - } + return errors.New("column list is empty") } // 验证字段是否都正确 tableColumnTable := make(map[string]bool) for _, itemColumn := range inputParam.TableColumnConfig { tableColumnTable[itemColumn.ColumnName] = true } - for columnName, _ := range inputParam.ColumnTable { - if !tableColumnTable[columnName] { - return errors.New(columnName + " : input column not found in table column list") + for _, columnConfig := range inputParam.ColumnList { + if !tableColumnTable[columnConfig.Column] { + return errors.New(columnConfig.Column + " : input column not found in table column list") } - } - for _, item := range inputParam.ValueList { - if !tableColumnTable[item.Column] { - return errors.New(item.Column + " : input column in `ValueList` is not found in table column list") + if len(columnConfig.Alias) == 0 { + columnConfig.Alias = columnConfig.Column } } return nil @@ -221,3 +255,26 @@ func (e *execute) getTx(ctx context.Context, inputParam *define.Api2SqlParam) (* } 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) ([]database.SetOption, error) { + optionList := []database.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 +} diff --git a/define/api2sql.go b/define/api2sql.go index 10e2476..0e20c28 100644 --- a/define/api2sql.go +++ b/define/api2sql.go @@ -7,7 +7,9 @@ // Date : 2024-08-21 16:05 package define -import "gorm.io/gorm" +import ( + "gorm.io/gorm" +) // Api2SqlParam 接口转sql的输入配置 // @@ -27,48 +29,55 @@ import "gorm.io/gorm" // // 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 相关解析分表配置 - TableSplit bool `json:"table_split"` // 是否分表 - SplitField string `json:"split_field"` // 分表字段, 仅分表时有效, 分表字段要求在 ValueList 必须存在 - SplitStrategy string `json:"split_strategy"` // 分表策略, 仅分表时有效, 支持注册自动以策略 - SqlType string `json:"sql_type"` // sql语句类型 : detail - 查询详情 list - 查询列表 count - 查询数量 update - 更新 insert - 插入 delete - 删除 - ColumnTable map[string]string `json:"column_list"` // 仅针对 select / detail 有效, 查询的字段列表, 字段名 => 字段别名, 不设置, 则以字段名输出 - Limit int64 `json:"limit"` // 操作数据量 - Offset int64 `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 生效 - ValueList []*Api2SqlParamValue `json:"value_list"` // 字段列表 - TableColumnConfig []*ColumnInfo `json:"table_column_config"` // 表字段配置 - Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 + 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 []*ColumnInfo `json:"table_column_config"` // 表字段配置 + Tx *gorm.DB `json:"-"` // 前后已有的数据库连接, 直接复用 } -// Api2SqlParamValue ... +// TableSplitConfig 分表配置 // // Author : go_developer@163.com<白茶清欢> // -// Date : 16:11 2024/8/21 -type Api2SqlParamValue struct { - Column string `json:"column"` // 表字段 - Value any `json:"value"` // 数据字段的值 - Operate string `json:"operate"` // 操作符 : Equal , NotEqual , In , NotIn , Like, NotLike - Alias string `json:"alias"` // 字段对外输出的名字, 不配置, 默认 与 Field 一致, 仅查询语句生效 - Default any `json:"-"` // 默认值 TODO : 配置默认值生成策略 - DataMask any `json:"-"` // 数据脱敏策略 +// 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"` // 一共分表多少张 } -const ( - SqlSelectBaseTpl = `SELECT {FIELD_LIST} FROM {TABLE}` // select 语句基础模板 - SqlInsertBaseTpl = `INSERT INTO {TABLE} ({FIELD_LIST}) VALUES ({VALUE_LIST})` // Insert语句基础模板 - SqlUpdateBaseTpl = `UPDATE {TABLE} SET {SET}` // Update语句基础模板 - SqlDeleteBaseTpl = `DELETE FROM {TABLE}` // Delete语句基础模板 - SqlCountBaseTpl = `SELECT COUNT(*) as count FROM {TABLE}` // Count语句基础模板 - SqlWhereTpl = "WHERE {WHERE}" // where 语句 - SqlOrder = "ORDER BY {ORDER_FIELD} {ORDER_RULE}" // 排序语句 - SqlLimit = "LIMIT {LIMIT}" // limit 语句 - SqlOffset = "OFFSET {OFFSET}" // offset 语句 -) +// 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"` // 数据值 +} + +// 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"` // 字段类型 +}