Files
data-engine/common/report/executor/executor.go
2026-06-11 13:06:54 +08:00

193 lines
4.5 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 执行原始SQLINSERT/UPDATE/DELETE
func (e *QueryExecutor) ExecRaw(ctx context.Context, sql string, args ...interface{}) error {
_, err := gfdb.DB(ctx).Exec(ctx, sql, args...)
return err
}