193 lines
4.5 KiB
Go
193 lines
4.5 KiB
Go
package executor
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"math"
|
||
"strings"
|
||
"time"
|
||
|
||
"dataengine/common/report/builder"
|
||
"dataengine/common/report/model"
|
||
|
||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||
"github.com/sirupsen/logrus"
|
||
)
|
||
|
||
// QueryExecutor SQL执行器
|
||
type QueryExecutor struct {
|
||
sqlBuilder *builder.SQLBuilder
|
||
}
|
||
|
||
// NewQueryExecutor 创建查询执行器
|
||
func NewQueryExecutor() *QueryExecutor {
|
||
return &QueryExecutor{
|
||
sqlBuilder: builder.NewSQLBuilder(),
|
||
}
|
||
}
|
||
|
||
// QueryReportByUserSelect 根据用户选择实时查询报表数据(核心接口)
|
||
func (e *QueryExecutor) QueryReportByUserSelect(ctx context.Context, req *model.UserSelectQueryReq) (*model.UserSelectQueryResp, error) {
|
||
start := time.Now()
|
||
logger := logrus.WithFields(logrus.Fields{
|
||
"businessCode": req.BusinessCode,
|
||
"reportCode": req.ReportCode,
|
||
})
|
||
|
||
// 1. 参数校验
|
||
if err := e.validateReq(req); err != nil {
|
||
return nil, fmt.Errorf("参数校验失败: %w", err)
|
||
}
|
||
|
||
// 2. 构建查询SQL
|
||
querySQL, queryArgs, metadata, err := e.sqlBuilder.BuildQuerySQL(ctx, req)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("构建查询SQL失败: %w", err)
|
||
}
|
||
|
||
logger.Debugf("查询SQL: %s args: %v", querySQL, queryArgs)
|
||
|
||
// 3. 获取记录总数
|
||
total, err := e.executeCount(ctx, metadata, queryArgs)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("查询总数失败: %w", err)
|
||
}
|
||
|
||
// 4. 分页查询
|
||
if req.Page < 1 {
|
||
req.Page = 1
|
||
}
|
||
if req.PageSize < 1 {
|
||
req.PageSize = 20
|
||
}
|
||
if req.PageSize > 1000 {
|
||
req.PageSize = 1000
|
||
}
|
||
|
||
pagedSQL := e.sqlBuilder.AddLimit(querySQL, req.Page, req.PageSize)
|
||
logger.Debugf("分页SQL: %s", pagedSQL)
|
||
|
||
dataResult, err := gfdb.DB(ctx).GetAll(ctx, pagedSQL, queryArgs...)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("查询数据失败: %w", err)
|
||
}
|
||
|
||
var list []map[string]interface{}
|
||
if dataResult.Len() > 0 {
|
||
list = dataResult.List()
|
||
}
|
||
|
||
// 计算总页数
|
||
totalPages := int(math.Ceil(float64(total) / float64(req.PageSize)))
|
||
|
||
execTime := time.Since(start).Milliseconds()
|
||
|
||
resp := &model.UserSelectQueryResp{
|
||
List: list,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
TotalPages: totalPages,
|
||
Sql: querySQL,
|
||
ExecTimeMs: execTime,
|
||
}
|
||
|
||
logger.Infof("报表查询完成, 总数:%d 耗时:%dms", total, execTime)
|
||
|
||
return resp, nil
|
||
}
|
||
|
||
// executeCount 执行总数统计
|
||
func (e *QueryExecutor) executeCount(ctx context.Context, metadata map[string]interface{}, args []interface{}) (int64, error) {
|
||
countSql, ok := metadata["countSql"].(string)
|
||
if !ok || countSql == "" {
|
||
return 0, nil
|
||
}
|
||
|
||
result, err := gfdb.DB(ctx).GetAll(ctx, countSql, args...)
|
||
if err != nil {
|
||
return 0, fmt.Errorf("统计总数失败: %w", err)
|
||
}
|
||
|
||
var total int64
|
||
if result.Len() > 0 {
|
||
for k, v := range result.List()[0] {
|
||
_ = k
|
||
if n, ok := v.(int64); ok {
|
||
total = n
|
||
} else {
|
||
total = result[0]["count"].Int64()
|
||
}
|
||
break
|
||
}
|
||
}
|
||
return total, nil
|
||
}
|
||
|
||
// validateReq 校验请求参数
|
||
func (e *QueryExecutor) validateReq(req *model.UserSelectQueryReq) error {
|
||
if req.BusinessCode == "" {
|
||
return fmt.Errorf("业务编码不能为空")
|
||
}
|
||
if req.ReportCode == "" {
|
||
return fmt.Errorf("报表编码不能为空")
|
||
}
|
||
if len(req.Indicators) == 0 {
|
||
return fmt.Errorf("必须选择至少一个指标")
|
||
}
|
||
|
||
// 校验指标
|
||
for i, ind := range req.Indicators {
|
||
if ind.FieldCode == "" {
|
||
return fmt.Errorf("第%d个指标字段编码不能为空", i+1)
|
||
}
|
||
if ind.Aggregate != "" {
|
||
agg := strings.ToUpper(ind.Aggregate)
|
||
validAggs := map[string]bool{
|
||
"SUM": true,
|
||
"COUNT": true,
|
||
"AVG": true,
|
||
"MAX": true,
|
||
"MIN": true,
|
||
}
|
||
if !validAggs[agg] {
|
||
return fmt.Errorf("不支持的聚合方式: %s", ind.Aggregate)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 校验时间分组
|
||
if req.TimeGroup != "" {
|
||
validGroups := map[string]bool{
|
||
"day": true,
|
||
"week": true,
|
||
"month": true,
|
||
"quarter": true,
|
||
}
|
||
if !validGroups[req.TimeGroup] {
|
||
return fmt.Errorf("不支持的时间分组: %s", req.TimeGroup)
|
||
}
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// RawQuery 执行原始SQL查询(用于特殊场景)
|
||
func (e *QueryExecutor) RawQuery(ctx context.Context, sql string, args ...interface{}) ([]map[string]interface{}, error) {
|
||
result, err := gfdb.DB(ctx).GetAll(ctx, sql, args...)
|
||
if err != nil {
|
||
return nil, fmt.Errorf("原始查询失败: %w", err)
|
||
}
|
||
|
||
if result.Len() > 0 {
|
||
return result.List(), nil
|
||
}
|
||
return nil, nil
|
||
}
|
||
|
||
// ExecRaw 执行原始SQL(INSERT/UPDATE/DELETE)
|
||
func (e *QueryExecutor) ExecRaw(ctx context.Context, sql string, args ...interface{}) error {
|
||
_, err := gfdb.DB(ctx).Exec(ctx, sql, args...)
|
||
return err
|
||
}
|