重构数据引擎和报表引擎
This commit is contained in:
192
common/report/executor/executor.go
Normal file
192
common/report/executor/executor.go
Normal file
@@ -0,0 +1,192 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user