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) // 实际抽取生成的 SQL(AGGREGATE 模式): // // 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 */