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

667 lines
30 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 report
// ============================================================
// 通用报表引擎 - 完整调用示例
// ============================================================
//
// 包名: dataengine/common/report
// 入口: report.GetService() → *ReportService
//
// 接口一览:
// ┌──────────────┬─────────────────────────────────────────────────┐
// │ 分类 │ 接口 │
// ├──────────────┼─────────────────────────────────────────────────┤
// │ 配置 CRUD │ SaveBusiness / DelBusiness / GetBusiness │
// │ │ SaveReport / DelReport / GetReport │
// │ │ SaveField / DelField / GetField │
// │ │ SaveExtractConfig / DelExtractConfig / Get.. │
// ├──────────────┼─────────────────────────────────────────────────┤
// │ 数据抽取 │ ExtractDailyData / AutoCreateStatTable │
// ├──────────────┼─────────────────────────────────────────────────┤
// │ 报表查询 │ QueryReportByUserSelect │
// ├──────────────┼─────────────────────────────────────────────────┤
// │ 辅助查询 │ GetAllBusinesses / GetAllReports │
// │ │ GetReportFields / GetExtractConfigs │
// └──────────────┴─────────────────────────────────────────────────┘
//
// ============================================================
// ============================================================================
// 场景一:新平台零代码接入(快手电商为例)
// ============================================================================
//
// 背景:快手订单数据已通过数据引擎同步到 kuaishou_order_list 表(每条订单一行)。
// 需求:按店铺+天聚合订单数据 → 自动建统计宽表 → 抽取 → 前端自由查询报表。
//
// 全程只调 API 不写 SQL前端管理后台可直接操作。
/*
package example
import (
"context"
"encoding/json"
"fmt"
"time"
"dataengine/common/report"
"dataengine/common/report/model"
)
func KuaishouExample() {
ctx := context.Background()
svc := report.GetService()
// ─── Step 1: 注册业务 ───────────────────────────────────────────
// 一个业务就是一个数据源平台(快手/抖音/淘宝/...
_, _ = svc.SaveBusiness(ctx, &model.SaveBusinessReq{
BusinessCode: "KUAISHOU", // 唯一标识,后续所有接口都用它
BusinessName: "快手电商",
Description: "快手平台电商业务线",
Status: model.StatusActive,
Operator: "admin",
})
// ─── Step 2: 注册报表 ───────────────────────────────────────────
// 一个业务可以有多个报表(店铺日报、商品日报、主播日报...
_, _ = svc.SaveReport(ctx, &model.SaveReportReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report", // 报表唯一编码
ReportName: "快手店铺日报",
StatTableName: "stat_kuaishou_shop_daily", // 统计宽表名(自动创建)
DateField: "stat_date", // 日期字段
ConflictKeys: []string{"shop_id", "stat_date"}, // 唯一约束upsert 依据)
Operator: "admin",
})
// ─── Step 3: 配置字段(前端可选择的所有维度/指标/筛选) ─────────
// 这是前端"自定义报表"的数据源 —— 前端建好字段后,
// 用户选择哪些维度、哪些指标、怎么筛选,实时查。
// 3a. 维度字段:分组依据,不可聚合
dimensions := []model.SaveFieldReq{
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "shop_id", FieldName: "店铺ID", FieldType: "STRING",
FieldRole: "DIMENSION", IsSortable: true, SortOrder: 1, GroupName: "店铺", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "shop_name", FieldName: "店铺名称", FieldType: "STRING",
FieldRole: "DIMENSION", IsSortable: true, SortOrder: 2, GroupName: "店铺", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "stat_date", FieldName: "统计日期", FieldType: "DATE",
FieldRole: "DIMENSION", IsSortable: true, SortOrder: 3, GroupName: "时间", Operator: "admin"},
}
// 3b. 指标字段:聚合度量,前端可选 SUM/COUNT/AVG/MAX/MIN
indicators := []model.SaveFieldReq{
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "order_count", FieldName: "订单数", FieldType: "INT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "COUNT", "AVG", "MAX", "MIN"},
SortOrder: 10, GroupName: "订单", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "order_amount", FieldName: "订单金额(元)", FieldType: "FLOAT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "AVG", "MAX", "MIN"},
SortOrder: 11, GroupName: "金额", Unit: "元", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "paid_amount", FieldName: "实付金额(元)", FieldType: "FLOAT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "AVG"},
SortOrder: 12, GroupName: "金额", Unit: "元", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "refund_amount", FieldName: "退款金额(元)", FieldType: "FLOAT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "AVG"},
SortOrder: 13, GroupName: "退款", Unit: "元", Operator: "admin"},
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "buyer_count", FieldName: "下单买家数", FieldType: "INT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "COUNT",
ValidAggregates: []string{"COUNT"},
SortOrder: 14, GroupName: "用户", Operator: "admin"},
// 衍生指标:退款率 = 退款金额 / 订单金额 * 100
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "refund_rate", FieldName: "退款率", FieldType: "FLOAT",
FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "AVG",
Expression: "{refund_amount} / NULLIF({order_amount}, 0) * 100",
ExpressionType: "CALCULATED", FormatPattern: "#,##0.00",
Unit: "%", SortOrder: 20, GroupName: "退款", Operator: "admin"},
}
// 3c. 筛选字段:纯筛选,不出现在 SELECT 中
filters := []model.SaveFieldReq{
{BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
FieldCode: "order_status", FieldName: "订单状态", FieldType: "STRING",
FieldRole: "FILTER", IsFilterable: true,
FilterOperators: []string{"=", "IN"},
SortOrder: 30, GroupName: "筛选", Operator: "admin"},
}
for _, f := range dimensions {
_, _ = svc.SaveField(ctx, &f)
}
for _, f := range indicators {
_, _ = svc.SaveField(ctx, &f)
}
for _, f := range filters {
_, _ = svc.SaveField(ctx, &f)
}
// ─── Step 4: 配置数据抽取规则 ────────────────────────────────────
// 关键AGGREGATE 模式,从源表聚合到统计宽表
_, _ = svc.SaveExtractConfig(ctx, &model.SaveExtractConfigReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report",
ExtractCode: "extract_shop_daily",
ExtractName: "快手店铺订单按天聚合",
SourceTableName: "kuaishou_order_list", // ← 源表(订单明细)
SourceTableAlias: "o",
TargetTableName: "stat_kuaishou_shop_daily", // ← 目标(统计宽表)
ExtractType: model.ExtractTypeIncremental, // 增量抽取
ExtractMode: model.ExtractModeAggregate, // ← 聚合模式
ExtractKeyField: "created_at", // 增量依据字段
GroupByFields: []string{"shop_id"}, // ← GROUP BY
FilterExpression: "o.order_status != 'CANCELLED'", // 过滤取消订单
// 字段映射:源表字段 → 目标表字段 + 聚合函数
FieldMappings: []model.FieldMapping{
{SourceField: "shop_id", TargetField: "shop_id", FieldType: "STRING"},
{SourceField: "shop_name", TargetField: "shop_name", FieldType: "STRING"},
{SourceField: "id", TargetField: "order_count", FieldType: "INT", AggregateFunction: "COUNT"},
{SourceField: "order_amount", TargetField: "order_amount", FieldType: "FLOAT", AggregateFunction: "SUM"},
{SourceField: "paid_amount", TargetField: "paid_amount", FieldType: "FLOAT", AggregateFunction: "SUM"},
{SourceField: "refund_amount",TargetField: "refund_amount",FieldType: "FLOAT", AggregateFunction: "SUM"},
{SourceField: "buyer_id", TargetField: "buyer_count", FieldType: "INT", AggregateFunction: "COUNT"},
},
BatchSize: 1000,
Operator: "admin",
})
// ─── Step 5: 每天定时抽取cron/k8s CronJob 中调用) ─────────
// ExtractDailyData 内部:
// 1. 检测 stat_kuaishou_shop_daily 表是否存在
// 2. 不存在 → 根据 FieldConfig 自动 CREATE TABLE
// 3. 从 kuaishou_order_list 按 AGGREGATE 模式抽取当天数据
// 4. UPSERT 到统计宽表
today := time.Now().Format("2006-01-02")
resp, _ := svc.ExtractDailyData(ctx, "KUAISHOU", "shop_daily_report", today, "cron")
fmt.Printf("[%s] 抽取完成: 总%d 成功%d 失败%d 耗时%dms\n",
today, resp.TotalCount, resp.SuccessCount, resp.FailCount, resp.ExecTimeMs)
// 实际抽取生成的 SQLAGGREGATE 模式):
//
// INSERT INTO stat_kuaishou_shop_daily (...)
// SELECT ROW_NUMBER() OVER () AS id,
// '2026-06-10' AS stat_date,
// o.shop_id, o.shop_name,
// COUNT(o.id) AS order_count,
// SUM(o.order_amount) AS order_amount,
// SUM(o.paid_amount) AS paid_amount,
// SUM(o.refund_amount) AS refund_amount,
// COUNT(o.buyer_id) AS buyer_count
// FROM kuaishou_order_list o
// WHERE o.created_at::date = '2026-06-10'
// GROUP BY o.shop_id
// ─── Step 6: 前端查询 ──────────────────────────────────────────
// 6a. 前端先拉取可用字段列表
fields, _ := svc.GetReportFields(ctx, "KUAISHOU", "shop_daily_report")
// 返回:
// dimensions: [shop_id, shop_name, stat_date]
// indicators: [order_count, order_amount, paid_amount, refund_amount, buyer_count, refund_rate]
// filters: [order_status]
// 前端据此渲染选择器面板
// 6b. 用户选择: 维度=店铺, 指标=金额+订单数, 时间=近7天, 排名=TOP10
top10Req := &model.UserSelectQueryReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report",
Dimensions: []string{"shop_id", "shop_name"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "total_amount"},
{FieldCode: "order_count", Aggregate: "SUM", Alias: "total_orders"},
{FieldCode: "paid_amount", Aggregate: "SUM", Alias: "total_paid"},
{FieldCode: "refund_rate", Aggregate: "AVG", Alias: "avg_refund_rate"},
},
TimeRange: &model.TimeRange{
StartDate: time.Now().AddDate(0, 0, -7).Format("2006-01-02"),
EndDate: today,
},
OrderBy: []model.OrderCondition{{FieldCode: "total_amount", Direction: "DESC"}},
Page: 1,
PageSize: 10,
}
top10Resp, _ := svc.QueryReportByUserSelect(ctx, top10Req)
fmt.Printf("TOP10 销售额排行 (总数=%d):\n", top10Resp.Total)
for i, row := range top10Resp.List {
b, _ := json.Marshal(row)
fmt.Printf(" %d. %s\n", i+1, string(b))
}
// 实际生成 SQL:
// SELECT o.shop_id, o.shop_name,
// SUM(o.order_amount) AS total_amount,
// SUM(o.order_count) AS total_orders,
// SUM(o.paid_amount) AS total_paid,
// AVG(refund_amount / NULLIF(order_amount,0) * 100) AS avg_refund_rate
// FROM stat_kuaishou_shop_daily o
// WHERE stat_date BETWEEN '2026-06-03' AND '2026-06-10'
// GROUP BY o.shop_id, o.shop_name
// ORDER BY total_amount DESC
// LIMIT 10 OFFSET 0
// 6c. 用户切换维度: 每日趋势(聚合所有店铺)
trendReq := &model.UserSelectQueryReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report",
Dimensions: []string{"stat_date"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "daily_amount"},
{FieldCode: "order_count", Aggregate: "SUM", Alias: "daily_orders"},
{FieldCode: "refund_amount",Aggregate: "SUM", Alias: "daily_refund"},
},
TimeRange: &model.TimeRange{
StartDate: time.Now().AddDate(0, 0, -30).Format("2006-01-02"),
EndDate: today,
},
TimeGroup: "day",
OrderBy: []model.OrderCondition{{FieldCode: "stat_date", Direction: "ASC"}},
PageSize: 100,
}
trendResp, _ := svc.QueryReportByUserSelect(ctx, trendReq)
fmt.Printf("30天趋势 (共%d行):\n", trendResp.Total)
// 6d. 加筛选条件: 只看活跃订单,金额>10000
filteredReq := &model.UserSelectQueryReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report",
Dimensions: []string{"shop_id", "shop_name"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "total_amount"},
},
Filters: []model.FilterCondition{
{FieldCode: "order_status", Operator: "=", Value: "ACTIVE"},
{FieldCode: "total_amount", Operator: ">=", Value: 10000}, // 指标别名也可筛选
},
OrderBy: []model.OrderCondition{{FieldCode: "total_amount", Direction: "DESC"}},
PageSize: 20,
}
_ = filteredReq
// 6e. 按周汇总趋势
weeklyReq := &model.UserSelectQueryReq{
BusinessCode: "KUAISHOU",
ReportCode: "shop_daily_report",
Dimensions: []string{"shop_id"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "weekly_amount"},
},
TimeGroup: "week",
OrderBy: []model.OrderCondition{{FieldCode: "weekly_amount", Direction: "DESC"}},
PageSize: 50,
}
_ = weeklyReq
}
// ============================================================================
// 场景二已有配置管理CRUD 二次开发参考)
// ============================================================================
func CRUDExample() {
ctx := context.Background()
svc := report.GetService()
// ── 业务 CRUD ────────────────────────────────────────────
// 新增
result, _ := svc.SaveBusiness(ctx, &model.SaveBusinessReq{
BusinessCode: "DOUYIN", BusinessName: "抖音电商",
Operator: "admin",
})
businessId := result.ID
// 修改(传 ID 即修改)
result, _ = svc.SaveBusiness(ctx, &model.SaveBusinessReq{
ID: &businessId, BusinessCode: "DOUYIN",
BusinessName: "抖音电商(新版)", Operator: "admin",
})
// 查询
biz, _ := svc.GetBusiness(ctx, businessId)
fmt.Printf("%s: %s\n", biz.BusinessCode, biz.BusinessName)
// 删除
svc.DeleteBusiness(ctx, businessId)
// 全部列表
allBiz, _ := svc.GetAllBusinesses(ctx)
for _, b := range allBiz {
fmt.Printf("- %s (%s)\n", b.BusinessCode, b.BusinessName)
}
// ── 报表 CRUD ────────────────────────────────────────────
reportResult, _ := svc.SaveReport(ctx, &model.SaveReportReq{
BusinessCode: "DOUYIN",
ReportCode: "shop_daily_report",
ReportName: "抖音店铺日报",
StatTableName: "stat_douyin_shop_daily",
ConflictKeys: []string{"shop_id", "stat_date"},
Operator: "admin",
})
reportId := reportResult.ID
rpt, _ := svc.GetReport(ctx, reportId)
svc.DeleteReport(ctx, reportId)
_ = rpt
// ── 字段 CRUD ────────────────────────────────────────────
// 新增字段id 不传 = 新增)
fieldResult, _ := svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "DOUYIN", ReportCode: "shop_daily_report",
FieldCode: "order_amount", FieldName: "订单金额",
FieldType: "FLOAT", FieldRole: "INDICATOR",
IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "AVG", "MAX", "MIN"},
SortOrder: 10, GroupName: "金额", Operator: "admin",
})
fieldId := fieldResult.ID
// 修改字段(传 id = 更新)
svc.SaveField(ctx, &model.SaveFieldReq{
ID: &fieldId,
FieldName: "订单金额(元)",
Operator: "admin",
// ... 只传要修改的字段,未传的保持原值
})
f, _ := svc.GetField(ctx, fieldId)
svc.DeleteField(ctx, fieldId)
_ = f
// ── 抽取配置 CRUD ────────────────────────────────────────
ecResult, _ := svc.SaveExtractConfig(ctx, &model.SaveExtractConfigReq{
BusinessCode: "DOUYIN", ReportCode: "shop_daily_report",
ExtractCode: "extract_daily", ExtractName: "按天聚合抽取",
SourceTableName: "douyin_order_list", SourceTableAlias: "o",
TargetTableName: "stat_douyin_shop_daily",
ExtractMode: "AGGREGATE",
ExtractKeyField: "created_at",
GroupByFields: []string{"shop_id"},
FieldMappings: []model.FieldMapping{
{SourceField: "id", TargetField: "order_count", FieldType: "INT", AggregateFunction: "COUNT"},
{SourceField: "order_amount", TargetField: "order_amount", FieldType: "FLOAT", AggregateFunction: "SUM"},
},
Operator: "admin",
})
ecId := ecResult.ID
ec, _ := svc.GetExtractConfig(ctx, ecId)
allEc, _ := svc.GetExtractConfigs(ctx, "DOUYIN", "shop_daily_report")
svc.DeleteExtractConfig(ctx, ecId)
_, _ = ec, allEc
}
// ============================================================================
// 场景三:任意平台接入的通用模式(零硬编码)
// ============================================================================
//
// 假设要接入淘宝平台taobao_order_list 表已有数据。
// 全程不写任何代码,只需调 CRUD API。
func GenericPlatformExample() {
ctx := context.Background()
svc := report.GetService()
// 1. 前端注册业务
svc.SaveBusiness(ctx, &model.SaveBusinessReq{
BusinessCode: "TAOBAO", BusinessName: "淘宝电商",
Operator: "admin",
})
// 2. 注册报表 + 指定统计宽表名(以后表名不再手工管理)
svc.SaveReport(ctx, &model.SaveReportReq{
BusinessCode: "TAOBAO",
ReportCode: "shop_daily_report",
ReportName: "淘宝店铺日报",
StatTableName: "stat_taobao_shop_daily",
ConflictKeys: []string{"shop_id", "stat_date"},
Operator: "admin",
})
// 3. 前端选择统计维度(自由组合,随时增删改)
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "shop_id", FieldName: "店铺ID",
FieldType: "STRING", FieldRole: "DIMENSION",
SortOrder: 1, GroupName: "店铺", Operator: "admin",
})
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "shop_name", FieldName: "店铺名称",
FieldType: "STRING", FieldRole: "DIMENSION",
SortOrder: 2, GroupName: "店铺", Operator: "admin",
})
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "stat_date", FieldName: "统计日期",
FieldType: "DATE", FieldRole: "DIMENSION",
SortOrder: 3, GroupName: "时间", Operator: "admin",
})
// 4. 前端选择统计指标(自由组合,随时增删改)
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "order_count", FieldName: "订单数",
FieldType: "INT", FieldRole: "INDICATOR",
IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "COUNT", "AVG"},
SortOrder: 10, GroupName: "订单", Operator: "admin",
})
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "order_amount", FieldName: "订单金额",
FieldType: "FLOAT", FieldRole: "INDICATOR",
IsAggregatable: true, DefaultAggregate: "SUM",
ValidAggregates: []string{"SUM", "AVG", "MAX", "MIN"},
SortOrder: 11, GroupName: "金额", Unit: "元", Operator: "admin",
})
svc.SaveField(ctx, &model.SaveFieldReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
FieldCode: "buyer_count", FieldName: "下单买家数",
FieldType: "INT", FieldRole: "INDICATOR",
IsAggregatable: true, DefaultAggregate: "COUNT",
ValidAggregates: []string{"COUNT"},
SortOrder: 12, GroupName: "用户", Operator: "admin",
})
// 5. 配置抽取规则
svc.SaveExtractConfig(ctx, &model.SaveExtractConfigReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
ExtractCode: "extract_daily", ExtractName: "淘宝按天聚合",
SourceTableName: "taobao_order_list", SourceTableAlias: "o",
TargetTableName: "stat_taobao_shop_daily",
ExtractMode: "AGGREGATE",
ExtractKeyField: "created_at",
GroupByFields: []string{"shop_id"},
FieldMappings: []model.FieldMapping{
{SourceField: "shop_id", TargetField: "shop_id", FieldType: "STRING"},
{SourceField: "shop_name", TargetField: "shop_name", FieldType: "STRING"},
{SourceField: "id", TargetField: "order_count", FieldType: "INT", AggregateFunction: "COUNT"},
{SourceField: "order_amount", TargetField: "order_amount", FieldType: "FLOAT", AggregateFunction: "SUM"},
{SourceField: "buyer_id", TargetField: "buyer_count", FieldType: "INT", AggregateFunction: "COUNT"},
},
BatchSize: 1000,
Operator: "admin",
})
// 6. 定时任务每天执行
svc.ExtractDailyData(ctx, "TAOBAO", "shop_daily_report", "2026-06-10", "cron")
// 7. 前端实时查询
req := &model.UserSelectQueryReq{
BusinessCode: "TAOBAO", ReportCode: "shop_daily_report",
Dimensions: []string{"shop_id", "shop_name"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "total"},
{FieldCode: "order_count", Aggregate: "SUM", Alias: "orders"},
},
TimeRange: &model.TimeRange{StartDate: "2026-06-01", EndDate: "2026-06-10"},
Page: 1, PageSize: 20,
}
resp, _ := svc.QueryReportByUserSelect(ctx, req)
fmt.Printf("查询结果: 共%d条, 耗时%dms\n", resp.Total, resp.ExecTimeMs)
// 平台接入完毕。全程零代码改动。
}
// ============================================================================
// 场景四:在外部业务服务(如 goview-report中调用
// ============================================================================
func ExternalServiceExample() {
ctx := context.Background()
svc := report.GetService()
// 直接用,不需要初始化,内部自动懒加载和建表。
today := time.Now().Format("2006-01-02")
// 按天抽取
svc.ExtractDailyData(ctx, "KUAISHOU", "shop_daily_report", today, "cron")
// 实时查询
svc.QueryReportByUserSelect(ctx, &model.UserSelectQueryReq{
BusinessCode: "KUAISHOU", ReportCode: "shop_daily_report",
Dimensions: []string{"shop_id", "shop_name"},
Indicators: []model.IndicatorSelect{
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "total_amount"},
},
TimeRange: &model.TimeRange{StartDate: "2026-06-01", EndDate: today},
Page: 1, PageSize: 20,
})
}
// ============================================================================
// 场景五Direct 模式(逐行抽取,不做聚合)
// ============================================================================
//
// 适用于源表已经是一行一条统计记录的场景(如已预聚合的报表源表)。
func DirectModeExample() {
ctx := context.Background()
svc := report.GetService()
svc.SaveBusiness(ctx, &model.SaveBusinessReq{
BusinessCode: "HDWL", BusinessName: "HDWL业务", Operator: "admin",
})
svc.SaveReport(ctx, &model.SaveReportReq{
BusinessCode: "HDWL", ReportCode: "shop_daily_stat",
ReportName: "HDWL店铺日报", StatTableName: "stat_hdwl_shop_daily",
ConflictKeys: []string{"report_date"},
Operator: "admin",
})
// Direct 模式:不聚合,逐行映射
svc.SaveExtractConfig(ctx, &model.SaveExtractConfigReq{
BusinessCode: "HDWL", ReportCode: "shop_daily_stat",
ExtractCode: "extract_direct", ExtractName: "直接逐行抽取",
SourceTableName: "hdwl_daily_summary", SourceTableAlias: "s",
TargetTableName: "stat_hdwl_shop_daily",
ExtractMode: "DIRECT", // ← DIRECT 模式
ExtractKeyField: "report_date",
FieldMappings: []model.FieldMapping{
{SourceField: "shop_id", TargetField: "shop_id", FieldType: "STRING"},
{SourceField: "shop_name", TargetField: "shop_name", FieldType: "STRING"},
{SourceField: "total_sale", TargetField: "sale_amount", FieldType: "FLOAT"},
{SourceField: "total_orders", TargetField: "order_count", FieldType: "INT"},
},
Operator: "admin",
})
svc.ExtractDailyData(ctx, "HDWL", "shop_daily_stat", "2026-06-10", "cron")
}
// ============================================================================
// 场景六:前端交互流程参考
// ============================================================================
//
// 前端代码调用示例(伪代码,展示 API 调用顺序):
//
// // 1. 页面加载 → 获取业务列表 → 渲染下拉框
// GET /api/report/businesses
// 返回: [{ businessCode: "KUAISHOU", businessName: "快手电商" }, ...]
//
// // 2. 用户选择业务 → 获取报表列表
// GET /api/report/reports?businessCode=KUAISHOU
// 返回: [{ reportCode: "shop_daily_report", reportName: "快手店铺日报" }, ...]
//
// // 3. 用户选择报表 → 获取可用字段 → 渲染维度/指标/筛选选择器
// GET /api/report/fields?businessCode=KUAISHOU&reportCode=shop_daily_report
// 返回: { dimensions: [...], indicators: [...], filters: [...] }
//
// // 4. 用户选好条件 → 查询
// POST /api/report/query
// Body: {
// "businessCode": "KUAISHOU", "reportCode": "shop_daily_report",
// "dimensions": ["shop_id", "shop_name"],
// "indicators": [
// {"fieldCode": "order_amount", "aggregate": "SUM", "alias": "total"},
// {"fieldCode": "order_count", "aggregate": "SUM", "alias": "count"}
// ],
// "timeRange": {"startDate": "2026-06-01", "endDate": "2026-06-10"},
// "orderBy": [{"fieldCode": "total", "direction": "DESC"}],
// "page": 1, "pageSize": 20
// }
// 返回: { list: [...], total: 152, page: 1, totalPages: 8, execTimeMs: 45 }
//
// // 5. 翻页/换维度/换指标 → 重新调 Step 4
//
// 管理后台(用户可自行维护配置):
//
// // 新增业务
// POST /api/report/business/save
// Body: { "businessCode": "TAOBAO", "businessName": "淘宝电商" }
//
// // 新增/修改字段(用户自定义统计维度)
// POST /api/report/field/save
// Body: { "businessCode": "TAOBAO", "reportCode": "shop_daily_report",
// "fieldCode": "category", "fieldName": "商品类目",
// "fieldType": "STRING", "fieldRole": "DIMENSION" }
//
// // 配置抽取规则
// POST /api/report/extractConfig/save
// Body: { "businessCode": "TAOBAO", "reportCode": "shop_daily_report",
// "extractCode": "extract_daily", "sourceTableName": "taobao_order_list",
// "extractMode": "AGGREGATE", "groupByFields": ["shop_id"],
// "fieldMappings": [...] }
// ============================================================================
// Direct vs AGGREGATE 模式对比
// ============================================================================
//
// ┌──────────┬──────────────────────────────────────────────┐
// │ 模式 │ 行为 │
// ├──────────┼──────────────────────────────────────────────┤
// │ DIRECT │ 逐行映射1:1 从源表复制到目标表 │
// │ │ 适用:源表已是一行一统计 │
// │ │ SQL: SELECT ... FROM source │
// ├──────────┼──────────────────────────────────────────────┤
// │ AGGREGATE│ GROUP BY + 聚合函数N:1 聚合 │
// │ │ 适用:源表是明细表(如订单行) │
// │ │ SQL: SELECT ... SUM() COUNT() ... │
// │ │ FROM source GROUP BY groupByFields │
// └──────────┴──────────────────────────────────────────────┘
//
// 可聚合函数: SUM / COUNT / AVG / MAX / MIN
// 字段角色: DIMENSION(维度) / INDICATOR(指标) / FILTER(筛选) / FILTER_ONLY
// 字段类型: STRING / INT / FLOAT / DATE / DATETIME / JSONB
// 操作符: = / != / > / < / >= / <= / IN / LIKE / BETWEEN
// 时间分组: day / week / month / quarter
*/