Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e736ebc9f7 | ||
|
|
06c64d1069 | ||
|
|
51b266cb39 | ||
|
|
b45c4a2316 | ||
|
|
802139c349 | ||
|
|
7f6ec3a757 | ||
|
|
3d965ba11e | ||
|
|
b25cea818e | ||
|
|
e3fc2fdadb | ||
|
|
918ed552e4 | ||
|
|
22f96a2dce | ||
|
|
b78ad2b561 | ||
|
|
9eeb7584b2 | ||
|
|
9e283f67a0 | ||
|
|
4c2f7f7326 | ||
|
|
7336f2d438 | ||
|
|
6e6f9087b6 | ||
|
|
ae9ada7f9d | ||
|
|
276ae55de9 | ||
|
|
32bd6c8895 | ||
|
|
b4fc6f54af | ||
|
|
e5133eea34 | ||
|
|
9dc12634f5 | ||
|
|
419473f266 | ||
|
|
285a0fc632 | ||
|
|
dbd9aa54f0 | ||
|
|
062a09fc59 | ||
|
|
812693caae | ||
|
|
15db71b7ba | ||
|
|
3ced686cb5 | ||
|
|
a1a259733d | ||
|
|
162bab15e6 | ||
|
|
3814c95047 | ||
|
|
222815a12a | ||
|
|
88e46d7877 | ||
|
|
2d844645a0 | ||
|
|
ac9ec66890 | ||
|
|
e1c0981bf9 | ||
|
|
9e2167d843 | ||
|
|
b6f9a832c0 | ||
|
|
ee713c5c0f | ||
|
|
eb5e0af308 | ||
|
|
5797fbb43e | ||
|
|
556ec5075e | ||
|
|
5d72dd3aff | ||
|
|
000ea03420 | ||
|
|
073a098317 | ||
|
|
a25bba717f | ||
|
|
5f2c9c3855 | ||
|
|
14db0dd2fe | ||
|
|
5b8a4c103d | ||
|
|
4f3ad39eeb | ||
|
|
c8162b1bfe | ||
|
|
2c6a8490b8 | ||
|
|
41089cca2d |
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(go build *)",
|
||||
"Bash(go test *)",
|
||||
"Bash(go run *)",
|
||||
"Bash(go mod *)",
|
||||
"Bash(curl *)",
|
||||
"Bash(npx *)",
|
||||
"Bash(brew *)",
|
||||
"Bash(npm install *)",
|
||||
"Bash(npm run *)",
|
||||
"Bash(python3 *)",
|
||||
"Bash(psql *)",
|
||||
"Bash(PGPASSWORD=*)",
|
||||
"Bash(lsof *)",
|
||||
"Bash(pkill *)",
|
||||
"Bash(sleep *)",
|
||||
"Bash(mkdir *)",
|
||||
"Bash(which *)",
|
||||
"Bash(pwd *)",
|
||||
"Bash(echo *)",
|
||||
"Bash(source *)",
|
||||
"Bash(export *)",
|
||||
"Bash(unset *)",
|
||||
"Bash(package *)",
|
||||
"Bash(kill *)"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
# 2026-06-01 工作记录
|
||||
|
||||
## 快手代发订单详情新增
|
||||
- 新增 `open.dropshipping.order.merchant.detail` 到 `seed_data_kuaishou.sql`
|
||||
- 第19个接口,POST、prefetch 自 dropshipping_order_list、single_record
|
||||
|
||||
## 快手接口全面审查
|
||||
发现 prefetch 相关7个接口存在3个严重Bug:
|
||||
1. Prefetch 阶段业务参数未包入 param JSON(body_wrapper_field 未生效)
|
||||
2. Prefetch 阶段 `parseResp(nil config)` 无法解析 data.items/data.cpsOrderList 等路径
|
||||
3. Prefetch 循环未处理游标分页(固定页码递增)
|
||||
|
||||
## Bug 修复(dynamic_sync.go)
|
||||
1. `syncWithPrefetch` 改用 `buildReqBody(prefetchIface)` 构建请求,body_wrapper_field 正确包装
|
||||
2. 改用 `parseRespExt(resp.Body, prefetchIface.ResponseConfig)` 解析响应
|
||||
3. 支持游标分页的 prefetch 循环(cursor/pcursor)
|
||||
4. `buildPrefetchParams` 增加过滤 body_wrapper_field/exclude_from_wrapper/cursor_pagination/time_field_mode
|
||||
5. 新增 `collectPrefetchEntities` 辅助函数
|
||||
6. 修复并发阶段 `inQuery` 变量缺失的问题
|
||||
|
||||
## 同步调度并发锁修复
|
||||
1. `SyncByConfig` 新增 `syncRunningMap` 内存锁(sync.Map.LoadOrStore),防止同一接口并发执行
|
||||
2. 调度器从 `time.NewTicker` 改为 `for { run(); time.Sleep(interval) }`,前一次完成后才开始计时
|
||||
|
||||
## MEMORY.md 更新
|
||||
- response_config 增加 `single_record` 字段说明
|
||||
- prefetch 流程补充游标分页、参数构建、响应解析的详细说明
|
||||
- 新增「并发保护」章节,记录内存锁和调度器时序
|
||||
- 调度器章节补充 for+sleep 模式说明
|
||||
|
||||
## 钉钉平台新增
|
||||
- 创建 `seed_data_dingtalk.sql`:钉钉开放平台 + 部门列表接口
|
||||
- auth_type: OAUTH2,access_token 在 URL 查询参数中
|
||||
- 部门列表:POST + parameters_location=query + recursive 递归遍历,响应 `result` 为数组
|
||||
- 表名:`dingtalk_department`,冲突键:dept_id
|
||||
|
||||
## 递归遍历支持
|
||||
- 新增 `RecursiveConfig` + `parseRecursiveConfig` + `syncRecursive` 函数
|
||||
- `syncRecursive`:BFS 队列遍历,先查根级 → 对每个子部门递归查下级,防重复 + 最大深度限制
|
||||
- `buildReqBody` / `buildPrefetchParams` 增加过滤 `recursive` / `max_recursive_depth`
|
||||
|
||||
## 钉钉智能人事平台
|
||||
- 新建 `seed_data_dingtalk_hrm.sql`:钉钉智能人事专用平台
|
||||
- auth_type: OAUTH2 + Header 认证(`x-acs-dingtalk-access-token`),独立于 oapi 平台
|
||||
- 企业职位列表 `position_list`:POST + cursor 分页 + hasMore,API base `api.dingtalk.com`
|
||||
|
||||
## 钉钉角色列表 + 引擎增强
|
||||
- `parseRespExt` 增加 `has_more_field` 支持 + 数字游标(float64→string)
|
||||
- `syncWithPrefetch` 支持递归预取来源(BFS)
|
||||
- `buildReqBody` 增加 `pagination_mode: "offset"` 支持(计算 offset = (page-1)*pageSize)
|
||||
- `syncSingleAPI` 增加 hasMore 驱动的分页循环(钉钉 offset/size + hasMore)
|
||||
- 新增 `hasMoreCheck` 辅助函数
|
||||
- 新增 `seed_data_dingtalk.sql` 角色列表接口:偏移分页 + hasMore
|
||||
@@ -0,0 +1,36 @@
|
||||
# 2026-06-03 工作记录
|
||||
|
||||
## 钉钉智能薪酬平台对接
|
||||
- 新增 `dingtalk_salary` 平台 + 2个接口到 `sql/seed_data_dingtalk_salary.sql`
|
||||
- 新认证类型 `APP_SIGNATURE`:app-id + signature 头部认证
|
||||
- 签名算法:MD5(body_string + app_secret).toUpperCase()
|
||||
|
||||
### 接口明细
|
||||
1. **salary_dept_list** — `POST /oapi/salary/dept/list`,无入参,单次拉取全量部门
|
||||
2. **salary_statistics** — `POST /oapi/salary/statistics/dept`,prefetch 自 salary_dept_list,row_inject statisticsMonth
|
||||
|
||||
### 代码改动
|
||||
- `api_client.go`: `applyAuthHeader` 扩展支持 `APP_SIGNATURE` 认证,新增 `applyAppSignatureAuth` + `computeBodySignature`
|
||||
- `platform_manager.go`: 新增 `APP_SIGNATURE` case,从 auth_config 提取 app_id/app_secret
|
||||
- `dynamic_sync.go`:
|
||||
- `toFloat64` 扩展支持 string→float64(适配 salary API 返回 code: "200" 字符串)
|
||||
- `buildReqBody`/`buildPrefetchParams` skip list 新增 `row_inject`
|
||||
- 新增 `injectRowFields` 函数:将请求参数注入响应行
|
||||
- `injectRowFields` 调用于 syncWithPrefetch 中3处 row 注入点
|
||||
|
||||
## 新增钉钉智能薪酬人力成本报表接口 (2026-06-03 第二阶段)
|
||||
|
||||
### 新增接口
|
||||
3. **salary_statistic_report** — `POST /oapi/salary/statistic/report/data`,无分页,single_record,获取指定月份的人力成本报表
|
||||
- 入参:`calBizId`(格式 yyyyMM"M",如 "202606M",为空默认当月)
|
||||
- 出参:`data.calBizId` + `data.sumStatisticsData`(成本项数组 JSONB)
|
||||
- 表:`dingtalk_salary_statistic_report`,conflict_key: `calBizId`
|
||||
|
||||
4. **salary_statistic_report_group** — `POST /oapi/salary/statistic/report/groupData`,无分页,single_record
|
||||
- 入参:`calBizId` + `salaryGroupName`(必填),使用 `row_inject` 注入 salaryGroupName
|
||||
- 表:`dingtalk_salary_statistic_report_group`,conflict_keys: `calBizId` + `salaryGroupName`
|
||||
|
||||
### 代码改动
|
||||
- `dynamic_sync.go`:修复主同步路径(非 prefetch)中 `injectRowFields` 未调用的问题
|
||||
- 在 5 处 savePage 调用前添加 injectRowFields:初始页、游标分页循环、hasMore 分页、普通分页(有/无 response_config)
|
||||
- 使 `row_inject` 在单记录(single_record)非 prefetch 接口中也能生效
|
||||
@@ -0,0 +1,15 @@
|
||||
# 2026-06-08 工作日志
|
||||
|
||||
## goview-report 项目初始化
|
||||
- 基于 GoView (gitee.com/bufanyun/go-view-server) 初始化报表引擎项目
|
||||
- 位置: `/Users/xujiaqian/GolandProjects/HDWL/goview-report/`
|
||||
- 改造内容:
|
||||
- 模块名 hotgo → goview-report
|
||||
- MySQL 驱动替换为 PostgreSQL 驱动 (github.com/gogf/gf/contrib/drivers/pgsql/v2)
|
||||
- Go 版本升级到 1.26.0,GoFrame 升级到 v2.10.0
|
||||
- config.yaml 中数据库连接改为 pgsql://postgres:root@127.0.0.1:5432/engine
|
||||
- 端口保持 8090,与 data-engine(:3002) 不冲突
|
||||
- 元数据表前缀 hg_,data-engine 同步表无前缀,同库不同前缀
|
||||
- 创建 Dockerfile + k8s/deployment+service+configmap
|
||||
- 编译验证通过
|
||||
- 与 data-engine 关系: 读写分离,goview-report 只读 PG 中的同步表做报表展示
|
||||
@@ -0,0 +1,18 @@
|
||||
# 2026-06-09 工作日志
|
||||
|
||||
## 公共查询接口开发
|
||||
- 新增 `/public/query` POST 接口,支持:
|
||||
- 字段白名单验证(只允许表定义中声明的字段)
|
||||
- 表名白名单验证(只允许 api_interface 中有 table_definition 的表)
|
||||
- WHERE 条件(支持 `_eq/_ne/_gt/_lt/_ge/_le/_like/_in/_between` 操作符)
|
||||
- GROUP BY / ORDER BY / 分页
|
||||
- 强制 tenant_id = 1 租户隔离
|
||||
- 新增辅助接口:
|
||||
- `GET /public/tables` - 获取可查询表列表
|
||||
- `GET /public/tables/{table}/columns` - 获取表字段列表
|
||||
- `DELETE /public/cache/clear` - 清除表缓存
|
||||
- 文件位置:
|
||||
- `model/dto/public/public_query_dto.go`
|
||||
- `service/public/public_query_service.go`
|
||||
- `controller/public/public_query_controller.go`
|
||||
- 同步修复 `dynamic_sync.go` 中残留的无效代码 "了呢"
|
||||
@@ -0,0 +1,179 @@
|
||||
# 2026-06-10
|
||||
|
||||
## 通用报表引擎 - 补充前端 CRUD 能力
|
||||
|
||||
为 `common/report` 包补充了配置的 CRUD 接口,使前端可以直接维护报表配置,无需操作数据库。
|
||||
|
||||
### 新增文件变更
|
||||
- `model/model.go`: 新增 SaveBusinessReq/SaveReportReq/SaveFieldReq/SaveExtractConfigReq 等请求结构体,SaveResult/DeleteResult 响应结构体
|
||||
- `config/loader.go`: 新增 Create/Update/Delete + GetByID 方法(Business/Report/Field/ExtractConfig 四种配置)
|
||||
- `api.go`: 新增 SaveBusiness/SaveReport/SaveField/SaveExtractConfig + Delete/Get 接口(新增/修改合一,id 为空则新增)
|
||||
- `example_usage.go`: 新增第五、六节,CRUD 调用示例 + 任意新平台零代码接入流程
|
||||
|
||||
### 关键设计决策
|
||||
- **Save 合一**:通过 `ID *int64` 区分新增(nil)和修改(有值),简化前端调用
|
||||
- **软删除**:status → INACTIVE + deleted_at=NOW(),数据不丢失
|
||||
- **缓存失效**:写操作后自动失效对应业务/报表缓存,保证下次读取一致性
|
||||
- 所有 CRUD 方法先调用 initTables() 确保表存在
|
||||
|
||||
### example_usage.go 完整重写
|
||||
将示例文件从分节的注释风格重写为6个完整场景的 Go 函数示例:
|
||||
1. **快手电商完整接入**(最详细,从业务注册到前端查询6步全流程)
|
||||
2. **已有配置的 CRUD 二次开发参考**(业务/报表/字段/抽取 增删改查)
|
||||
3. **任意平台接入通用模式**(淘宝示例,零硬编码)
|
||||
4. **外部业务服务调用**(goview-report 等直接 import)
|
||||
5. **Direct 模式示例**(不聚合的逐行抽取)
|
||||
6. **前端 HTTP API 交互流程**(伪代码,展示 API 调用顺序)
|
||||
|
||||
每个场景都有实际可运行的代码片段、生成的 SQL 预览、返回值输出示例。
|
||||
|
||||
## 报表引擎管理前端页面
|
||||
|
||||
为报表引擎创建了可直接使用的前端管理页面,访问 `/admin/report`。
|
||||
|
||||
### 新增文件
|
||||
- `controller/report/report_controller.go`: HTTP API Controller,16个端点暴露公共报表服务的全部能力(业务/报表/字段/抽取配置的 CRUD、数据抽取触发、自动建表、用户选择查询)
|
||||
- `controller/report/report_admin_controller.go`: 嵌入式HTML SPA,5个Tab页:
|
||||
- **业务管理**:列表 CRUD
|
||||
- **报表配置**:按业务筛选列表 CRUD
|
||||
- **字段配置**:按业务+报表筛选,维度/指标/筛选角色管理
|
||||
- **抽取配置**:按业务+报表筛选,支持 DIRECT/AGGREGATE 模式配置 + 一键执行抽取
|
||||
- **数据查询**:可视化维度选择器、指标聚合选择器、筛选条件构建器、查询结果展示
|
||||
- `main.go`: 注册 reportCtrl.ReportController 到 RouteRegister + `/admin/report` 页面路由
|
||||
|
||||
### 技术要点
|
||||
- 纯原生 HTML/CSS/JS,零框架依赖,与现有管理后台 `/admin` 风格统一
|
||||
- 所有 CRUD 通过 `ID *int64` 区分新增(nil)/修改(有值)
|
||||
- 查询Tab支持动态字段选择:维度多选、指标聚合方式选择、筛选条件动态构建(支持 BETWEEN/IN 等操作符)
|
||||
- 管理后台页面增加了返回链接 `← 返回管理后台`
|
||||
|
||||
## 修复 report_common_ddl.sql 语法错误
|
||||
|
||||
PostgreSQL 不支持 MySQL 风格的内联 `COMMENT '...'` 语法。修复了全部5张表的列定义:
|
||||
- 移除所有列定义中的内联 `COMMENT 'xxx'`(MySQL 语法)
|
||||
- 已有的 `COMMENT ON COLUMN` 独立语句保留不变
|
||||
- `report_extract_log` 表补充了缺失的 `COMMENT ON COLUMN` 语句(business_code/report_code/extract_code/stat_date/extract_type/status/total_count/success_count/fail_count)
|
||||
|
||||
关键区别:MySQL 支持 `column_name VARCHAR(64) COMMENT 'xxx'`,PostgreSQL 必须用 `COMMENT ON COLUMN table.column IS 'xxx'`。
|
||||
|
||||
## 补充 tenant_id 到报表系统表
|
||||
|
||||
5张报表系统表原先缺失 `tenant_id`,与项目核心表(`init_core_tables.sql`)不一致。修改内容:
|
||||
- **DDL SQL** (`sql/report_common_ddl.sql`):5张表全部新增 `tenant_id BIGINT NOT NULL DEFAULT 0` 列,所有 UNIQUE 约束和索引都加入 tenant_id
|
||||
- **Go initTables** (`common/report/report.go`):同步更新 5 张表的 DDL 定义,加上 tenant_id
|
||||
- 约束变更:`uk_business_report_code UNIQUE (business_code, report_code)` → `(tenant_id, business_code, report_code)`,其余类推
|
||||
|
||||
当前 report 模块租户支持为占位符级别(硬编码 tenant_id=1),后续需从 context 动态获取。
|
||||
- **单独迁移脚本**: `sql/report_add_tenant_id.sql`,对已存在的表执行 ALTER TABLE ADD COLUMN + 重建约束/索引
|
||||
|
||||
## 快手电商报表初始化种子数据
|
||||
|
||||
**初版(已废弃)**:字段名随意捏造,与真实 DDL 不对齐。
|
||||
|
||||
**重写版** `sql/seed_data_report_kuaishou.sql`(基于 seed_data_kuaishou.sql 实际 DDL):
|
||||
- 1 条业务配置 (kuaishou_ecommerce)
|
||||
- 5 条报表配置:order_analysis / item_analysis / refund_analysis / cps_order_analysis / dropshipping_analysis
|
||||
- 76 条字段配置(订单23+商品15+售后12+分销13+代发13),含维度/指标/衍生字段
|
||||
- 5 条抽取配置(AGGREGATE 聚合模式,每个报表一条)
|
||||
|
||||
**严格设计原则**:
|
||||
- sourceField 对齐源表实际列名(camelCase:oid, status, createTime, totalFee, buyerNick 等)
|
||||
- targetField / field_code 使用 snake_case(stat_date, total_fee, buyer_nick 等)
|
||||
- 金额字段源表存分(BIGINT),报表提供衍生元字段(CALCULATED expression / 100.0)
|
||||
- 增量抽取依据统一用 updateTime(毫秒时间戳 BIGINT)
|
||||
- transform_rules:ts_to_date FORMAT createTime → stat_date(yyyy-MM-dd)
|
||||
- 所有 INSERT 使用 ON CONFLICT DO NOTHING 保证幂等
|
||||
|
||||
## 修复报表管理前端编辑不显示数据问题
|
||||
|
||||
在 `report_admin_controller.go` 中修复了编辑弹窗不显示数据的问题:
|
||||
|
||||
**根因**:
|
||||
- GoFrame `MiddlewareHandlerResponse` 包裹响应为 `{code:0, data: <struct>}`,而单条查询返回结构体为 `getBusinessRes{Data: ...}` 序列化为 `{"data": {...}}`,最终响应为 `{code:0, data: {"data": {...}}}` 双层嵌套
|
||||
- 某些 GoFrame 版本可能序列化 Go 字段名(首字母大写 `Data`)而非 json tag(`data`),导致 `res.data` 为 undefined
|
||||
|
||||
**修复内容**:
|
||||
1. `api()` 函数增加兼容逻辑:当 `json.data.Data` 存在而 `json.data.data` 不存在时,自动取 `json.data.Data`
|
||||
2. `api()` 函数增加 `console.log` 调试日志,打印原始响应和解包后的数据
|
||||
3. 四个编辑函数(`openBizModal/openReportModal/openFieldModal/openExtractModal`):
|
||||
- 增加 fallback:`res.data !== undefined ? res.data : res.Data`
|
||||
- 增加数据有效性校验:`if (!d || !d.id)` 时弹出错误提示并阻止打开空表单
|
||||
- 增加 `console.log` 调试日志
|
||||
|
||||
## 修复 report_report_config 查询 SQL 永假条件问题
|
||||
|
||||
**现象**:查询 `report_report_config` 时生成的 SQL 包含 `WHERE (("id" = 1) AND ("id" = 0 AND "business_code" = '' AND ...))`,id 不可能同时为 1 和 0,导致查询始终返回 0 行。
|
||||
|
||||
**根因**:`common/db/gfdb/gfdb.go` 中 `Model()` 方法使用了 `OmitNil()`(只过滤 nil 指针,不过滤零值)。当 GoFrame v2 内部的软删除机制(`deleted_at IS NULL`)自动构建 struct WHERE 条件时,由于 `OmitNil()` 不忽略零值字段,结构体中所有字段的零值(`id=0`, `business_code=""`, `report_code=""` 等)都被加入 WHERE 条件,与显式的 `Where("id", 1)` 叠加形成 `id=1 AND id=0` 的永假条件。
|
||||
|
||||
**修复**:`OmitNil()` **保留**,额外追加 `.OmitEmptyWhere()`,两者同时生效:
|
||||
- `OmitNil()`:保持 INSERT/UPDATE 的 Data() 行为不变(nil 指针/map 字段不写入 SQL,走 DB 默认值),避免破坏数据引擎写入逻辑
|
||||
- `OmitEmptyWhere()`:过滤 struct WHERE 条件中的零值字段,解决软删除 hook 注入零值条件的问题
|
||||
|
||||
最终代码:`OmitNil().OmitEmptyWhere()`
|
||||
|
||||
**影响范围**:`common/db/gfdb/gfdb.go` - 全局 DB Model 初始化。写入行为完全兼容,查询条件不再包含零值 struct 字段。
|
||||
|
||||
**安全性审计(2026-06-10)**:逐文件审查了数据引擎核心同步路径全部 40+ 处 `gfdb.Model()` 调用:
|
||||
- 100% 传字符串表名(非 struct),不会触发 struct WHERE 推断
|
||||
- 100% 使用 `Where("col", val)` 字符串键值对(非 struct/map),`OmitEmptyWhere()` 对此无效
|
||||
- `OmitEmptyWhere()` 不影响 `Data()`/`Insert()`/`Update()`/`Save()`
|
||||
- 结论:`OmitEmptyWhere()` 对数据引擎核心同步逻辑完全无影响,唯一生效场景是 `common/report` 软删除 hook
|
||||
|
||||
## 修复 GetXxxByID 方法零值结构体返回问题
|
||||
|
||||
**现象**:前端编辑报表/业务/字段/抽取配置时一直提示"未获取到XX数据(ID=N)"。与上述 OmitNil 问题同根:SQL 返回 0 行时,GoFrame 的 `One()` 可能不返回 `sql.ErrNoRows`(驱动差异),此时 `GetXxxByID` 判断 `err == nil` 就返回了零值结构体 `{ID: 0, ...}`。Controller 包装为 `{code:0, data:{"data":{id:0}}}`,前端 `!d.id` 判定失败弹出错误提示。
|
||||
|
||||
**修复**:`common/report/config/loader.go` 中全部 7 个 `One()` 调用方法,在 `err` 检查之外增加 `result.IsEmpty()` 检查:
|
||||
- `GetBusinessByID` / `GetReportByID` / `GetFieldByID` / `GetExtractConfigByID`:四者模式一致,`_` 改为 `result` 变量并追加 `if result.IsEmpty()`
|
||||
- `GetBusiness` / `GetReport`(by code)、`GetExtractLog`:同上,追加 `IsEmpty()` 守卫
|
||||
|
||||
**双重防御**:gfdb.go 的 `OmitEmptyWhere()` 是根本修复(消除错误 SQL);loader.go 的 `IsEmpty()` 是兜底守卫(即使 SQL 返回 0 行也不返回零值结构体)。
|
||||
|
||||
## 修复编辑弹窗数据提取的三层兼容
|
||||
|
||||
**根因**:`api()` 返回 `json.data` 给调用者。当 GoFrame `MiddlewareHandlerResponse` 双层包裹时(`{code:0, data: {"data": {...}}}`),`api()` 返回 `{"data": {...}}`,调用者通过 `res.data` 正确提取。但当 GoFrame 单层包裹时(`{code:0, data: {...}}`),`api()` 直接返回 `{...config...}`,调用者 `res.data` 为 undefined → `d` 为 undefined → 触发 "未获取到XX数据" 错误。
|
||||
|
||||
**修复**:4 个编辑函数(`openBizModal`/`openReportModal`/`openFieldModal`/`openExtractModal`)的数据提取改为三层 fallback:
|
||||
```javascript
|
||||
const d = res && res.data !== undefined ? res.data : (res && res.Data !== undefined ? res.Data : res);
|
||||
```
|
||||
- 第一层:`res.data`(双层包裹,json tag 小写)
|
||||
- 第二层:`res.Data`(Go 字段名大写 D)
|
||||
- 第三层:`res` 本身(单层包裹,数据直接在 res 里)
|
||||
|
||||
同时 console.log 增加打印 `raw:` 和 `data:` 便于排查。
|
||||
|
||||
## 修复报表引擎实体缺失 TenantId 字段 → 查询 tenant_id 不匹配返回零值
|
||||
|
||||
**现象**:`GET /report/business?id=1` 返回 `code: 0` 但 data 为全零值 `{id: 0, businessCode: "", ...}`,尽管 DB 中实际有数据。
|
||||
|
||||
**根因**:四个报表引擎实体结构体均缺失 `TenantId` 字段,但 DDL 中表定义有 `tenant_id BIGINT NOT NULL DEFAULT 0`:
|
||||
1. `CreateBusiness` 序列化 `BusinessConfig` 时,JSON 中无 `tenant_id` → insert map 中无 `tenant_id` key
|
||||
2. `insertHook` 检查 `in.Data[i]["tenant_id"]` 是否存在 → 不存在 → 不设置 → DB 用 DEFAULT 0
|
||||
3. `selectHook` 从 `ctxWithUser` 获取 `TenantId=1`,注入 `WHERE tenant_id = 1`
|
||||
4. DB 行 `tenant_id=0` 与查询 `tenant_id=1` 不匹配 → 查不到行 → 返回零值结构体
|
||||
|
||||
**修复**(`common/report/model/model.go`):给 `BusinessConfig`、`ReportConfig`、`FieldConfig`、`ExtractConfig` 四个结构体新增 `TenantId uint64 \`orm:"tenant_id" json:"tenant_id"\``。
|
||||
|
||||
**原理**:添加后序列化 JSON 包含 `"tenant_id": 0`,`insertHook` 检测到 key 存在且为零值,自动赋值为 `userInfo.TenantId`。
|
||||
|
||||
**调试日志**(`common/report/config/loader.go`):`GetBusinessByID` 增加 `g.Log().Infof` 打印 `id`、`err`、`IsEmpty`、`biz.ID`、`biz.BusinessCode`,便于排查后续类似问题。同时补充 import `"github.com/gogf/gf/v2/frame/g"`。
|
||||
|
||||
## 修复 GoFrame One(&struct) 直接映射失效 → 改用两步模式
|
||||
|
||||
**现象**:修复 TenantId 后,SQL 能正确执行并返回1行(`[rows:1]`, `IsEmpty=false`),但 struct 字段全为零值(`biz.ID=0, biz.BusinessCode=""`)。
|
||||
|
||||
**根因**:`loader.go` 中 7 处 `One(&struct)` 直接传结构体指针,在 GoFrame v2.10.0 + `selectHook` 修改 SQL 的组合下,struct 自动映射失效 —— 查询返回 `gdb.Record` 但未 populate 结构体。
|
||||
|
||||
**修复**:全部 7 处改为项目已验证的两步模式(参照 `dao/dict/api_interface_dao.go`):
|
||||
```go
|
||||
// 旧写法(struct 映射失效)
|
||||
result, err := gfdb.DB(ctx).Model(ctx, "table").Where(...).One(&biz)
|
||||
|
||||
// 新写法(先取 Record,再手动 Struct)
|
||||
r, err := gfdb.DB(ctx).Model(ctx, "table").Where(...).One()
|
||||
if err = r.Struct(&biz); err != nil { return nil, err }
|
||||
```
|
||||
|
||||
**影响的方法**:`GetBusiness`、`GetBusinessByID`、`GetReport`、`GetReportByID`、`GetFieldByID`、`GetExtractConfigByID`、`GetExtractLog`。
|
||||
@@ -0,0 +1,616 @@
|
||||
# 数据引擎 - 项目记忆 (MEMORY.md)
|
||||
|
||||
## 项目概述
|
||||
Golang + GoFrame v2 + PostgreSQL 的配置化数据同步引擎。通过 `api_datasource_platform` + `api_interface` 两表驱动,支持多平台多接口的数据同步,无需重新编译代码。
|
||||
|
||||
## 核心表结构
|
||||
见 `sql/init_core_tables.sql`:
|
||||
- `api_datasource_platform` — 数据源平台配置
|
||||
- `api_interface` — API 接口配置(request_config / response_config / table_definition 三大 JSONB)
|
||||
- `sync_tracker` — 同步跟踪(last_sync_time, sync_status)
|
||||
- `sync_task_log` — 同步任务日志(用于补偿重试)
|
||||
|
||||
## 认证类型(auth_type)模板
|
||||
|
||||
### 1. OAuth2(腾讯广告)
|
||||
```
|
||||
auth_type: OAUTH2
|
||||
token: access_token 值
|
||||
client_id / client_secret: OAuth2 凭证
|
||||
auth_config: {
|
||||
"token_in_query": true, // token 放 URL 查询参数
|
||||
"query_key": "access_token", // 参数名
|
||||
"refresh_token": "xxx", // 刷新 token
|
||||
"extra_query_params": {
|
||||
"timestamp": "{timestamp}",
|
||||
"nonce": "{nonce}"
|
||||
}
|
||||
}
|
||||
```
|
||||
安全校验:`applyAuthHeader` 中 OAUTH2/TOKEN 模式会设 `Authorization: Bearer {token}`,但 `token_in_query=true` 时跳过 Header,转加 URL 参数。
|
||||
|
||||
### 2. API_KEY + 签名(快手电商)
|
||||
```
|
||||
auth_type: API_KEY
|
||||
token/access_token: 调用凭证
|
||||
auth_config: {
|
||||
"sign_algorithm": "md5_upper", // 签名算法: md5 / md5_upper
|
||||
"app_key": "xxx",
|
||||
"app_secret": "xxx",
|
||||
"token_in_query": true,
|
||||
"query_key": "access_token",
|
||||
"extra_query_params": {
|
||||
"timestamp": "{timestamp_ms}",
|
||||
"signMethod": "MD5"
|
||||
}
|
||||
}
|
||||
```
|
||||
签名过程:所有查询参数按 key 排序 → `k1=v1&k2=v2&...&key=app_secret` → MD5 摘要 → 追加 `sign` 参数。
|
||||
`{timestamp_ms}` 替换为毫秒时间戳,`{timestamp}` 替换为秒时间戳,`{nonce}` 替换为随机数。
|
||||
|
||||
### 3. TOKEN(简单 Bearer Token)
|
||||
```
|
||||
auth_type: TOKEN
|
||||
token: "xxx"
|
||||
```
|
||||
`applyAuthHeader` 设 `Authorization: Bearer {token}`。
|
||||
|
||||
### 4. SIGN(仅签名,无额外 token)
|
||||
```
|
||||
auth_type: SIGN
|
||||
auth_config: { "app_key": "...", "app_secret": "..." }
|
||||
```
|
||||
`applyAuthHeader` 不设特殊 Header,仅通过 `applySignature` 追加签名参数。`PlatformManager` 从 `auth_config` 读取 app_key/app_secret。
|
||||
|
||||
## request_config 字段完整参考
|
||||
|
||||
| 字段 | 类型 | 含义 | 适用平台 |
|
||||
|------|------|------|---------|
|
||||
| `parameters_location` | string | `"query"`=URL查询参数,不设则POST走JSON body | 通用 |
|
||||
| `page_param` | string | 分页参数名,默认`"page"` | 通用 |
|
||||
| `page_size_param` | string | 每页条数参数名,默认`"page_size"` | 通用 |
|
||||
| `page_size` | int | 每页条数,**覆盖 config.yml 全局默认值**,需按各平台最大限制设置(如快手最大50,腾讯100) | 通用 |
|
||||
| `cursor_pagination` | bool | 是否游标分页(true=游标,false/无=普通分页) | 通用 |
|
||||
| `time_field` | string | 增量时间字段名 | 通用 |
|
||||
| `time_field_mode` | string | `"range"`=beginTime/endTime模式(快手),`"filtering"`=filtering数组模式(腾讯,默认) | 通用 |
|
||||
| `full_sync_start_time` | int | 全量同步时的时间起点,毫秒时间戳。`lastSyncTime=0` 时优先读取此值,不存在则回退90天前(range)或不传时间过滤(filtering) | 通用 |
|
||||
| `prefetch` | object | 预取配置(见下方) | 需遍历实体的接口 |
|
||||
| `recursive` | object | 递归遍历配置(见下方) | 树形结构接口(如钉钉部门) |
|
||||
| `max_recursive_depth` | int | 递归最大深度,默认20 | 递归遍历使用 |
|
||||
| `body_wrapper_field` | string | 业务参数包装字段名,如`"param"` | 快手 |
|
||||
| `exclude_from_wrapper` | string[] | body_wrapper 时不包装的字段 | 快手 |
|
||||
| `top_level_params` | string[] | 保留在顶层的字段(备用) | 通用 |
|
||||
| `fields` | string[] | API 的 fields 参数 | 腾讯 |
|
||||
| `row_inject` | string[] | 将请求参数值注入到每行数据中(如 salaryGroupName),解决响应不含某请求参数但需存储的场景 | 通用(prefetch 和非 prefetch 均支持) |
|
||||
|
||||
### prefetch 预取配置
|
||||
```json
|
||||
"prefetch": {
|
||||
"url": "/advertiser/get", // 预取接口的相对路径
|
||||
"method": "GET", // HTTP 方法
|
||||
"response_path": "data.list", // 从响应中取列表的 JSON 路径
|
||||
"target_param": "account_id", // 预取值注入到数据接口的参数名
|
||||
"value_field": "account_id" // 从预取列表里取哪个字段的值
|
||||
}
|
||||
```
|
||||
流程:先分页拉取预取接口 → 提取所有 ID → 逐个 ID 并发拉取数据接口 → 将 ID 注入每行数据。
|
||||
|
||||
**预取阶段分页**:自动适配预取来源接口的分页方式:
|
||||
- **游标分页**(`cursor_pagination: true`):首次 `cursor=""`,后续从响应 `cursor_field` 取值,直到 `""` 或 `"nomore"` 停止
|
||||
- **普通分页**:读取 `page_info.total_page` 遍历全部页码
|
||||
|
||||
**预取阶段参数构建**:使用 `buildReqBody(prefetchIface)` 构建,自动处理 `body_wrapper_field` 包装(param JSON)、时间过滤(增量时传 beginTime/endTime)、分页参数名。响应解析使用 `prefetchIface.ResponseConfig`,正确识别各种 `list_path`(`data.orderList`、`data.items`、`data.cpsOrderList` 等)。
|
||||
|
||||
**预取阶段时间过滤**:增量同步时自动复用预取来源接口的 `time_field` + `time_field_mode`(如果来源支持增量,prefetch 会自动带上时间范围只拉增量数据)。
|
||||
|
||||
## response_config 字段完整参考
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
|------|------|------|
|
||||
| `success_field` | string | 成功标识字段名,默认`"code"` |
|
||||
| `success_value` | number | 成功标识值,默认`0` |
|
||||
| `message_field` | string | 错误信息字段名,默认`"message"` |
|
||||
| `list_path` | string | 数据列表在 JSON 中的路径,如`"data.list"`、`"data.orderList"` |
|
||||
| `cursor_field` | string | 游标字段路径,如`"data.cursor"` |
|
||||
| `cursor_end_marker` | string | 游标结束标记,如`"nomore"`(代码中硬判断此字符串) |
|
||||
| `single_record` | bool | 单记录模式:`list_path` 指向的路径是单个对象而非数组,会自动包装为单元素数组 |
|
||||
|
||||
**响应解析逻辑**:`parseRespExt` 函数
|
||||
1. 检查 `success_field` 的值是否等于 `success_value` | **注意**:`success_value` 仅支持数字类型(内部通过 `toFloat64` 转换),不支持字符串如 `"SUCCESS"` 或布尔值 `true`
|
||||
2. 按 `list_path` 遍历到数据列表数组 | 支持路径如 `data.orderList`,最后一段是数组 OK(代码有兼容处理)
|
||||
3. 每条数据调用 `flattenRow` 展平嵌套 field(`orderBaseInfo.oid` → `oid`)
|
||||
4. 提取 `cursor_field` 用于下一页,为空或等于 `cursor_end_marker` 时停止循环
|
||||
5. 从 `page_info.total_page` 获取总页数(普通分页用)
|
||||
|
||||
**游标分页首次请求**:代码自动传 `cursor=""`(首页不传游标),由服务端返回第一页数据 + 下一页游标值。
|
||||
|
||||
**单记录模式**:`"single_record": true` 时,`list_path` 指向单个对象(如 `"data"`),自动包装成单元素数组用于统一处理。
|
||||
|
||||
## table_definition 字段完整参考
|
||||
|
||||
| 字段 | 类型 | 含义 |
|
||||
|------|------|------|
|
||||
| `table_name` | string | 目标表名,如`"kuaishou_order_list"` |
|
||||
| `columns` | array | 列定义数组,每个元素 `{"name":"字段名","type":"PG类型","comment":"说明"}` |
|
||||
| `conflict_keys` | string[] | upsert 冲突键,如`["oid"]`或`["image_id","account_id"]` |
|
||||
|
||||
注意:
|
||||
- 列名必须和 API 响应中的字段名**完全一致**(camelCase/snake_case 取决于 API)
|
||||
- 嵌套字段会被 `flattenRow` 自动展平到顶层
|
||||
- `raw_data` 列自动包含,存储完整原始 JSON
|
||||
- 自动建表时会额外添加 id/tenant_id/creator/created_at/updater/updated_at/deleted_at 审计字段
|
||||
- **必须写全**:`columns` 中需列出 API 响应中所有平铺的标量字段(字符串/数字/布尔),数组和嵌套对象字段保留在 `raw_data` 中即可
|
||||
|
||||
## 同步策略原则
|
||||
- **API 支持时间过滤**(有 beginTime/endTime 或 filtering 参数)→ 配 `time_field` + `time_field_mode`,实现增量同步
|
||||
- **API 不支持时间过滤** → 不配 `time_field`,走全量同步
|
||||
- **详情/子接口**(通过 prefetch 遍历):prefetch 阶段自动复用预取来源接口的 `time_field_mode`(如果来源接口支持增量,prefetch 也会带上时间过滤只拉增量数据)
|
||||
|
||||
## 全量同步流程
|
||||
|
||||
```
|
||||
SyncByConfig(platformCode, interfaceCode, isFullSync=false)
|
||||
│
|
||||
├─ getLastSyncTime() → 无记录返回 0
|
||||
│ lastSyncTime=0 → 全量
|
||||
│
|
||||
├─ markSyncRunning() 标记 running
|
||||
│
|
||||
├─ buildReqBody(page=1, cursor="", lastSyncTime=0)
|
||||
│ ├─ 游标分页: cursor="" 首次空游标
|
||||
│ ├─ range 模式: beginTime=90天前, endTime=now
|
||||
│ └─ filtering 模式: 不添加时间过滤
|
||||
│
|
||||
├─ API → parseRespExt → flattenRow → savePage(upsert)
|
||||
│
|
||||
└─ 游标循环 → 直到 cursor="" 或 "nomore"
|
||||
updateSyncTime() 记录最大时间戳
|
||||
```
|
||||
|
||||
## 增量同步流程
|
||||
|
||||
```
|
||||
SyncByConfig(platformCode, interfaceCode, isFullSync=false)
|
||||
│
|
||||
├─ getLastSyncTime() → 返回上次 maxTime
|
||||
│ lastSyncTime>0 → 增量
|
||||
│
|
||||
├─ buildReqBody(cursor="", lastSyncTime=xxx)
|
||||
│ ├─ range 模式: beginTime=lastSyncTime, endTime=now
|
||||
│ └─ filtering 模式: filtering=[{field=time_field, operator=GREATER_EQUALS, values=[lastSyncTime]}]
|
||||
│
|
||||
└─ 后续同全量(只拉取时间范围内的数据)
|
||||
```
|
||||
|
||||
## 并发保护
|
||||
|
||||
### 内存锁(同一进程内)
|
||||
`SyncByConfig` 入口使用 `sync.Map`(`syncRunningMap`)记录正在执行的接口。若同一接口的同步请求再次到达(如调度器重叠),直接跳过并打印警告:
|
||||
```
|
||||
WARN 接口 [kuaishou/order_list] 正在同步中,跳过重复请求
|
||||
```
|
||||
无论接口类型(prefetch/非prefetch、腾讯/快手、现有/新增)均自动生效。
|
||||
|
||||
### 异常中断检测(DB 状态)
|
||||
如果 `sync_tracker.sync_status = "running"`,说明上次同步异常中断(进程崩溃),自动回退全量。
|
||||
|
||||
## 补偿机制
|
||||
`compensation.go` 定时扫描 `sync_task_log` 中 status="failed" 的任务,自动调用 `SyncByConfig` 重试(增量),支持指数退避(5, 15, 30, 60, 120 分钟)。
|
||||
|
||||
## 自动同步调度
|
||||
`sync_scheduler.go` 的 `runAutoSync` 遍历所有 ACTIVE 平台下的所有配置了 table_definition 的活跃接口,自动调用同步。
|
||||
|
||||
**调度器时序**:使用 `for { runAutoSync(); time.Sleep(interval) }` 模式,而非 `time.NewTicker`。每次同步**完全结束后**才开始计时 interval,避免前一次未跑完就启动下一次导致重叠。无论全量跑 30 分钟还是 70 分钟,下一次都在「本次完成时间 + interval」后执行。
|
||||
|
||||
## 三种遍历模式对比
|
||||
|
||||
| 模式 | 配置 | 适用场景 | 流程 |
|
||||
|------|------|---------|------|
|
||||
| **普通分页** | `page_param` + `page_size_param` | 列表接口分页 | 请求第1页 → 读 `page_info.total_page` → 遍历后续页 |
|
||||
| **游标分页** | `cursor_pagination: true` | 实时滚动列表(快手订单) | 首次 `cursor=""` → 响应返回 `cursor` → 直到 `""` 或 `"nomore"` |
|
||||
| **prefetch 预取** | `prefetch: {url, ...}` | 先取实体列表,再查详情 | 分页拉取实体来源列表 → 提取全部 ID → 并发查详情 |
|
||||
| **recursive 递归** | `recursive: {key_field, target_param}` | 树形结构(钉钉部门) | BFS 队列:根级调用 → 提取子ID → 逐级递归下级 → 防重复 |
|
||||
|
||||
## 新增平台 + 接口的操作步骤
|
||||
|
||||
### 操作步骤
|
||||
1. 在 `sql/` 下创建 `seed_data_{平台编码}.sql`
|
||||
2. 先执行 `sql/init_core_tables.sql`(仅首次需要)
|
||||
3. 执行新建的 seed SQL 文件(INSERT 平台 → INSERT 接口)
|
||||
4. 配置认证凭据(token / app_key / app_secret 等)
|
||||
5. 重启服务,管理后台验证:`http://localhost:3002/admin`
|
||||
|
||||
### 种子 SQL 标准模板
|
||||
|
||||
#### 模板 A:OAuth2 + 普通分页(腾讯广告风格)
|
||||
```sql
|
||||
-- =============================================
|
||||
-- 1. 创建 {平台编码} 平台
|
||||
-- =============================================
|
||||
INSERT INTO api_datasource_platform (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_code, platform_name, description, status,
|
||||
api_base_url, auth_type,
|
||||
token, client_id, client_secret,
|
||||
auth_config,
|
||||
rate_limit_per_minute, rate_limit_per_hour,
|
||||
concurrency_limit, request_timeout_ms, max_retries, retry_delay_ms
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
'{平台编码}', '{平台名称}', '{描述}', 'ACTIVE',
|
||||
'{API_BASE_URL}', 'OAUTH2',
|
||||
'{access_token}', '{client_id}', '{client_secret}',
|
||||
'{
|
||||
"token_in_query": true,
|
||||
"query_key": "access_token",
|
||||
"refresh_token": "{refresh_token}",
|
||||
"extra_query_params": {
|
||||
"timestamp": "{timestamp}",
|
||||
"nonce": "{nonce}"
|
||||
}
|
||||
}'::jsonb,
|
||||
60, 3600, 10, 30000, 3, 1000
|
||||
);
|
||||
|
||||
-- 2. 创建 {接口名} 接口(普通分页 + 增量 filtering)
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = '{平台编码}'),
|
||||
'{接口名称}', '{接口编码}',
|
||||
'{相对路径}', 'GET', 'active', 'inherit',
|
||||
'{
|
||||
"parameters_location": "query",
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page",
|
||||
"page_size_param": "page_size",
|
||||
"pagination_mode": "PAGINATION_MODE_NORMAL",
|
||||
"time_field": "last_modified_time",
|
||||
"fields": ["field1", "field2"]
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "code",
|
||||
"success_value": 0,
|
||||
"message_field": "message",
|
||||
"list_path": "data.list"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{表名}",
|
||||
"columns": [
|
||||
{"name": "id", "type": "BIGINT", "comment": "主键"},
|
||||
{"name": "name", "type": "VARCHAR(200)", "comment": "名称"},
|
||||
{"name": "created_time", "type": "BIGINT", "comment": "创建时间"}
|
||||
],
|
||||
"conflict_keys": ["id"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
#### 模板 B:OAuth2 + prefetch 预取(遍历账户拉取子数据,腾讯广告风格)
|
||||
```sql
|
||||
-- 预取接口(先拉取实体列表)
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = '{平台编码}'),
|
||||
'{实体列表名称}', '{实体编码}',
|
||||
'{实体列表相对路径}', 'GET', 'active', 'inherit',
|
||||
'{
|
||||
"parameters_location": "query",
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page",
|
||||
"page_size_param": "page_size",
|
||||
"fields": ["id", "name"]
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "code",
|
||||
"success_value": 0,
|
||||
"list_path": "data.list"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{实体表名}",
|
||||
"columns": [
|
||||
{"name": "id", "type": "BIGINT", "comment": "实体ID"},
|
||||
{"name": "name", "type": "VARCHAR(200)", "comment": "实体名称"}
|
||||
],
|
||||
"conflict_keys": ["id"]
|
||||
}'::jsonb
|
||||
);
|
||||
|
||||
-- 数据接口(遍历每个实体拉取数据)
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = '{平台编码}'),
|
||||
'{数据接口名称}', '{数据接口编码}',
|
||||
'{数据接口相对路径}', 'GET', 'active', 'inherit',
|
||||
'{
|
||||
"parameters_location": "query",
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page",
|
||||
"page_size_param": "page_size",
|
||||
"time_field": "last_modified_time",
|
||||
"prefetch": {
|
||||
"url": "{实体列表相对路径}",
|
||||
"method": "GET",
|
||||
"response_path": "data.list",
|
||||
"target_param": "{参数名}",
|
||||
"value_field": "{取值字段}"
|
||||
}
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "code",
|
||||
"success_value": 0,
|
||||
"list_path": "data.list"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{数据表名}",
|
||||
"columns": [
|
||||
{"name": "id", "type": "BIGINT", "comment": "ID"},
|
||||
{"name": "{参数名}", "type": "BIGINT", "comment": "实体ID(预取注入)"},
|
||||
{"name": "created_time", "type": "BIGINT", "comment": "创建时间"}
|
||||
],
|
||||
"conflict_keys": ["id", "{参数名}"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
#### 模板 C:API_KEY + MD5 签名 + 游标分页 + param 包装(快手风格)
|
||||
```sql
|
||||
INSERT INTO api_datasource_platform (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_code, platform_name, description, status,
|
||||
api_base_url, auth_type,
|
||||
auth_config,
|
||||
rate_limit_per_minute, rate_limit_per_hour,
|
||||
concurrency_limit, request_timeout_ms, max_retries, retry_delay_ms
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
'{平台编码}', '{平台名称}', '{描述}', 'ACTIVE',
|
||||
'{API_BASE_URL}', 'API_KEY',
|
||||
'{
|
||||
"sign_algorithm": "md5_upper",
|
||||
"app_key": "{YOUR_APP_KEY}",
|
||||
"app_secret": "{YOUR_APP_SECRET}",
|
||||
"token_in_query": true,
|
||||
"query_key": "access_token",
|
||||
"extra_query_params": {
|
||||
"timestamp": "{timestamp_ms}",
|
||||
"signMethod": "MD5"
|
||||
}
|
||||
}'::jsonb,
|
||||
100, 3600, 5, 30000, 3, 1000
|
||||
);
|
||||
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = '{平台编码}'),
|
||||
'{接口名称}', '{接口编码}',
|
||||
'{相对路径}', 'GET', 'active', 'inherit',
|
||||
'{
|
||||
"parameters_location": "query",
|
||||
"page_param": "cursor",
|
||||
"page_size_param": "pageSize",
|
||||
"cursor_pagination": true,
|
||||
"method": "{API方法名}",
|
||||
"version": 1,
|
||||
"signMethod": "MD5",
|
||||
"pageSize": 50,
|
||||
"time_field": "updateTime",
|
||||
"time_field_mode": "range",
|
||||
"body_wrapper_field": "param",
|
||||
"exclude_from_wrapper": ["method", "version", "signMethod"],
|
||||
"{其他业务参数}": {值}
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "result",
|
||||
"success_value": 1,
|
||||
"list_path": "data.{数组字段名}",
|
||||
"cursor_field": "data.cursor",
|
||||
"cursor_end_marker": "nomore"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{表名}",
|
||||
"columns": [
|
||||
{"name": "oid", "type": "BIGINT", "comment": "订单ID"},
|
||||
{"name": "createTime", "type": "BIGINT", "comment": "创建时间"},
|
||||
{"name": "updateTime", "type": "BIGINT", "comment": "更新时间"},
|
||||
{"name": "{字段名}", "type": "{类型}", "comment": "{说明}"}
|
||||
],
|
||||
"conflict_keys": ["oid"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
|
||||
#### 模板 D:POST + JSON Body(腾讯音频风格,无 prefetch,无增量)
|
||||
```sql
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = '{平台编码}'),
|
||||
'{接口名称}', '{接口编码}',
|
||||
'{相对路径}', 'POST', 'active', 'inherit',
|
||||
'{
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page",
|
||||
"page_size_param": "page_size",
|
||||
"fields": ["field1", "field2"]
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "code",
|
||||
"success_value": 0,
|
||||
"message_field": "message",
|
||||
"list_path": "data.list"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{表名}",
|
||||
"columns": [
|
||||
{"name": "id", "type": "BIGINT", "comment": "ID"},
|
||||
{"name": "name", "type": "VARCHAR(200)", "comment": "名称"}
|
||||
],
|
||||
"conflict_keys": ["id"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
注意:POST 方法且无 `parameters_location: "query"` 时,参数以 JSON body 形式发送。
|
||||
|
||||
#### 模板 E:OAuth2 + POST + 无分页(钉钉风格)
|
||||
```sql
|
||||
INSERT INTO api_datasource_platform (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_code, platform_name, description, status,
|
||||
api_base_url, auth_type,
|
||||
auth_config,
|
||||
rate_limit_per_minute, rate_limit_per_hour,
|
||||
concurrency_limit, request_timeout_ms, max_retries, retry_delay_ms
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
'dingtalk', '钉钉', '钉钉开放平台数据同步', 'ACTIVE',
|
||||
'https://oapi.dingtalk.com', 'OAUTH2',
|
||||
'{
|
||||
"token_in_query": true,
|
||||
"query_key": "access_token"
|
||||
}'::jsonb,
|
||||
60, 3600, 5, 30000, 3, 1000
|
||||
);
|
||||
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, creator, created_at, updater, updated_at,
|
||||
platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1, 'admin', NOW(), 'admin', NOW(),
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code = 'dingtalk'),
|
||||
'{接口名称}', '{接口编码}',
|
||||
'{相对路径}', 'POST', 'active', 'inherit',
|
||||
'{
|
||||
"parameters_location": "query",
|
||||
"page_param": "cursor",
|
||||
"page_size_param": "pageSize",
|
||||
"{业务参数}": {值}
|
||||
}'::jsonb,
|
||||
'{
|
||||
"success_field": "errcode",
|
||||
"success_value": 0,
|
||||
"message_field": "errmsg",
|
||||
"list_path": "result"
|
||||
}'::jsonb,
|
||||
'{
|
||||
"table_name": "{表名}",
|
||||
"columns": [
|
||||
{"name": "id", "type": "BIGINT", "comment": "ID"},
|
||||
{"name": "name", "type": "VARCHAR(300)", "comment": "名称"}
|
||||
],
|
||||
"conflict_keys": ["id"]
|
||||
}'::jsonb
|
||||
);
|
||||
```
|
||||
注意:钉钉使用 `access_token` 作为 URL 查询参数,通过 `token_in_query: true` + `query_key: "access_token"` 配置。POST 请求通过 `parameters_location: "query"` 将所有参数放在 URL 查询字符串中(DingTalk 同时支持 query 和 body 传参)。
|
||||
|
||||
如需递归遍历树形结构(如部门全量),在 `request_config` 中添加:
|
||||
```json
|
||||
"recursive": {
|
||||
"key_field": "dept_id", // 从响应行中提取哪个字段的值用于递归
|
||||
"target_param": "dept_id" // 递归参数注入到请求中的参数名
|
||||
},
|
||||
"max_recursive_depth": 20 // 可选,最大递归深度,默认20
|
||||
```
|
||||
递归流程:BFS 遍历,先查根级空参 → 提取每行的 `key_field` → 逐级注入 `target_param` 查询下级 → 防重复循环保护 → 所有结果合并入库。
|
||||
|
||||
## 新增接口步骤总结
|
||||
|
||||
1. **确定平台**:已有平台直接跳到第3步,否则先新增平台(参考对应认证类型的模板)
|
||||
2. **确定平台 auth_type**:OAUTH2 / TOKEN / API_KEY / SIGN → 复制对应模板的 auth_config
|
||||
3. **分析 API**:
|
||||
- 请求方式(GET/POST)
|
||||
- 分页方式(普通分页 → page + total_page / 游标分页 → cursor + cursor_end)
|
||||
- 成功标识(code=0 / result=1 / errcode=0 等)
|
||||
- 数据列表路径(data.list / data.orderList / data.records 等)
|
||||
- 数据字段名(注意大小写,嵌套结构会被展开)
|
||||
- 是否需要遍历实体(prefetch)
|
||||
- 增量时间字段(字段名 + 值类型)
|
||||
4. **选择模板**并填入对应值
|
||||
|
||||
> ⚠️ **全量同步起始时间**:如果接口数据量很大,首次全量不想从 90 天前拉取,可以在 `request_config` 中加 `"full_sync_start_time": <毫秒时间戳>` 指定起点。例如只同步最近 30 天:
|
||||
> ```json
|
||||
> "full_sync_start_time": 1745942400000
|
||||
> ```
|
||||
> - **range 模式**(快手 `time_field_mode: "range"`):全量时取此时间作为 beginTime
|
||||
> - **filtering 模式**(腾讯 `time_field_mode: "filtering"`):全量时生成 `GREATER_EQUALS` 过滤条件
|
||||
> - **不配此字段**:行为不变(range 回退 90 天,filtering 不传时间过滤)
|
||||
|
||||
5. **执行 seed SQL**
|
||||
6. **在管理后台验证**:`http://{host}:3002/admin`
|
||||
|
||||
## 代码审计发现的项目规范
|
||||
|
||||
### 包命名规范(2026-06-03 审计)
|
||||
- `model/dto/dict/` 下的 `.go` 文件必须用 `package dict`(曾错误写为 `package api_feature`,已修复)
|
||||
- `model/entity/dict/` 下的 `.go` 文件用 `package dict`
|
||||
- `consts/api-feature/` 目录下的文件用 `package api_feature`
|
||||
- `consts/public/` 用 `package public`
|
||||
- **不要**在 `consts/dict/` 中重复定义 `PlatformStatus`/`ApiMethod` 等类型,应使用 `consts/api-feature` 中的定义
|
||||
|
||||
### 数据库状态值规范
|
||||
- `api_datasource_platform.status`:使用大写 `"ACTIVE"` / `"INACTIVE"`
|
||||
- `api_interface.status`:使用小写 `"active"` / `"inactive"`
|
||||
- 代码中查平台状态时不要使用 `api_feature.PlatformStatusActive`(其值为小写),应直接用 `"ACTIVE"` 字符串
|
||||
|
||||
### Known Bad Practices(已知待改进项)
|
||||
- 敏感信息(access_token / app_secret)仍分散在 SQL seed文件和 config.yml 中,建议迁移到环境变量或 Vault
|
||||
- `consts/dict/consts.go` 和 `scheduler/run_sync_task_log_task.go` 可能随业务扩展需要更新
|
||||
|
||||
## 通用报表引擎 (common/report)
|
||||
|
||||
2026-06-10: 通用报表公共包,配置驱动的报表子系统,支持前端自由选择维度/指标/筛选/时间查询。
|
||||
|
||||
### 架构分层 (9个Go文件)
|
||||
```
|
||||
common/report/
|
||||
├── api.go # ReportService 统一门面(单例)
|
||||
├── report.go # 5张系统表 DDL
|
||||
├── example_usage.go # 完整使用示例文档
|
||||
├── model/model.go # 实体/请求/响应/常量
|
||||
├── config/loader.go # 配置加载器(读缓存+CRUD)
|
||||
├── builder/sql_builder.go # 动态 SQL 构建
|
||||
├── executor/executor.go # 查询执行器
|
||||
├── extract/extract.go # 天级数据抽取(DIRECT/AGGREGATE)
|
||||
└── ddlsync/creator.go # 统计宽表自动创建
|
||||
```
|
||||
|
||||
### 核心能力
|
||||
1. **自动建表**:根据 FieldConfig 自动 CREATE TABLE stat_xxx
|
||||
2. **数据抽取**:DIRECT(逐行)/ AGGREGATE(聚合 SUM/COUNT)模式,幂等保证
|
||||
3. **动态查询**:前端选维度+指标+筛选+时间 → 实时构建 SQL → 分页返回
|
||||
4. **配置 CRUD**(2026-06-10 新增):BusinessConfig/ReportConfig/FieldConfig/ExtractConfig 全部可前端维护
|
||||
|
||||
### 对外接口 (ReportService)
|
||||
- 查询:`QueryReportByUserSelect` / `GetReportFields` / `GetAllBusinesses` / `GetAllReports`
|
||||
- 抽取:`ExtractDailyData` / `AutoCreateStatTable`
|
||||
- CRUD:`SaveBusiness/DeleteBusiness/GetBusiness`、`SaveReport/DeleteReport/GetReport`、`SaveField/DeleteField/GetField`、`SaveExtractConfig/DeleteExtractConfig/GetExtractConfig/GetExtractConfigs`
|
||||
|
||||
### 设计原则
|
||||
- 零硬编码:任意平台/接口通过配置接入,不改代码
|
||||
- 配置驱动:5张 PG 表存储全部配置,CRUD API 支持前端维护
|
||||
- 单例模式:`report.GetService()` 可在任意业务服务中直接调用
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
.git
|
||||
@@ -1 +1,5 @@
|
||||
/.idea/*
|
||||
/data-engine
|
||||
/resource/*
|
||||
.gstack/
|
||||
/docs/*
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# data-engine
|
||||
|
||||
> ⚠️ **启动时必读:** 每次会话开始时,先读取 `docs/INDEX.md` 了解项目全景和最近变更。
|
||||
|
||||
## Skill routing
|
||||
|
||||
When the user's request matches an available skill, invoke it via the Skill tool. When in doubt, invoke the skill.
|
||||
|
||||
Key routing rules:
|
||||
- Product ideas/brainstorming → invoke /office-hours
|
||||
- Strategy/scope → invoke /plan-ceo-review
|
||||
- Architecture → invoke /plan-eng-review
|
||||
- Design system/plan review → invoke /design-consultation or /plan-design-review
|
||||
- Full review pipeline → invoke /autoplan
|
||||
- Bugs/errors → invoke /investigate
|
||||
- QA/testing site behavior → invoke /qa or /qa-only
|
||||
- Code review/diff check → invoke /review
|
||||
- Visual polish → invoke /design-review
|
||||
- Ship/deploy/PR → invoke /ship or /land-and-deploy
|
||||
- Save progress → invoke /context-save
|
||||
- Resume context → invoke /context-restore
|
||||
- Author a backlog-ready spec/issue → invoke /spec
|
||||
|
||||
## Agent skills
|
||||
|
||||
### Issue tracker
|
||||
|
||||
Issues tracked as local markdown files under `.scratch/`. See `docs/agents/issue-tracker.md`.
|
||||
|
||||
### Triage labels
|
||||
|
||||
Default five-role vocabulary (`needs-triage`, `needs-info`, `ready-for-agent`, `ready-for-human`, `wontfix`). See `docs/agents/triage-labels.md`.
|
||||
|
||||
### Domain docs
|
||||
|
||||
Single-context repo. See `docs/agents/domain.md`.
|
||||
+21
-53
@@ -1,57 +1,25 @@
|
||||
FROM golang:1.25.3
|
||||
RUN go env -w GO111MODULE=on
|
||||
RUN go env -w GOPROXY=https://goproxy.cn,direct
|
||||
ENV WORKDIR /usr/local/bin/app
|
||||
WORKDIR $WORKDIR
|
||||
# 阶段1: 构建
|
||||
FROM golang:alpine AS builder
|
||||
|
||||
ENV TIME_ZONE=Asia/Seoul
|
||||
RUN useradd -ms /bin/bash golang
|
||||
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories && \
|
||||
apk add --no-cache git ca-certificates tzdata
|
||||
|
||||
ENV TZ=Asia/Shanghai
|
||||
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
|
||||
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://goproxy.cn,direct
|
||||
ENV CGO_ENABLED=0
|
||||
ENV GOTOOLCHAIN=auto
|
||||
WORKDIR /build
|
||||
|
||||
COPY . .
|
||||
|
||||
RUN go mod download && go mod tidy
|
||||
|
||||
RUN go build -ldflags="-s -w" -o main ./main.go
|
||||
|
||||
|
||||
RUN ln -sf /usr/share/zoneinfo/$TIME_ZONE /etc/localtime \
|
||||
&& chmod 0755 /usr/bin/wall \
|
||||
&& chmod 0755 /usr/bin/passwd \
|
||||
&& chmod 0755 /usr/bin/newgrp \
|
||||
&& chmod 0755 /usr/bin/chfn \
|
||||
&& chmod 0755 /usr/bin/chage \
|
||||
&& chmod 0755 /usr/bin/gpasswd \
|
||||
&& chmod 0755 /usr/bin/chsh \
|
||||
&& chmod 0755 /usr/bin/expiry \
|
||||
&& chmod 0755 /bin/umount \
|
||||
&& chmod 0755 /bin/mount \
|
||||
&& chmod 0755 /bin/su \
|
||||
&& chmod 0755 /sbin/unix_chkpwd \
|
||||
&& chmod 110 /usr/bin/chfn \
|
||||
&& chmod 110 /usr/bin/passwd \
|
||||
&& chmod 110 /usr/bin/newgrp \
|
||||
&& chmod 110 /usr/bin/chsh \
|
||||
&& chmod 110 /usr/bin/wall \
|
||||
&& chmod 110 /usr/bin/gpasswd \
|
||||
&& chmod 110 /usr/bin/expiry \
|
||||
&& chmod 110 /usr/bin/chage \
|
||||
&& chmod 110 /bin/mount \
|
||||
&& chmod 110 /bin/umount \
|
||||
&& chmod 110 /bin/su \
|
||||
&& chmod 110 /sbin/unix_chkpwd \
|
||||
&& chmod 110 /usr/lib/openssh/ssh-keysign \
|
||||
&& chmod 110 /usr/bin/ssh-agent
|
||||
EXPOSE 3013
|
||||
|
||||
COPY go.mod go.sum ./
|
||||
# RUN chown -R golang:golang $WORKDIR
|
||||
RUN go mod download && go mod verify
|
||||
COPY ../../cid $WORKDIR
|
||||
RUN chown -R golang:golang $WORKDIR
|
||||
# Remove SetUID, SetGID
|
||||
RUN chmod 0755 /usr/local/bin/app/api \
|
||||
&& chmod 0755 /usr/local/bin/app/common \
|
||||
&& chmod 0755 /usr/local/bin/app/middleware \
|
||||
&& chmod 0755 /usr/local/bin/app/model \
|
||||
&& rm -rf .git \
|
||||
&& rm -rf .gitignore \
|
||||
&& rm -rf .gitlab-ci.yml \
|
||||
&& rm -rf Dockerfile.bak \
|
||||
&& rm -rf config-local.yml
|
||||
USER golang
|
||||
RUN go build -v -o /usr/local/bin/app ./...
|
||||
EXPOSE 3002
|
||||
CMD ./cid
|
||||
CMD ["./main"]
|
||||
|
||||
@@ -0,0 +1,900 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"dataengine/common/report/config"
|
||||
"dataengine/common/report/ddlsync"
|
||||
"dataengine/common/report/executor"
|
||||
"dataengine/common/report/extract"
|
||||
"dataengine/common/report/model"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
)
|
||||
|
||||
// getUserNameFromCtx 从上下文中获取当前用户名
|
||||
func getUserNameFromCtx(ctx context.Context) string {
|
||||
if user, ok := ctx.Value("user").(*beans.User); ok && user != nil && user.UserName != "" {
|
||||
return user.UserName
|
||||
}
|
||||
return "system"
|
||||
}
|
||||
|
||||
// ReportService 报表公共服务
|
||||
// 对外暴露的统一接口
|
||||
type ReportService struct {
|
||||
configLoader *config.ConfigLoader
|
||||
tableCreator *ddlsync.StatTableCreator
|
||||
queryExecutor *executor.QueryExecutor
|
||||
dailyExtractor *extract.DailyExtractor
|
||||
}
|
||||
|
||||
var defaultService *ReportService
|
||||
|
||||
// GetService 获取报表服务单例
|
||||
func GetService() *ReportService {
|
||||
if defaultService == nil {
|
||||
defaultService = &ReportService{
|
||||
configLoader: config.GetLoader(),
|
||||
tableCreator: ddlsync.NewStatTableCreator(),
|
||||
queryExecutor: executor.NewQueryExecutor(),
|
||||
dailyExtractor: extract.NewDailyExtractor(),
|
||||
}
|
||||
}
|
||||
return defaultService
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 核心接口 1: 自动创建统计宽表
|
||||
// 首次抽取前调用
|
||||
// ============================================================
|
||||
|
||||
// AutoCreateStatTable 根据配置自动创建统计宽表
|
||||
// businessCode: 业务编码
|
||||
// reportCode: 报表编码
|
||||
func (s *ReportService) AutoCreateStatTable(ctx context.Context, businessCode, reportCode string) (*model.AutoCreateStatTableResp, error) {
|
||||
// 初始化系统表
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
resp, err := s.tableCreator.AutoCreateStatTable(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("AutoCreateStatTable 失败: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 核心接口 2: 按天抽取数据
|
||||
// 业务层定时任务调用
|
||||
// ============================================================
|
||||
|
||||
// ExtractDailyData 按天抽取数据
|
||||
// businessCode: 业务编码
|
||||
// reportCode: 报表编码
|
||||
// statDate: 统计日期 yyyy-MM-dd
|
||||
// executor: 执行人
|
||||
func (s *ReportService) ExtractDailyData(ctx context.Context, businessCode, reportCode, statDate, executor string) (*model.ExtractDailyDataResp, error) {
|
||||
// 1. 先确保统计宽表存在
|
||||
report, err := s.configLoader.GetReport(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取报表配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 检查表是否存在
|
||||
result, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT EXISTS (SELECT 1 FROM pg_tables WHERE tablename = $1) AS exists",
|
||||
strings.ToLower(report.StatTableName))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("检查统计宽表失败: %w", err)
|
||||
}
|
||||
tableExists := false
|
||||
if len(result) > 0 {
|
||||
tableExists = result[0]["exists"].Bool()
|
||||
}
|
||||
if !tableExists {
|
||||
// 表不存在,先创建
|
||||
if _, createErr := s.AutoCreateStatTable(ctx, businessCode, reportCode); createErr != nil {
|
||||
return nil, fmt.Errorf("创建统计宽表失败: %w", createErr)
|
||||
}
|
||||
}
|
||||
|
||||
resp, err := s.dailyExtractor.ExtractDailyData(ctx, businessCode, reportCode, statDate, executor)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("ExtractDailyData 失败: %w", err)
|
||||
}
|
||||
|
||||
// 清除缓存
|
||||
s.configLoader.InvalidateCache(businessCode, reportCode)
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 核心接口 3: 批量回填数据
|
||||
// 首次接入时对日期范围逐天抽取
|
||||
// ============================================================
|
||||
|
||||
// Backfill 批量回填数据
|
||||
// 遍历 startDate → endDate 逐天调用 ExtractDailyData
|
||||
// 返回汇总统计
|
||||
func (s *ReportService) Backfill(ctx context.Context, req *model.BackfillReq) (*model.BackfillResp, error) {
|
||||
start := time.Now()
|
||||
|
||||
startDate, err := time.Parse("2006-01-02", req.StartDate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("开始日期格式错误: %w", err)
|
||||
}
|
||||
endDate, err := time.Parse("2006-01-02", req.EndDate)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("结束日期格式错误: %w", err)
|
||||
}
|
||||
if endDate.Before(startDate) {
|
||||
return nil, fmt.Errorf("结束日期不能早于开始日期")
|
||||
}
|
||||
|
||||
totalDays := 0
|
||||
successDays := 0
|
||||
failDays := 0
|
||||
var lastErr error
|
||||
|
||||
for d := startDate; !d.After(endDate); d = d.AddDate(0, 0, 1) {
|
||||
totalDays++
|
||||
dateStr := d.Format("2006-01-02")
|
||||
resp, err := s.ExtractDailyData(ctx, req.BusinessCode, req.ReportCode, dateStr, req.Executor)
|
||||
if err != nil {
|
||||
failDays++
|
||||
lastErr = fmt.Errorf("回填 %s 失败: %w", dateStr, err)
|
||||
continue
|
||||
}
|
||||
if !resp.Success {
|
||||
failDays++
|
||||
lastErr = fmt.Errorf("回填 %s 异常: %s", dateStr, resp.ErrorMsg)
|
||||
continue
|
||||
}
|
||||
successDays++
|
||||
}
|
||||
|
||||
execTime := time.Since(start).Milliseconds()
|
||||
|
||||
resp := &model.BackfillResp{
|
||||
Success: failDays == 0,
|
||||
TotalDays: totalDays,
|
||||
SuccessDays: successDays,
|
||||
FailDays: failDays,
|
||||
ExecTimeMs: execTime,
|
||||
}
|
||||
if lastErr != nil {
|
||||
resp.ErrorMsg = lastErr.Error()
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 核心接口 4: 用户选择查询(最核心)
|
||||
// 前端用户选择条件 → 实时构建SQL → 返回报表数据
|
||||
// ============================================================
|
||||
|
||||
// QueryReportByUserSelect 根据用户选择实时查询报表数据
|
||||
// 不是自动生成报表,是用户在前端选择维度/指标/筛选/时间后实时查询展示
|
||||
func (s *ReportService) QueryReportByUserSelect(ctx context.Context, req *model.UserSelectQueryReq) (*model.UserSelectQueryResp, error) {
|
||||
// 参数校验
|
||||
if req.BusinessCode == "" {
|
||||
return nil, fmt.Errorf("businessCode 不能为空")
|
||||
}
|
||||
if req.ReportCode == "" {
|
||||
return nil, fmt.Errorf("reportCode 不能为空")
|
||||
}
|
||||
|
||||
resp, err := s.queryExecutor.QueryReportByUserSelect(ctx, req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("QueryReportByUserSelect 失败: %w", err)
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 辅助接口
|
||||
// ============================================================
|
||||
|
||||
// GetReportFields 获取报表可用字段(按维度/指标/筛选分类)
|
||||
func (s *ReportService) GetReportFields(ctx context.Context, businessCode, reportCode string) (*model.GetReportFieldsResp, error) {
|
||||
resp, err := s.configLoader.GetReportFields(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("GetReportFields 失败: %w", err)
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// GetAllBusinesses 获取所有启用业务列表
|
||||
|
||||
// ListBusinesses 分页获取业务列表
|
||||
func (s *ReportService) ListBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {
|
||||
return s.configLoader.ListBusinesses(ctx, pageNum, pageSize)
|
||||
}
|
||||
func (s *ReportService) GetAllBusinesses(ctx context.Context) ([]model.BusinessConfig, error) {
|
||||
return s.configLoader.GetAllBusinesses(ctx)
|
||||
}
|
||||
|
||||
// GetAllReports 获取业务下所有报表列表
|
||||
|
||||
// ListReports 分页获取报表列表
|
||||
func (s *ReportService) ListReports(ctx context.Context, businessCode, reportName string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {
|
||||
return s.configLoader.ListReports(ctx, businessCode, reportName, pageNum, pageSize)
|
||||
}
|
||||
func (s *ReportService) GetAllReports(ctx context.Context, businessCode string) ([]model.ReportConfig, error) {
|
||||
return s.configLoader.GetAllReports(ctx, businessCode)
|
||||
}
|
||||
|
||||
// InvalidateCache 失效指定业务报表缓存
|
||||
func (s *ReportService) InvalidateCache(businessCode, reportCode string) {
|
||||
s.configLoader.InvalidateCache(businessCode, reportCode)
|
||||
}
|
||||
|
||||
// InitSystemTables 初始化系统表
|
||||
func (s *ReportService) InitSystemTables(ctx context.Context) error {
|
||||
return initTables(ctx)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配置 CRUD: 业务
|
||||
// ============================================================
|
||||
|
||||
// SaveBusiness 保存业务配置(新增/修改合一)
|
||||
func (s *ReportService) SaveBusiness(ctx context.Context, req *model.SaveBusinessReq) (*model.SaveResult, error) {
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
// 新增时校验 businessCode 唯一性
|
||||
if req.ID == nil || *req.ID == 0 {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx, "SELECT id FROM report_business_config WHERE business_code = $1 LIMIT 1", req.BusinessCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("校验业务编码失败: %w", err)
|
||||
}
|
||||
if !r.IsEmpty() {
|
||||
return nil, fmt.Errorf("业务编码 %s 已存在", req.BusinessCode)
|
||||
}
|
||||
}
|
||||
|
||||
userName := getUserNameFromCtx(ctx)
|
||||
biz := &model.BusinessConfig{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: userName, Updater: userName, TenantId: 1},
|
||||
BusinessCode: req.BusinessCode,
|
||||
BusinessName: req.BusinessName,
|
||||
Description: req.Description,
|
||||
Status: req.Status,
|
||||
Config: req.Config,
|
||||
}
|
||||
|
||||
if req.Status == "" {
|
||||
biz.Status = model.StatusActive
|
||||
}
|
||||
if biz.Config == nil {
|
||||
biz.Config = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if req.ID != nil && *req.ID > 0 {
|
||||
// 更新
|
||||
biz.Id = *req.ID
|
||||
if err := s.configLoader.UpdateBusiness(ctx, biz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: *req.ID, Message: "更新成功"}, nil
|
||||
}
|
||||
|
||||
// 新增
|
||||
id, err := s.configLoader.CreateBusiness(ctx, biz)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: id, Message: "创建成功"}, nil
|
||||
}
|
||||
|
||||
// DeleteBusiness 删除业务配置
|
||||
func (s *ReportService) DeleteBusiness(ctx context.Context, id int64) (*model.DeleteResult, error) {
|
||||
biz, err := s.configLoader.GetBusinessByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 级联检查:该业务下是否存在报表
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx, "SELECT COUNT(1) AS cnt FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL LIMIT 1", biz.BusinessCode)
|
||||
if err == nil && !r.IsEmpty() {
|
||||
cnt := r[0]["cnt"].Int()
|
||||
if cnt > 0 {
|
||||
return &model.DeleteResult{Success: false, Message: fmt.Sprintf("业务 %s 下存在 %d 个报表配置,请先删除关联报表后再删除业务", biz.BusinessCode, cnt)}, nil
|
||||
}
|
||||
}
|
||||
if err := s.configLoader.DeleteBusiness(ctx, id, biz.BusinessCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.DeleteResult{Success: true, Message: "删除成功"}, nil
|
||||
}
|
||||
|
||||
// GetBusiness 获取单个业务配置
|
||||
func (s *ReportService) GetBusiness(ctx context.Context, id int64) (*model.BusinessConfig, error) {
|
||||
return s.configLoader.GetBusinessByID(ctx, id)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配置 CRUD: 报表
|
||||
// ============================================================
|
||||
|
||||
// SaveReport 保存报表配置(新增/修改合一)
|
||||
func (s *ReportService) SaveReport(ctx context.Context, req *model.SaveReportReq) (*model.SaveResult, error) {
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
// 新增时校验 reportCode 唯一性(同业务下)
|
||||
if req.ID == nil || *req.ID == 0 {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx, "SELECT id FROM report_report_config WHERE business_code = $1 AND report_code = $2 LIMIT 1",
|
||||
req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("校验报表编码失败: %w", err)
|
||||
}
|
||||
if !r.IsEmpty() {
|
||||
return nil, fmt.Errorf("业务 %s 下报表编码 %s 已存在", req.BusinessCode, req.ReportCode)
|
||||
}
|
||||
}
|
||||
|
||||
userName := getUserNameFromCtx(ctx)
|
||||
rpt := &model.ReportConfig{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: userName, Updater: userName, TenantId: 1},
|
||||
BusinessCode: req.BusinessCode,
|
||||
ReportCode: req.ReportCode,
|
||||
ReportName: req.ReportName,
|
||||
Description: req.Description,
|
||||
Status: req.Status,
|
||||
StatTableName: req.StatTableName,
|
||||
StatTableComment: req.StatTableComment,
|
||||
DateField: req.DateField,
|
||||
PrimaryKeys: req.PrimaryKeys,
|
||||
ConflictKeys: req.ConflictKeys,
|
||||
Config: req.Config,
|
||||
}
|
||||
|
||||
if req.Status == "" {
|
||||
rpt.Status = model.StatusActive
|
||||
}
|
||||
if rpt.DateField == "" {
|
||||
rpt.DateField = "stat_date"
|
||||
}
|
||||
if rpt.PrimaryKeys == nil {
|
||||
rpt.PrimaryKeys = []string{"id"}
|
||||
}
|
||||
if rpt.ConflictKeys == nil {
|
||||
rpt.ConflictKeys = []string{rpt.DateField}
|
||||
}
|
||||
if rpt.Config == nil {
|
||||
rpt.Config = make(map[string]interface{})
|
||||
}
|
||||
|
||||
if req.ID != nil && *req.ID > 0 {
|
||||
rpt.Id = *req.ID
|
||||
if err := s.configLoader.UpdateReport(ctx, rpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: *req.ID, Message: "更新成功"}, nil
|
||||
}
|
||||
|
||||
id, err := s.configLoader.CreateReport(ctx, rpt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: id, Message: "创建成功"}, nil
|
||||
}
|
||||
|
||||
// DeleteReport 删除报表配置
|
||||
func (s *ReportService) DeleteReport(ctx context.Context, id int64) (*model.DeleteResult, error) {
|
||||
rpt, err := s.configLoader.GetReportByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 级联检查:该报表下是否存在未删除的字段
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx, "SELECT COUNT(1) AS cnt FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL LIMIT 1",
|
||||
rpt.BusinessCode, rpt.ReportCode)
|
||||
if err == nil && !r.IsEmpty() && r[0]["cnt"].Int() > 0 {
|
||||
return &model.DeleteResult{Success: false, Message: fmt.Sprintf("报表 %s 下存在字段配置,请先删除关联字段后再删除报表", rpt.ReportCode)}, nil
|
||||
}
|
||||
// 级联检查:该报表下是否存在抽取配置
|
||||
r, err = gfdb.DB(ctx).GetAll(ctx, "SELECT COUNT(1) AS cnt FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL LIMIT 1",
|
||||
rpt.BusinessCode, rpt.ReportCode)
|
||||
if err == nil && !r.IsEmpty() && r[0]["cnt"].Int() > 0 {
|
||||
return &model.DeleteResult{Success: false, Message: fmt.Sprintf("报表 %s 下存在抽取配置,请先删除关联抽取配置后再删除报表", rpt.ReportCode)}, nil
|
||||
}
|
||||
if err := s.configLoader.DeleteReport(ctx, id, rpt.BusinessCode, rpt.ReportCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.DeleteResult{Success: true, Message: "删除成功"}, nil
|
||||
}
|
||||
|
||||
// GetReport 获取单个报表配置
|
||||
func (s *ReportService) GetReport(ctx context.Context, id int64) (*model.ReportConfig, error) {
|
||||
rpt, err := s.configLoader.GetReportByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// 同时加载全部字段(含已失效),编辑时前端可直接回显
|
||||
allFields, err := s.configLoader.GetAllFields(ctx, rpt.BusinessCode, rpt.ReportCode)
|
||||
if err == nil {
|
||||
rpt.Fields = allFields
|
||||
}
|
||||
return rpt, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 合并操作:报表+字段一起保存
|
||||
// ============================================================
|
||||
|
||||
// SaveReportWithFields 保存报表及字段配置(报表+字段全量替换)
|
||||
func (s *ReportService) SaveReportWithFields(ctx context.Context, req *model.SaveReportWithFieldsReq) (*model.SaveResult, error) {
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
// 自动填充每个字段的 businessCode/reportCode(从报表级继承)
|
||||
for i := range req.Fields {
|
||||
if req.Fields[i].BusinessCode == "" {
|
||||
req.Fields[i].BusinessCode = req.BusinessCode
|
||||
}
|
||||
if req.Fields[i].ReportCode == "" {
|
||||
req.Fields[i].ReportCode = req.ReportCode
|
||||
}
|
||||
}
|
||||
|
||||
var reportID int64
|
||||
userName := getUserNameFromCtx(ctx)
|
||||
|
||||
// 事务:要么全成功,要么不动
|
||||
err := gfdb.DB(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
|
||||
// 1. 保存报表(新增或更新)
|
||||
now := time.Now()
|
||||
if req.ID != nil && *req.ID > 0 {
|
||||
// 更新报表
|
||||
data := map[string]interface{}{
|
||||
"business_code": req.BusinessCode,
|
||||
"report_code": req.ReportCode,
|
||||
"report_name": req.ReportName,
|
||||
"description": req.Description,
|
||||
"status": req.Status,
|
||||
"stat_table_name": req.StatTableName,
|
||||
"stat_table_comment": req.StatTableComment,
|
||||
"date_field": req.DateField,
|
||||
"primary_keys": req.PrimaryKeys,
|
||||
"conflict_keys": req.ConflictKeys,
|
||||
"config": req.Config,
|
||||
"updated_at": now,
|
||||
"updater": userName,
|
||||
}
|
||||
if _, err := tx.Model("report_report_config").Where("id", *req.ID).Data(data).Update(); err != nil {
|
||||
return fmt.Errorf("更新报表失败: %w", err)
|
||||
}
|
||||
reportID = *req.ID
|
||||
} else {
|
||||
// 新增时校验 reportCode 唯一性
|
||||
cnt, err := tx.Model("report_report_config").Where("business_code", req.BusinessCode).Where("report_code", req.ReportCode).Count()
|
||||
if err != nil {
|
||||
return fmt.Errorf("校验报表编码失败: %w", err)
|
||||
}
|
||||
if cnt > 0 {
|
||||
return fmt.Errorf("业务 %s 下报表编码 %s 已存在", req.BusinessCode, req.ReportCode)
|
||||
}
|
||||
data := map[string]interface{}{
|
||||
"business_code": req.BusinessCode,
|
||||
"report_code": req.ReportCode,
|
||||
"report_name": req.ReportName,
|
||||
"description": req.Description,
|
||||
"status": ifVal(req.Status, model.StatusActive),
|
||||
"stat_table_name": req.StatTableName,
|
||||
"stat_table_comment": req.StatTableComment,
|
||||
"date_field": ifVal(req.DateField, "stat_date"),
|
||||
"primary_keys": req.PrimaryKeys,
|
||||
"conflict_keys": req.ConflictKeys,
|
||||
"config": req.Config,
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"creator": userName,
|
||||
"updater": userName,
|
||||
"tenant_id": 1,
|
||||
}
|
||||
if data["primary_keys"] == nil || len(data["primary_keys"].([]string)) == 0 {
|
||||
data["primary_keys"] = []string{"id"}
|
||||
}
|
||||
if data["conflict_keys"] == nil || len(data["conflict_keys"].([]string)) == 0 {
|
||||
data["conflict_keys"] = []string{ifVal(req.DateField, "stat_date")}
|
||||
}
|
||||
result, err := tx.Model("report_report_config").Data(data).Insert()
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "duplicate key") {
|
||||
return fmt.Errorf("业务 %s 下报表编码 %s 已存在", req.BusinessCode, req.ReportCode)
|
||||
}
|
||||
return fmt.Errorf("创建报表失败: %w", err)
|
||||
}
|
||||
rid, err := result.LastInsertId()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取自增ID失败: %w", err)
|
||||
}
|
||||
reportID = rid
|
||||
}
|
||||
|
||||
// 2. 获取报表下现有字段(含已删除,编辑时重新添加的字段需走 UPDATE 而非 INSERT)
|
||||
rows, err := tx.Model("report_field_config").
|
||||
Where("business_code", req.BusinessCode).
|
||||
Where("report_code", req.ReportCode).
|
||||
All()
|
||||
if err != nil {
|
||||
return fmt.Errorf("获取现有字段失败: %w", err)
|
||||
}
|
||||
existingMap := make(map[string]int64) // fieldCode -> id
|
||||
for _, row := range rows {
|
||||
existingMap[row["field_code"].String()] = row["id"].Int64()
|
||||
}
|
||||
|
||||
// 3. 处理请求中的字段(新增或更新)
|
||||
for i := range req.Fields {
|
||||
f := &req.Fields[i]
|
||||
status := ifVal(f.Status, model.StatusActive)
|
||||
dataType := ifVal(f.DataType, model.FieldTypeString)
|
||||
|
||||
if existingID, ok := existingMap[f.FieldCode]; ok {
|
||||
// 已存在 → 更新
|
||||
updateData := map[string]interface{}{
|
||||
"field_name": f.FieldName,
|
||||
"field_type": f.FieldType,
|
||||
"data_type": dataType,
|
||||
"field_role": f.FieldRole,
|
||||
"is_aggregatable": f.IsAggregatable,
|
||||
"is_filterable": f.IsFilterable,
|
||||
"is_queryable": f.IsQueryable,
|
||||
"is_sortable": f.IsSortable,
|
||||
"default_aggregate": f.DefaultAggregate,
|
||||
"valid_aggregates": f.ValidAggregates,
|
||||
"filter_operators": f.FilterOperators,
|
||||
"expression": f.Expression,
|
||||
"expression_type": f.ExpressionType,
|
||||
"format_pattern": f.FormatPattern,
|
||||
"unit": f.Unit,
|
||||
"dict_code": f.DictCode,
|
||||
"sort_order": f.SortOrder,
|
||||
"group_name": f.GroupName,
|
||||
"status": status,
|
||||
"updated_at": now,
|
||||
"updater": userName,
|
||||
}
|
||||
if _, err := tx.Model("report_field_config").Where("id", existingID).Data(updateData).Update(); err != nil {
|
||||
return fmt.Errorf("更新字段 %s 失败: %w", f.FieldCode, err)
|
||||
}
|
||||
delete(existingMap, f.FieldCode)
|
||||
} else {
|
||||
// 不存在 → 新增
|
||||
insertData := map[string]interface{}{
|
||||
"business_code": req.BusinessCode,
|
||||
"report_code": req.ReportCode,
|
||||
"field_code": f.FieldCode,
|
||||
"field_name": f.FieldName,
|
||||
"field_type": f.FieldType,
|
||||
"data_type": dataType,
|
||||
"field_role": f.FieldRole,
|
||||
"is_aggregatable": f.IsAggregatable,
|
||||
"is_filterable": f.IsFilterable,
|
||||
"is_queryable": f.IsQueryable,
|
||||
"is_sortable": f.IsSortable,
|
||||
"default_aggregate": f.DefaultAggregate,
|
||||
"valid_aggregates": f.ValidAggregates,
|
||||
"filter_operators": f.FilterOperators,
|
||||
"expression": f.Expression,
|
||||
"expression_type": f.ExpressionType,
|
||||
"format_pattern": f.FormatPattern,
|
||||
"unit": f.Unit,
|
||||
"dict_code": f.DictCode,
|
||||
"sort_order": f.SortOrder,
|
||||
"group_name": f.GroupName,
|
||||
"status": status,
|
||||
"created_at": now,
|
||||
"updated_at": now,
|
||||
"creator": userName,
|
||||
"updater": userName,
|
||||
"tenant_id": 1,
|
||||
}
|
||||
if _, err := tx.Model("report_field_config").Data(insertData).Insert(); err != nil {
|
||||
if strings.Contains(err.Error(), "duplicate key") {
|
||||
return fmt.Errorf("字段编码 %s 在当前报表下已存在,请更换字段编码", f.FieldCode)
|
||||
}
|
||||
return fmt.Errorf("创建字段 %s 失败: %w", f.FieldCode, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 删除剩余字段(不在请求中的旧字段)
|
||||
for fieldCode, existingID := range existingMap {
|
||||
if _, err := tx.Model("report_field_config").Where("id", existingID).Data(map[string]interface{}{
|
||||
"status": model.StatusInactive,
|
||||
"deleted_at": now,
|
||||
"updated_at": now,
|
||||
"updater": userName,
|
||||
}).Update(); err != nil {
|
||||
return fmt.Errorf("删除字段 %s 失败: %w", fieldCode, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 清理缓存
|
||||
s.configLoader.InvalidateCache(req.BusinessCode, req.ReportCode)
|
||||
|
||||
return &model.SaveResult{Success: true, ID: reportID, Message: "报表及字段保存成功"}, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配置 CRUD: 字段
|
||||
// ============================================================
|
||||
|
||||
// SaveField 保存字段配置(新增/修改合一)
|
||||
func (s *ReportService) SaveField(ctx context.Context, req *model.SaveFieldReq) (*model.SaveResult, error) {
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
if req.BusinessCode == "" {
|
||||
return nil, fmt.Errorf("businessCode 不能为空")
|
||||
}
|
||||
if req.ReportCode == "" {
|
||||
return nil, fmt.Errorf("reportCode 不能为空")
|
||||
}
|
||||
|
||||
userName := getUserNameFromCtx(ctx)
|
||||
field := &model.FieldConfig{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: userName, Updater: userName, TenantId: 1},
|
||||
BusinessCode: req.BusinessCode,
|
||||
ReportCode: req.ReportCode,
|
||||
FieldCode: req.FieldCode,
|
||||
FieldName: req.FieldName,
|
||||
FieldType: req.FieldType,
|
||||
DataType: req.DataType,
|
||||
FieldRole: req.FieldRole,
|
||||
IsAggregatable: req.IsAggregatable,
|
||||
IsFilterable: req.IsFilterable,
|
||||
IsQueryable: req.IsQueryable,
|
||||
IsSortable: req.IsSortable,
|
||||
DefaultAggregate: req.DefaultAggregate,
|
||||
ValidAggregates: req.ValidAggregates,
|
||||
FilterOperators: req.FilterOperators,
|
||||
Expression: req.Expression,
|
||||
ExpressionType: req.ExpressionType,
|
||||
FormatPattern: req.FormatPattern,
|
||||
Unit: req.Unit,
|
||||
DictCode: req.DictCode,
|
||||
SortOrder: req.SortOrder,
|
||||
GroupName: req.GroupName,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
// 校验字段类型
|
||||
validFieldTypes := map[string]bool{
|
||||
model.FieldTypeString: true,
|
||||
model.FieldTypeInt: true,
|
||||
model.FieldTypeFloat: true,
|
||||
model.FieldTypeDate: true,
|
||||
model.FieldTypeDatetime: true,
|
||||
model.FieldTypeJsonb: true,
|
||||
}
|
||||
if req.FieldType != "" && !validFieldTypes[req.FieldType] {
|
||||
return nil, fmt.Errorf("不支持的字段类型: %s,仅支持 STRING/INT/FLOAT/DATE/DATETIME/JSONB", req.FieldType)
|
||||
}
|
||||
if req.DataType != "" && !validFieldTypes[req.DataType] {
|
||||
return nil, fmt.Errorf("不支持的存储类型: %s,仅支持 STRING/INT/FLOAT/DATE/DATETIME/JSONB", req.DataType)
|
||||
}
|
||||
|
||||
if req.Status == "" {
|
||||
field.Status = model.StatusActive
|
||||
}
|
||||
if field.DataType == "" {
|
||||
field.DataType = model.FieldTypeString
|
||||
}
|
||||
if field.ValidAggregates == nil {
|
||||
field.ValidAggregates = []string{}
|
||||
}
|
||||
if field.FilterOperators == nil {
|
||||
field.FilterOperators = []string{"=", "!=", ">", "<", ">=", "<=", "IN", "LIKE", "BETWEEN"}
|
||||
}
|
||||
|
||||
// 新增时校验字段编码唯一性
|
||||
if req.ID == nil || *req.ID == 0 {
|
||||
existing, err := s.configLoader.GetReportFields(ctx, req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("校验字段编码失败: %w", err)
|
||||
}
|
||||
allFields := append(append(existing.Dimensions, existing.Indicators...), existing.Filters...)
|
||||
for _, f := range allFields {
|
||||
if f.FieldCode == req.FieldCode && f.Status == model.StatusActive {
|
||||
return nil, fmt.Errorf("字段编码 %s 已存在(业务: %s, 报表: %s)", req.FieldCode, req.BusinessCode, req.ReportCode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if req.ID != nil && *req.ID > 0 {
|
||||
field.Id = *req.ID
|
||||
if err := s.configLoader.UpdateField(ctx, field); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: *req.ID, Message: "更新成功"}, nil
|
||||
}
|
||||
|
||||
id, err := s.configLoader.CreateField(ctx, field)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: id, Message: "创建成功"}, nil
|
||||
}
|
||||
|
||||
// DeleteField 删除字段配置
|
||||
func (s *ReportService) DeleteField(ctx context.Context, id int64) (*model.DeleteResult, error) {
|
||||
field, err := s.configLoader.GetFieldByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.configLoader.DeleteField(ctx, id, field.BusinessCode, field.ReportCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.DeleteResult{Success: true, Message: "删除成功"}, nil
|
||||
}
|
||||
|
||||
// GetField 获取单个字段配置
|
||||
func (s *ReportService) GetField(ctx context.Context, id int64) (*model.FieldConfig, error) {
|
||||
return s.configLoader.GetFieldByID(ctx, id)
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配置 CRUD: 抽取配置
|
||||
// ============================================================
|
||||
|
||||
// SaveExtractConfig 保存抽取配置(新增/修改合一)
|
||||
func (s *ReportService) SaveExtractConfig(ctx context.Context, req *model.SaveExtractConfigReq) (*model.SaveResult, error) {
|
||||
if err := initTables(ctx); err != nil {
|
||||
return nil, fmt.Errorf("初始化系统表失败: %w", err)
|
||||
}
|
||||
|
||||
// 新增时校验 extractCode 唯一性(同业务+报表下)
|
||||
if req.ID == nil || *req.ID == 0 {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx, "SELECT id FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND extract_code = $3 LIMIT 1",
|
||||
req.BusinessCode, req.ReportCode, req.ExtractCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("校验抽取编码失败: %w", err)
|
||||
}
|
||||
if !r.IsEmpty() {
|
||||
return nil, fmt.Errorf("业务 %s/报表 %s 下抽取编码 %s 已存在", req.BusinessCode, req.ReportCode, req.ExtractCode)
|
||||
}
|
||||
}
|
||||
|
||||
userName := getUserNameFromCtx(ctx)
|
||||
ec := &model.ExtractConfig{
|
||||
SQLBaseDO: beans.SQLBaseDO{Creator: userName, Updater: userName, TenantId: 1},
|
||||
BusinessCode: req.BusinessCode,
|
||||
ReportCode: req.ReportCode,
|
||||
ExtractCode: req.ExtractCode,
|
||||
ExtractName: req.ExtractName,
|
||||
SourceTableName: req.SourceTableName,
|
||||
SourceTableAlias: req.SourceTableAlias,
|
||||
TargetTableName: req.TargetTableName,
|
||||
IsEnabled: req.IsEnabled,
|
||||
ExtractType: req.ExtractType,
|
||||
ExtractMode: req.ExtractMode,
|
||||
ExtractKeyField: req.ExtractKeyField,
|
||||
ExtractKeyFormat: req.ExtractKeyFormat,
|
||||
GroupByFields: req.GroupByFields,
|
||||
FilterExpression: req.FilterExpression,
|
||||
JoinConfigs: req.JoinConfigs,
|
||||
FieldMappings: req.FieldMappings,
|
||||
TransformRules: req.TransformRules,
|
||||
BatchSize: req.BatchSize,
|
||||
Status: req.Status,
|
||||
}
|
||||
|
||||
if req.Status == "" {
|
||||
ec.Status = model.StatusActive
|
||||
}
|
||||
if ec.ExtractType == "" {
|
||||
ec.ExtractType = model.ExtractTypeIncremental
|
||||
}
|
||||
if ec.ExtractMode == "" {
|
||||
ec.ExtractMode = model.ExtractModeDirect
|
||||
}
|
||||
if ec.BatchSize == 0 {
|
||||
ec.BatchSize = 1000
|
||||
}
|
||||
if ec.JoinConfigs == nil {
|
||||
ec.JoinConfigs = []model.JoinConfig{}
|
||||
}
|
||||
if ec.FieldMappings == nil {
|
||||
ec.FieldMappings = []model.FieldMapping{}
|
||||
}
|
||||
if ec.TransformRules == nil {
|
||||
ec.TransformRules = []model.TransformRule{}
|
||||
}
|
||||
if ec.GroupByFields == nil {
|
||||
ec.GroupByFields = []string{}
|
||||
}
|
||||
|
||||
if req.ID != nil && *req.ID > 0 {
|
||||
ec.Id = *req.ID
|
||||
if err := s.configLoader.UpdateExtractConfig(ctx, ec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: *req.ID, Message: "更新成功"}, nil
|
||||
}
|
||||
|
||||
id, err := s.configLoader.CreateExtractConfig(ctx, ec)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.SaveResult{Success: true, ID: id, Message: "创建成功"}, nil
|
||||
}
|
||||
|
||||
// DeleteExtractConfig 删除抽取配置
|
||||
func (s *ReportService) DeleteExtractConfig(ctx context.Context, id int64) (*model.DeleteResult, error) {
|
||||
ec, err := s.configLoader.GetExtractConfigByID(ctx, id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := s.configLoader.DeleteExtractConfig(ctx, id, ec.BusinessCode, ec.ReportCode); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &model.DeleteResult{Success: true, Message: "删除成功"}, nil
|
||||
}
|
||||
|
||||
// GetExtractConfig 获取单个抽取配置
|
||||
func (s *ReportService) GetExtractConfig(ctx context.Context, id int64) (*model.ExtractConfig, error) {
|
||||
return s.configLoader.GetExtractConfigByID(ctx, id)
|
||||
}
|
||||
|
||||
// GetExtractConfigs 获取业务报表下所有抽取配置
|
||||
|
||||
// ListExtractConfigs 分页获取抽取配置列表
|
||||
func (s *ReportService) ListExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {
|
||||
return s.configLoader.ListExtractConfigs(ctx, businessCode, reportCode, pageNum, pageSize)
|
||||
}
|
||||
func (s *ReportService) GetExtractConfigs(ctx context.Context, businessCode, reportCode string) ([]model.ExtractConfig, error) {
|
||||
return s.configLoader.GetExtractConfigs(ctx, businessCode, reportCode)
|
||||
}
|
||||
|
||||
// GetExtractConfigsAll 获取业务报表下所有抽取配置(不限 status,用于填充字段展示)
|
||||
func (s *ReportService) GetExtractConfigsAll(ctx context.Context, businessCode, reportCode string) ([]model.ExtractConfig, error) {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT id, extract_code, extract_name, status FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND status != $3 ORDER BY id ASC",
|
||||
businessCode, reportCode, model.StatusInactive)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configs []model.ExtractConfig
|
||||
for _, record := range r {
|
||||
var ec model.ExtractConfig
|
||||
if err := record.Struct(&ec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
configs = append(configs, ec)
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// ifVal 三目运算:非空返回 s,否则返回 fallback
|
||||
func ifVal(s, fallback string) string {
|
||||
if s == "" {
|
||||
return fallback
|
||||
}
|
||||
return s
|
||||
}
|
||||
@@ -0,0 +1,528 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"dataengine/common/report/config"
|
||||
"dataengine/common/report/model"
|
||||
)
|
||||
|
||||
// SQLBuilder 动态SQL构建器
|
||||
type SQLBuilder struct {
|
||||
loader *config.ConfigLoader
|
||||
}
|
||||
|
||||
// NewSQLBuilder 创建SQL构建器
|
||||
func NewSQLBuilder() *SQLBuilder {
|
||||
return &SQLBuilder{
|
||||
loader: config.GetLoader(),
|
||||
}
|
||||
}
|
||||
|
||||
// BuildQuerySQL 根据用户选择构建查询SQL
|
||||
func (b *SQLBuilder) BuildQuerySQL(ctx context.Context, req *model.UserSelectQueryReq) (string, []interface{}, map[string]interface{}, error) {
|
||||
// 1. 校验配置
|
||||
report, err := b.loader.GetReport(ctx, req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("获取报表配置失败: %w", err)
|
||||
}
|
||||
|
||||
fieldMap, err := b.loader.GetFieldMap(ctx, req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
return "", nil, nil, fmt.Errorf("获取字段配置失败: %w", err)
|
||||
}
|
||||
|
||||
tableName := report.StatTableName
|
||||
|
||||
// 2. 构建 SELECT 部分
|
||||
selectClause, err := b.buildSelectClause(req, fieldMap, report)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// 3. 构建 FROM 部分
|
||||
fromClause := tableName
|
||||
|
||||
// 4. 构建 WHERE 部分
|
||||
whereClause, whereArgs, err := b.buildWhereClause(ctx, req, fieldMap, report)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// 5. 构建 GROUP BY 部分
|
||||
groupByClause, err := b.buildGroupByClause(req, fieldMap)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// 6. 构建 ORDER BY 部分
|
||||
orderByClause, err := b.buildOrderByClause(req, fieldMap)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
|
||||
// 7. 组合完整SQL
|
||||
var sql strings.Builder
|
||||
sql.WriteString("SELECT ")
|
||||
sql.WriteString(selectClause)
|
||||
sql.WriteString(" FROM ")
|
||||
sql.WriteString(fromClause)
|
||||
|
||||
if whereClause != "" {
|
||||
sql.WriteString(" WHERE ")
|
||||
sql.WriteString(whereClause)
|
||||
}
|
||||
|
||||
if groupByClause != "" {
|
||||
sql.WriteString(" GROUP BY ")
|
||||
sql.WriteString(groupByClause)
|
||||
}
|
||||
|
||||
if orderByClause != "" {
|
||||
sql.WriteString(" ORDER BY ")
|
||||
sql.WriteString(orderByClause)
|
||||
}
|
||||
|
||||
// 8. 统计总数SQL
|
||||
countSql := "SELECT COUNT(*) FROM " + fromClause
|
||||
if whereClause != "" {
|
||||
countSql += " WHERE " + whereClause
|
||||
}
|
||||
if groupByClause != "" {
|
||||
if whereClause != "" {
|
||||
countSql = fmt.Sprintf("SELECT COUNT(*) FROM (SELECT 1 FROM %s WHERE %s GROUP BY %s) AS t",
|
||||
fromClause, whereClause, groupByClause)
|
||||
} else {
|
||||
countSql = fmt.Sprintf("SELECT COUNT(*) FROM (SELECT 1 FROM %s GROUP BY %s) AS t",
|
||||
fromClause, groupByClause)
|
||||
}
|
||||
}
|
||||
|
||||
metadata := map[string]interface{}{
|
||||
"countSql": countSql,
|
||||
"tableName": tableName,
|
||||
"reportConfig": report,
|
||||
}
|
||||
|
||||
return sql.String(), whereArgs, metadata, nil
|
||||
}
|
||||
|
||||
// buildSelectClause 构建SELECT子句
|
||||
func (b *SQLBuilder) buildSelectClause(req *model.UserSelectQueryReq, fieldMap map[string]*model.FieldConfig, report *model.ReportConfig) (string, error) {
|
||||
var selectParts []string
|
||||
|
||||
// 1. 添加维度字段
|
||||
for _, dim := range req.Dimensions {
|
||||
dim = strings.TrimSpace(dim)
|
||||
if dim == "" {
|
||||
continue
|
||||
}
|
||||
fc, ok := fieldMap[dim]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("维度字段不存在: %s", dim)
|
||||
}
|
||||
if fc.FieldRole != model.RoleDimension && fc.FieldRole != model.RoleFilter {
|
||||
return "", fmt.Errorf("字段 %s 不可作为维度", dim)
|
||||
}
|
||||
selectParts = append(selectParts, dim)
|
||||
}
|
||||
|
||||
// 2. 添加指标字段(含聚合)
|
||||
if len(req.Indicators) == 0 {
|
||||
return "", fmt.Errorf("必须选择至少一个指标")
|
||||
}
|
||||
|
||||
for _, ind := range req.Indicators {
|
||||
fc, ok := fieldMap[ind.FieldCode]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("指标字段不存在: %s", ind.FieldCode)
|
||||
}
|
||||
|
||||
alias := ind.Alias
|
||||
if alias == "" {
|
||||
alias = ind.FieldCode
|
||||
}
|
||||
|
||||
agg := strings.ToUpper(ind.Aggregate)
|
||||
if agg == "" {
|
||||
agg = fc.DefaultAggregate
|
||||
if agg == "" {
|
||||
agg = model.AggregateSum
|
||||
}
|
||||
}
|
||||
|
||||
// 校验聚合方式
|
||||
if len(fc.ValidAggregates) > 0 {
|
||||
valid := false
|
||||
for _, v := range fc.ValidAggregates {
|
||||
if strings.ToUpper(v) == agg {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return "", fmt.Errorf("字段 %s 不支持聚合方式 %s", ind.FieldCode, agg)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理衍生指标(表达式)
|
||||
if fc.ExpressionType == "CALCULATED" && fc.Expression != "" {
|
||||
expr := b.parseExpression(fc.Expression, req.Indicators)
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s AS %s", expr, alias))
|
||||
} else {
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s(%s) AS %s", agg, ind.FieldCode, alias))
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 添加时间分组字段
|
||||
if req.TimeGroup != "" && req.TimeGroup != "day" {
|
||||
dateField := report.DateField
|
||||
if dateField == "" {
|
||||
dateField = "stat_date"
|
||||
}
|
||||
timeGroupExpr := b.buildTimeGroupExpr(dateField, req.TimeGroup)
|
||||
selectParts = append(selectParts, timeGroupExpr)
|
||||
}
|
||||
|
||||
return strings.Join(selectParts, ", "), nil
|
||||
}
|
||||
|
||||
// buildWhereClause 构建WHERE子句
|
||||
func (b *SQLBuilder) buildWhereClause(ctx context.Context, req *model.UserSelectQueryReq, fieldMap map[string]*model.FieldConfig, report *model.ReportConfig) (string, []interface{}, error) {
|
||||
var conditions []string
|
||||
var args []interface{}
|
||||
|
||||
// 1. 租户过滤
|
||||
conditions = append(conditions, "tenant_id = 1")
|
||||
|
||||
// 2. 时间范围过滤
|
||||
if req.TimeRange != nil {
|
||||
dateField := report.DateField
|
||||
if dateField == "" {
|
||||
dateField = "stat_date"
|
||||
}
|
||||
|
||||
if req.TimeRange.StartDate != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("%s >= ?", dateField))
|
||||
args = append(args, req.TimeRange.StartDate)
|
||||
}
|
||||
if req.TimeRange.EndDate != "" {
|
||||
conditions = append(conditions, fmt.Sprintf("%s <= ?", dateField))
|
||||
args = append(args, req.TimeRange.EndDate)
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 业务过滤
|
||||
conditions = append(conditions, "business_code = ?")
|
||||
args = append(args, req.BusinessCode)
|
||||
|
||||
// 4. 用户筛选条件
|
||||
for _, filter := range req.Filters {
|
||||
fc, ok := fieldMap[filter.FieldCode]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("筛选字段不存在: %s", filter.FieldCode)
|
||||
}
|
||||
|
||||
if !fc.IsFilterable {
|
||||
return "", nil, fmt.Errorf("字段 %s 不可用于筛选", filter.FieldCode)
|
||||
}
|
||||
|
||||
op := strings.ToUpper(filter.Operator)
|
||||
if op == "" {
|
||||
op = "="
|
||||
}
|
||||
|
||||
// 校验操作符
|
||||
if len(fc.FilterOperators) > 0 {
|
||||
valid := false
|
||||
for _, v := range fc.FilterOperators {
|
||||
if strings.ToUpper(v) == op {
|
||||
valid = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
return "", nil, fmt.Errorf("字段 %s 不支持操作符 %s", filter.FieldCode, op)
|
||||
}
|
||||
}
|
||||
|
||||
cond, vals, err := b.buildFilterCondition(filter, op, fc.FieldType)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
conditions = append(conditions, cond)
|
||||
args = append(args, vals...)
|
||||
}
|
||||
|
||||
return strings.Join(conditions, " AND "), args, nil
|
||||
}
|
||||
|
||||
// buildFilterCondition 构建单个筛选条件
|
||||
func (b *SQLBuilder) buildFilterCondition(filter model.FilterCondition, op string, fieldType string) (string, []interface{}, error) {
|
||||
field := filter.FieldCode
|
||||
var args []interface{}
|
||||
|
||||
switch op {
|
||||
case "=":
|
||||
return fmt.Sprintf("%s = ?", field), []interface{}{filter.Value}, nil
|
||||
case "!=":
|
||||
return fmt.Sprintf("%s != ?", field), []interface{}{filter.Value}, nil
|
||||
case ">":
|
||||
return fmt.Sprintf("%s > ?", field), []interface{}{filter.Value}, nil
|
||||
case "<":
|
||||
return fmt.Sprintf("%s < ?", field), []interface{}{filter.Value}, nil
|
||||
case ">=":
|
||||
return fmt.Sprintf("%s >= ?", field), []interface{}{filter.Value}, nil
|
||||
case "<=":
|
||||
return fmt.Sprintf("%s <= ?", field), []interface{}{filter.Value}, nil
|
||||
case "IN":
|
||||
values, err := b.convertToSlice(filter.Value)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
placeholders := make([]string, len(values))
|
||||
for i := range values {
|
||||
placeholders[i] = "?"
|
||||
args = append(args, values[i])
|
||||
}
|
||||
return fmt.Sprintf("%s IN (%s)", field, strings.Join(placeholders, ",")), args, nil
|
||||
case "LIKE":
|
||||
return fmt.Sprintf("%s LIKE ?", field), []interface{}{"%" + fmt.Sprintf("%v", filter.Value) + "%"}, nil
|
||||
case "BETWEEN":
|
||||
return fmt.Sprintf("%s BETWEEN ? AND ?", field), []interface{}{filter.Value, filter.Value2}, nil
|
||||
default:
|
||||
return fmt.Sprintf("%s = ?", field), []interface{}{filter.Value}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// buildGroupByClause 构建GROUP BY子句
|
||||
func (b *SQLBuilder) buildGroupByClause(req *model.UserSelectQueryReq, fieldMap map[string]*model.FieldConfig) (string, error) {
|
||||
var groupFields []string
|
||||
for _, dim := range req.Dimensions {
|
||||
fc, ok := fieldMap[dim]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if fc.FieldRole == model.RoleDimension || fc.FieldRole == model.RoleFilter {
|
||||
groupFields = append(groupFields, dim)
|
||||
}
|
||||
}
|
||||
|
||||
if len(groupFields) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
return strings.Join(groupFields, ", "), nil
|
||||
}
|
||||
|
||||
// buildOrderByClause 构建ORDER BY子句
|
||||
func (b *SQLBuilder) buildOrderByClause(req *model.UserSelectQueryReq, fieldMap map[string]*model.FieldConfig) (string, error) {
|
||||
if len(req.OrderBy) == 0 {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var orderParts []string
|
||||
for _, order := range req.OrderBy {
|
||||
field := order.FieldCode
|
||||
dir := strings.ToUpper(order.Direction)
|
||||
if dir == "" {
|
||||
dir = "ASC"
|
||||
}
|
||||
if dir != "ASC" && dir != "DESC" {
|
||||
return "", fmt.Errorf("排序方向必须是 ASC 或 DESC")
|
||||
}
|
||||
|
||||
fc, ok := fieldMap[field]
|
||||
if !ok {
|
||||
return "", fmt.Errorf("排序字段不存在: %s", field)
|
||||
}
|
||||
|
||||
if !fc.IsSortable {
|
||||
return "", fmt.Errorf("字段 %s 不可排序", field)
|
||||
}
|
||||
|
||||
orderParts = append(orderParts, fmt.Sprintf("%s %s", field, dir))
|
||||
}
|
||||
|
||||
return strings.Join(orderParts, ", "), nil
|
||||
}
|
||||
|
||||
// buildTimeGroupExpr 构建时间分组表达式
|
||||
func (b *SQLBuilder) buildTimeGroupExpr(dateField, timeGroup string) string {
|
||||
switch timeGroup {
|
||||
case "week":
|
||||
return fmt.Sprintf("DATE_TRUNC('week', %s::date)::text AS time_group", dateField)
|
||||
case "month":
|
||||
return fmt.Sprintf("TO_CHAR(%s::date, 'YYYY-MM') AS time_group", dateField)
|
||||
case "year":
|
||||
return fmt.Sprintf("TO_CHAR(%s::date, 'YYYY') AS time_group", dateField)
|
||||
case "quarter":
|
||||
return "TO_CHAR(" + dateField + "::date, 'YYYY-\"Q\"Q') AS time_group"
|
||||
default:
|
||||
return dateField + " AS time_group"
|
||||
}
|
||||
}
|
||||
|
||||
// parseExpression 解析衍生指标表达式
|
||||
// 将 {fieldCode} 占位符替换为原始字段名,当字段不在 indicators 中时仍返回 fieldCode
|
||||
// 避免未替换的 {fieldCode} 占位符泄漏到 SQL 中导致语法错误
|
||||
func (b *SQLBuilder) parseExpression(expr string, indicators []model.IndicatorSelect) string {
|
||||
re := regexp.MustCompile(`\{([^}]+)\}`)
|
||||
return re.ReplaceAllStringFunc(expr, func(match string) string {
|
||||
fieldCode := match[1 : len(match)-1]
|
||||
for _, ind := range indicators {
|
||||
if ind.FieldCode == fieldCode {
|
||||
return fieldCode
|
||||
}
|
||||
}
|
||||
// 字段不在 indicators 中也返回裸字段名(避免 {fieldCode} 残留 SQL 导致语法错误)
|
||||
return fieldCode
|
||||
})
|
||||
}
|
||||
|
||||
// convertToSlice 转换为切片
|
||||
func (b *SQLBuilder) convertToSlice(v interface{}) ([]interface{}, error) {
|
||||
switch val := v.(type) {
|
||||
case []interface{}:
|
||||
return val, nil
|
||||
case []string:
|
||||
result := make([]interface{}, len(val))
|
||||
for i, s := range val {
|
||||
result[i] = s
|
||||
}
|
||||
return result, nil
|
||||
case string:
|
||||
parts := strings.Split(val, ",")
|
||||
result := make([]interface{}, len(parts))
|
||||
for i, p := range parts {
|
||||
result[i] = strings.TrimSpace(p)
|
||||
}
|
||||
return result, nil
|
||||
default:
|
||||
return []interface{}{v}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// BuildCountSQL 构建统计总数SQL
|
||||
func (b *SQLBuilder) BuildCountSQL(sql string) string {
|
||||
sql = regexp.MustCompile(`(?i)SELECT\s+.*?\s+FROM`).ReplaceAllString(sql, "SELECT COUNT(*) FROM")
|
||||
return sql
|
||||
}
|
||||
|
||||
// AddLimit 添加分页
|
||||
func (b *SQLBuilder) AddLimit(sql string, page, pageSize int) string {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if pageSize < 1 {
|
||||
pageSize = 20
|
||||
}
|
||||
if pageSize > 1000 {
|
||||
pageSize = 1000
|
||||
}
|
||||
offset := (page - 1) * pageSize
|
||||
return fmt.Sprintf("%s LIMIT %d OFFSET %d", sql, pageSize, offset)
|
||||
}
|
||||
|
||||
// GenerateInsertSQL 生成upsert SQL
|
||||
func (b *SQLBuilder) GenerateInsertSQL(tableName string, columns []string, conflictKeys []string) string {
|
||||
cols := strings.Join(columns, ", ")
|
||||
placeholders := make([]string, len(columns))
|
||||
for i := range columns {
|
||||
placeholders[i] = fmt.Sprintf("$%d", i+1)
|
||||
}
|
||||
placeholdersStr := strings.Join(placeholders, ", ")
|
||||
|
||||
var updateParts []string
|
||||
for _, col := range columns {
|
||||
if col == "id" || col == "created_at" {
|
||||
continue
|
||||
}
|
||||
updateParts = append(updateParts, fmt.Sprintf("%s = EXCLUDED.%s", col, col))
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", tableName, cols, placeholdersStr)
|
||||
|
||||
if len(conflictKeys) > 0 {
|
||||
sql += " ON CONFLICT (" + strings.Join(conflictKeys, ", ") + ")"
|
||||
sql += " DO UPDATE SET " + strings.Join(updateParts, ", ")
|
||||
}
|
||||
|
||||
return sql
|
||||
}
|
||||
|
||||
// BuildExtractSQL 构建数据抽取SQL
|
||||
func (b *SQLBuilder) BuildExtractSQL(ctx context.Context, extractConfig *model.ExtractConfig, statDate string) (string, []interface{}, error) {
|
||||
var selectParts []string
|
||||
var args []interface{}
|
||||
|
||||
// 基础字段
|
||||
selectParts = append(selectParts, "tenant_id")
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS business_code", extractConfig.BusinessCode))
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS stat_date", statDate))
|
||||
|
||||
// 字段映射
|
||||
sourceTable := extractConfig.SourceTableName
|
||||
if extractConfig.SourceTableAlias != "" {
|
||||
sourceTable = extractConfig.SourceTableAlias
|
||||
}
|
||||
|
||||
for _, mapping := range extractConfig.FieldMappings {
|
||||
targetField := mapping.TargetField
|
||||
sourceField := mapping.SourceField
|
||||
|
||||
if mapping.TransformRule != nil {
|
||||
expr := b.applyTransformRule(mapping.TransformRule, sourceField)
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s AS %s", expr, targetField))
|
||||
} else {
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s.%s AS %s", sourceTable, sourceField, targetField))
|
||||
}
|
||||
}
|
||||
|
||||
// 构建 FROM 和 JOIN
|
||||
fromClause := extractConfig.SourceTableName
|
||||
if extractConfig.SourceTableAlias != "" {
|
||||
fromClause += " " + extractConfig.SourceTableAlias
|
||||
}
|
||||
|
||||
for _, join := range extractConfig.JoinConfigs {
|
||||
joinType := "LEFT JOIN"
|
||||
if strings.ToUpper(join.JoinType) == "INNER" {
|
||||
joinType = "INNER JOIN"
|
||||
} else if strings.ToUpper(join.JoinType) == "RIGHT" {
|
||||
joinType = "RIGHT JOIN"
|
||||
}
|
||||
fromClause += fmt.Sprintf(" %s %s %s ON %s", joinType, join.JoinTable, join.JoinAlias, join.JoinCondition)
|
||||
}
|
||||
|
||||
// WHERE 条件
|
||||
whereClause := ""
|
||||
if extractConfig.FilterExpression != "" {
|
||||
whereClause = " WHERE " + extractConfig.FilterExpression
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("SELECT %s FROM %s%s", strings.Join(selectParts, ", "), fromClause, whereClause)
|
||||
|
||||
return sql, args, nil
|
||||
}
|
||||
|
||||
// applyTransformRule 应用转换规则
|
||||
func (b *SQLBuilder) applyTransformRule(rule *model.TransformRule, sourceField string) string {
|
||||
switch rule.RuleType {
|
||||
case "CALCULATE":
|
||||
if rule.Expression != "" {
|
||||
return strings.ReplaceAll(rule.Expression, "{source}", sourceField)
|
||||
}
|
||||
case "FORMAT":
|
||||
if rule.Format != "" {
|
||||
return fmt.Sprintf("TO_CHAR(%s, '%s')", sourceField, rule.Format)
|
||||
}
|
||||
case "MAPPING":
|
||||
// 运行时映射,需要在代码中处理
|
||||
return sourceField
|
||||
}
|
||||
return sourceField
|
||||
}
|
||||
@@ -0,0 +1,484 @@
|
||||
package builder
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"dataengine/common/report/model"
|
||||
)
|
||||
|
||||
// Test data - reusable field configs
|
||||
var testFields = map[string]*model.FieldConfig{
|
||||
"shop_id": {FieldCode: "shop_id", FieldName: "店铺ID", FieldType: "STRING", FieldRole: "DIMENSION", IsFilterable: true, IsSortable: true, FilterOperators: []string{"=", "IN"}},
|
||||
"shop_name": {FieldCode: "shop_name", FieldName: "店铺名称", FieldType: "STRING", FieldRole: "DIMENSION", IsFilterable: true, IsSortable: true, FilterOperators: []string{"=", "LIKE"}},
|
||||
"stat_date": {FieldCode: "stat_date", FieldName: "统计日期", FieldType: "DATE", FieldRole: "DIMENSION", IsFilterable: true, IsSortable: true, FilterOperators: []string{"=", ">=", "<=", "BETWEEN"}},
|
||||
"order_count": {FieldCode: "order_count", FieldName: "订单数", FieldType: "INT", FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "SUM", ValidAggregates: []string{"SUM", "COUNT"}},
|
||||
"order_amount": {FieldCode: "order_amount", FieldName: "订单金额", FieldType: "FLOAT", FieldRole: "INDICATOR", IsAggregatable: true, IsSortable: true, DefaultAggregate: "SUM", ValidAggregates: []string{"SUM", "AVG", "MAX", "MIN"}},
|
||||
"refund_rate": {FieldCode: "refund_rate", FieldName: "退款率", FieldType: "FLOAT", FieldRole: "INDICATOR", IsAggregatable: true, DefaultAggregate: "AVG", ValidAggregates: []string{"AVG"}, Expression: "{refund_amount} / NULLIF({order_amount}, 0) * 100", ExpressionType: "CALCULATED"},
|
||||
"order_status": {FieldCode: "order_status", FieldName: "订单状态", FieldType: "STRING", FieldRole: "FILTER", IsFilterable: true, FilterOperators: []string{"=", "IN"}},
|
||||
}
|
||||
|
||||
// Test data - reusable report config
|
||||
var testReport = &model.ReportConfig{
|
||||
StatTableName: "test_stat_table",
|
||||
DateField: "stat_date",
|
||||
}
|
||||
|
||||
func TestBuildSelectClause(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("basic dimensions + indicators", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id", "shop_name"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_count", Aggregate: "SUM", Alias: "total_orders"},
|
||||
{FieldCode: "order_amount", Aggregate: "SUM", Alias: "total_amount"},
|
||||
},
|
||||
}
|
||||
sql, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if sql == "" {
|
||||
t.Fatal("empty SELECT clause")
|
||||
}
|
||||
assertContains(t, sql, "shop_id")
|
||||
assertContains(t, sql, "shop_name")
|
||||
assertContains(t, sql, "SUM(order_count) AS total_orders")
|
||||
assertContains(t, sql, "SUM(order_amount) AS total_amount")
|
||||
})
|
||||
|
||||
t.Run("calculated field", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_count", Aggregate: "SUM", Alias: "orders"},
|
||||
{FieldCode: "refund_rate", Aggregate: "AVG", Alias: "rate"},
|
||||
},
|
||||
}
|
||||
sql, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, sql, "shop_id")
|
||||
assertContains(t, sql, "SUM(order_count) AS orders")
|
||||
assertContains(t, sql, "refund_amount")
|
||||
assertContains(t, sql, "order_amount")
|
||||
})
|
||||
|
||||
t.Run("time_group weekly", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_count", Aggregate: "SUM", Alias: "orders"},
|
||||
},
|
||||
TimeGroup: "week",
|
||||
}
|
||||
sql, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, sql, "DATE_TRUNC")
|
||||
assertContains(t, sql, "time_group")
|
||||
})
|
||||
|
||||
t.Run("time_group monthly", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_amount", Aggregate: "SUM"},
|
||||
},
|
||||
TimeGroup: "month",
|
||||
}
|
||||
sql, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, sql, "YYYY-MM")
|
||||
assertContains(t, sql, "time_group")
|
||||
})
|
||||
|
||||
t.Run("invalid dimension field", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"nonexistent_field"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_count", Aggregate: "SUM"},
|
||||
},
|
||||
}
|
||||
_, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nonexistent dimension")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("no indicators", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{}
|
||||
_, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for no indicators")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid aggregate", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id"},
|
||||
Indicators: []model.IndicatorSelect{
|
||||
{FieldCode: "order_count", Aggregate: "AVG"},
|
||||
},
|
||||
}
|
||||
_, err := b.buildSelectClause(req, testFields, testReport)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for invalid aggregate")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildFilterCondition(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
operator string
|
||||
value interface{}
|
||||
value2 interface{}
|
||||
wantLike string
|
||||
}{
|
||||
{"operator =", "=", "active", nil, "order_status = ?"},
|
||||
{"operator !=", "!=", "cancelled", nil, "order_status != ?"},
|
||||
{"operator >", ">", 100, nil, "order_amount > ?"},
|
||||
{"operator <", "<", 50, nil, "order_amount < ?"},
|
||||
{"operator >=", ">=", 100, nil, "order_amount >= ?"},
|
||||
{"operator <=", "<=", 50, nil, "order_amount <= ?"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
filter := model.FilterCondition{
|
||||
FieldCode: "order_amount",
|
||||
Operator: tt.operator,
|
||||
Value: tt.value,
|
||||
Value2: tt.value2,
|
||||
}
|
||||
cond, args, err := b.buildFilterCondition(filter, tt.operator, "FLOAT")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if cond == "" {
|
||||
t.Fatal("empty condition")
|
||||
}
|
||||
if len(args) == 0 {
|
||||
t.Fatal("no args")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFilterConditionIN(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
filter := model.FilterCondition{
|
||||
FieldCode: "order_status",
|
||||
Operator: "IN",
|
||||
Value: []interface{}{1, 2, 3},
|
||||
}
|
||||
cond, args, err := b.buildFilterCondition(filter, "IN", "STRING")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, cond, "order_status IN (?,?,?)")
|
||||
if len(args) != 3 {
|
||||
t.Fatalf("expected 3 args, got %d", len(args))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildFilterConditionLIKE(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
filter := model.FilterCondition{
|
||||
FieldCode: "shop_name",
|
||||
Operator: "LIKE",
|
||||
Value: "旗舰",
|
||||
}
|
||||
cond, args, err := b.buildFilterCondition(filter, "LIKE", "STRING")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, cond, "shop_name LIKE ?")
|
||||
if len(args) != 1 {
|
||||
t.Fatalf("expected 1 arg, got %d", len(args))
|
||||
}
|
||||
val := args[0].(string)
|
||||
assertContains(t, val, "%旗舰%")
|
||||
}
|
||||
|
||||
func TestBuildFilterConditionBETWEEN(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
filter := model.FilterCondition{
|
||||
FieldCode: "stat_date",
|
||||
Operator: "BETWEEN",
|
||||
Value: "2026-06-01",
|
||||
Value2: "2026-06-16",
|
||||
}
|
||||
cond, args, err := b.buildFilterCondition(filter, "BETWEEN", "DATE")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, cond, "stat_date BETWEEN ? AND ?")
|
||||
if len(args) != 2 {
|
||||
t.Fatalf("expected 2 args, got %d", len(args))
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildGroupByClause(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("with dimensions", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
Dimensions: []string{"shop_id", "shop_name"},
|
||||
}
|
||||
gb, err := b.buildGroupByClause(req, testFields)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, gb, "shop_id")
|
||||
assertContains(t, gb, "shop_name")
|
||||
})
|
||||
|
||||
t.Run("no dimensions", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{}
|
||||
gb, err := b.buildGroupByClause(req, testFields)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if gb != "" {
|
||||
t.Fatalf("expected empty, got %s", gb)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildOrderByClause(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("with orders", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
OrderBy: []model.OrderCondition{
|
||||
{FieldCode: "order_amount", Direction: "DESC"},
|
||||
{FieldCode: "shop_id", Direction: "ASC"},
|
||||
},
|
||||
}
|
||||
ob, err := b.buildOrderByClause(req, testFields)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, ob, "order_amount DESC")
|
||||
assertContains(t, ob, "shop_id ASC")
|
||||
})
|
||||
|
||||
t.Run("no orders", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{}
|
||||
ob, err := b.buildOrderByClause(req, testFields)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if ob != "" {
|
||||
t.Fatalf("expected empty, got %s", ob)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid field", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
OrderBy: []model.OrderCondition{
|
||||
{FieldCode: "nonexistent", Direction: "ASC"},
|
||||
},
|
||||
}
|
||||
_, err := b.buildOrderByClause(req, testFields)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nonexistent field")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildTimeGroupExpr(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
tests := []struct {
|
||||
group string
|
||||
wantSub string
|
||||
}{
|
||||
{"year", "YYYY"},
|
||||
{"week", "DATE_TRUNC"},
|
||||
{"month", "YYYY-MM"},
|
||||
{"quarter", "Q"},
|
||||
{"day", "stat_date"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.group, func(t *testing.T) {
|
||||
got := b.buildTimeGroupExpr("stat_date", tt.group)
|
||||
assertContains(t, got, tt.wantSub)
|
||||
assertContains(t, got, "time_group")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddLimit(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("normal pagination", func(t *testing.T) {
|
||||
sql := b.AddLimit("SELECT * FROM t", 2, 10)
|
||||
assertContains(t, sql, "LIMIT 10")
|
||||
assertContains(t, sql, "OFFSET 10")
|
||||
})
|
||||
|
||||
t.Run("page 1", func(t *testing.T) {
|
||||
sql := b.AddLimit("SELECT * FROM t", 1, 20)
|
||||
assertContains(t, sql, "OFFSET 0")
|
||||
})
|
||||
|
||||
t.Run("capped page size", func(t *testing.T) {
|
||||
sql := b.AddLimit("SELECT * FROM t", 1, 5000)
|
||||
assertContains(t, sql, "LIMIT 1000")
|
||||
})
|
||||
}
|
||||
|
||||
func TestBuildWhereClause(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("time range only", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
BusinessCode: "TEST_BIZ",
|
||||
TimeRange: &model.TimeRange{
|
||||
StartDate: "2026-06-01",
|
||||
EndDate: "2026-06-16",
|
||||
},
|
||||
}
|
||||
where, args, err := b.buildWhereClause(context.Background(), req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, where, "tenant_id")
|
||||
assertContains(t, where, "stat_date")
|
||||
assertContains(t, where, "business_code = ?")
|
||||
if len(args) < 2 {
|
||||
t.Fatalf("expected at least 2 args, got %d", len(args))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("with filters", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
BusinessCode: "TEST_BIZ",
|
||||
Filters: []model.FilterCondition{
|
||||
{FieldCode: "order_status", Operator: "=", Value: 1},
|
||||
{FieldCode: "shop_name", Operator: "LIKE", Value: "旗舰"},
|
||||
},
|
||||
}
|
||||
where, args, err := b.buildWhereClause(context.Background(), req, testFields, testReport)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
assertContains(t, where, "order_status = ?")
|
||||
assertContains(t, where, "shop_name LIKE ?")
|
||||
if len(args) < 3 {
|
||||
t.Fatalf("expected at least 3 args, got %d", len(args))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid filter field", func(t *testing.T) {
|
||||
req := &model.UserSelectQueryReq{
|
||||
BusinessCode: "TEST_BIZ",
|
||||
Filters: []model.FilterCondition{
|
||||
{FieldCode: "nonexistent", Operator: "=", Value: "x"},
|
||||
},
|
||||
}
|
||||
_, _, err := b.buildWhereClause(context.Background(), req, testFields, testReport)
|
||||
if err == nil {
|
||||
t.Fatal("expected error for nonexistent filter field")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestParseExpression(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
expr := "{refund_amount} / NULLIF({order_amount}, 0) * 100"
|
||||
indicators := []model.IndicatorSelect{
|
||||
{FieldCode: "refund_amount"},
|
||||
{FieldCode: "order_amount"},
|
||||
}
|
||||
|
||||
result := b.parseExpression(expr, indicators)
|
||||
assertContains(t, result, "refund_amount")
|
||||
assertContains(t, result, "order_amount")
|
||||
assertContains(t, result, "NULLIF")
|
||||
}
|
||||
|
||||
// Regression: ISSUE-002 — calculated expression referencing non-indicator field
|
||||
// {fieldCode} placeholder should be replaced with bare field name, not left in SQL
|
||||
// Found by /qa on 2026-06-16
|
||||
func TestParseExpressionRegressionNonIndicatorRef(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
expr := "{refund_amount} / NULLIF({external_field}, 0) * 100"
|
||||
indicators := []model.IndicatorSelect{
|
||||
{FieldCode: "refund_amount"},
|
||||
}
|
||||
result := b.parseExpression(expr, indicators)
|
||||
// external_field not in indicators, but should still be replaced with bare field name
|
||||
if !contains(result, "external_field") {
|
||||
t.Fatalf("expected external_field in expression, got: %s", result)
|
||||
}
|
||||
if contains(result, "{") || contains(result, "}") {
|
||||
t.Fatalf("expression should not contain unreplaced brace placeholders: %s", result)
|
||||
}
|
||||
if !contains(result, "refund_amount") {
|
||||
t.Fatalf("expected refund_amount in expression, got: %s", result)
|
||||
}
|
||||
if !contains(result, "NULLIF") {
|
||||
t.Fatalf("expected NULLIF in expression, got: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConvertToSlice(t *testing.T) {
|
||||
b := &SQLBuilder{}
|
||||
|
||||
t.Run("from []interface{}", func(t *testing.T) {
|
||||
result, err := b.convertToSlice([]interface{}{1, 2, 3})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(result) != 3 {
|
||||
t.Fatalf("expected 3 elements, got %d", len(result))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("from comma string", func(t *testing.T) {
|
||||
result, err := b.convertToSlice("a,b,c")
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if len(result) != 3 {
|
||||
t.Fatalf("expected 3 elements, got %d", len(result))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// assertContains helper
|
||||
func assertContains(t *testing.T, s, substr string) {
|
||||
t.Helper()
|
||||
if s == "" {
|
||||
t.Fatalf("want substr %q in empty string", substr)
|
||||
}
|
||||
if !contains(s, substr) {
|
||||
t.Fatalf("want %q to contain %q", s, substr)
|
||||
}
|
||||
}
|
||||
|
||||
func contains(s, substr string) bool {
|
||||
return len(s) >= len(substr) && searchString(s, substr)
|
||||
}
|
||||
|
||||
func searchString(s, substr string) bool {
|
||||
for i := 0; i <= len(s)-len(substr); i++ {
|
||||
if s[i:i+len(substr)] == substr {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -0,0 +1,770 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
"dataengine/common/report/model"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// ConfigLoader 配置加载器
|
||||
type ConfigLoader struct {
|
||||
mu sync.RWMutex
|
||||
// 缓存
|
||||
businessCache map[string]*model.BusinessConfig
|
||||
reportCache map[string]*model.ReportConfig
|
||||
fieldCache map[string][]model.FieldConfig
|
||||
extractCache map[string][]model.ExtractConfig
|
||||
}
|
||||
|
||||
var (
|
||||
defaultLoader *ConfigLoader
|
||||
once sync.Once
|
||||
)
|
||||
|
||||
// GetLoader 获取配置加载器单例
|
||||
func GetLoader() *ConfigLoader {
|
||||
once.Do(func() {
|
||||
defaultLoader = &ConfigLoader{
|
||||
businessCache: make(map[string]*model.BusinessConfig),
|
||||
reportCache: make(map[string]*model.ReportConfig),
|
||||
fieldCache: make(map[string][]model.FieldConfig),
|
||||
extractCache: make(map[string][]model.ExtractConfig),
|
||||
}
|
||||
})
|
||||
return defaultLoader
|
||||
}
|
||||
|
||||
// GetBusiness 获取业务配置
|
||||
func (l *ConfigLoader) GetBusiness(ctx context.Context, businessCode string) (*model.BusinessConfig, error) {
|
||||
l.mu.RLock()
|
||||
if biz, ok := l.businessCache[businessCode]; ok {
|
||||
l.mu.RUnlock()
|
||||
return biz, nil
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
|
||||
var biz model.BusinessConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_business_config WHERE business_code = $1 AND deleted_at IS NULL LIMIT 1",
|
||||
businessCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询业务配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("业务配置不存在: %s", businessCode)
|
||||
}
|
||||
if err = r[0].Struct(&biz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if biz.Config == nil {
|
||||
biz.Config = make(map[string]interface{})
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
l.businessCache[businessCode] = &biz
|
||||
l.mu.Unlock()
|
||||
|
||||
return &biz, nil
|
||||
}
|
||||
|
||||
// GetReport 获取报表配置
|
||||
func (l *ConfigLoader) GetReport(ctx context.Context, businessCode, reportCode string) (*model.ReportConfig, error) {
|
||||
key := businessCode + ":" + reportCode
|
||||
l.mu.RLock()
|
||||
if rpt, ok := l.reportCache[key]; ok {
|
||||
l.mu.RUnlock()
|
||||
return rpt, nil
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
|
||||
var rpt model.ReportConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_report_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL LIMIT 1",
|
||||
businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询报表配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("报表配置不存在: %s/%s", businessCode, reportCode)
|
||||
}
|
||||
if err = r[0].Struct(&rpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rpt.PrimaryKeys == nil {
|
||||
rpt.PrimaryKeys = []string{"id"}
|
||||
}
|
||||
if rpt.ConflictKeys == nil {
|
||||
rpt.ConflictKeys = []string{"stat_date"}
|
||||
}
|
||||
if rpt.Config == nil {
|
||||
rpt.Config = make(map[string]interface{})
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
l.reportCache[key] = &rpt
|
||||
l.mu.Unlock()
|
||||
|
||||
return &rpt, nil
|
||||
}
|
||||
|
||||
// GetFields 获取报表字段配置
|
||||
func (l *ConfigLoader) GetFields(ctx context.Context, businessCode, reportCode string) ([]model.FieldConfig, error) {
|
||||
key := businessCode + ":" + reportCode
|
||||
l.mu.RLock()
|
||||
if fields, ok := l.fieldCache[key]; ok {
|
||||
l.mu.RUnlock()
|
||||
return fields, nil
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY sort_order ASC",
|
||||
businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var fields []model.FieldConfig
|
||||
for _, record := range r {
|
||||
var f model.FieldConfig
|
||||
if err := record.Struct(&f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f.ValidAggregates == nil {
|
||||
f.ValidAggregates = []string{}
|
||||
}
|
||||
if f.FilterOperators == nil {
|
||||
f.FilterOperators = []string{"=", "!=", ">", "<", ">=", "<=", "IN", "LIKE", "BETWEEN"}
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
l.fieldCache[key] = fields
|
||||
l.mu.Unlock()
|
||||
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// GetAllFields 获取报表全部字段(含 INACTIVE,不含已删除,用于编辑回显)
|
||||
func (l *ConfigLoader) GetAllFields(ctx context.Context, businessCode, reportCode string) ([]model.FieldConfig, error) {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY sort_order ASC",
|
||||
businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var fields []model.FieldConfig
|
||||
for _, record := range r {
|
||||
var f model.FieldConfig
|
||||
if err := record.Struct(&f); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if f.ValidAggregates == nil {
|
||||
f.ValidAggregates = []string{}
|
||||
}
|
||||
if f.FilterOperators == nil {
|
||||
f.FilterOperators = []string{"=", "!=", ">", "<", ">=", "<=", "IN", "LIKE", "BETWEEN"}
|
||||
}
|
||||
fields = append(fields, f)
|
||||
}
|
||||
return fields, nil
|
||||
}
|
||||
|
||||
// GetFieldMap 获取字段配置Map
|
||||
func (l *ConfigLoader) GetFieldMap(ctx context.Context, businessCode, reportCode string) (map[string]*model.FieldConfig, error) {
|
||||
fields, err := l.GetFields(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fieldMap := make(map[string]*model.FieldConfig)
|
||||
for i := range fields {
|
||||
fieldMap[fields[i].FieldCode] = &fields[i]
|
||||
}
|
||||
return fieldMap, nil
|
||||
}
|
||||
|
||||
// GetExtractConfigs 获取抽取配置
|
||||
func (l *ConfigLoader) GetExtractConfigs(ctx context.Context, businessCode, reportCode string) ([]model.ExtractConfig, error) {
|
||||
key := businessCode + ":" + reportCode
|
||||
l.mu.RLock()
|
||||
if configs, ok := l.extractCache[key]; ok {
|
||||
l.mu.RUnlock()
|
||||
return configs, nil
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY id ASC",
|
||||
businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var configs []model.ExtractConfig
|
||||
for _, record := range r {
|
||||
var ec model.ExtractConfig
|
||||
if err := record.Struct(&ec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ec.JoinConfigs == nil {
|
||||
ec.JoinConfigs = []model.JoinConfig{}
|
||||
}
|
||||
if ec.FieldMappings == nil {
|
||||
ec.FieldMappings = []model.FieldMapping{}
|
||||
}
|
||||
if ec.TransformRules == nil {
|
||||
ec.TransformRules = []model.TransformRule{}
|
||||
}
|
||||
if ec.GroupByFields == nil {
|
||||
ec.GroupByFields = []string{}
|
||||
}
|
||||
if ec.ExtractMode == "" {
|
||||
ec.ExtractMode = model.ExtractModeDirect
|
||||
}
|
||||
configs = append(configs, ec)
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
l.extractCache[key] = configs
|
||||
l.mu.Unlock()
|
||||
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
// GetExtractLog 获取抽取记录
|
||||
func (l *ConfigLoader) GetExtractLog(ctx context.Context, businessCode, reportCode, extractCode, statDate string) (*model.ExtractLog, error) {
|
||||
var log model.ExtractLog
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_extract_log WHERE business_code = $1 AND report_code = $2 AND extract_code = $3 AND stat_date = $4 LIMIT 1",
|
||||
businessCode, reportCode, extractCode, statDate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, nil
|
||||
}
|
||||
if err = r[0].Struct(&log); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &log, nil
|
||||
}
|
||||
|
||||
// CreateExtractLog 创建抽取记录
|
||||
func (l *ConfigLoader) CreateExtractLog(ctx context.Context, log *model.ExtractLog) error {
|
||||
data, _ := json.Marshal(log)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_extract_log").Data(m).Save()
|
||||
return err
|
||||
}
|
||||
|
||||
// UpdateExtractLog 更新抽取记录
|
||||
func (l *ConfigLoader) UpdateExtractLog(ctx context.Context, log *model.ExtractLog) error {
|
||||
data, _ := json.Marshal(log)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_extract_log").
|
||||
Where("business_code", log.BusinessCode).
|
||||
Where("report_code", log.ReportCode).
|
||||
Where("extract_code", log.ExtractCode).
|
||||
Where("stat_date", log.StatDate).
|
||||
Data(m).
|
||||
Update()
|
||||
return err
|
||||
}
|
||||
|
||||
// InvalidateCache 失效缓存
|
||||
func (l *ConfigLoader) InvalidateCache(businessCode, reportCode string) {
|
||||
l.mu.Lock()
|
||||
delete(l.businessCache, businessCode)
|
||||
delete(l.reportCache, businessCode+":"+reportCode)
|
||||
delete(l.fieldCache, businessCode+":"+reportCode)
|
||||
delete(l.extractCache, businessCode+":"+reportCode)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// InvalidateBusinessCache 只失效业务缓存(不影响报表/字段)
|
||||
func (l *ConfigLoader) InvalidateBusinessCache(businessCode string) {
|
||||
l.mu.Lock()
|
||||
delete(l.businessCache, businessCode)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CRUD: BusinessConfig
|
||||
// ============================================================
|
||||
|
||||
// CreateBusiness 创建业务配置
|
||||
func (l *ConfigLoader) CreateBusiness(ctx context.Context, biz *model.BusinessConfig) (int64, error) {
|
||||
data, _ := json.Marshal(biz)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "updated_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
result, err := gfdb.DB(ctx).Model(ctx, "report_business_config").Data(m).Insert()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建业务配置失败: %w", err)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
l.InvalidateBusinessCache(biz.BusinessCode)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateBusiness 更新业务配置
|
||||
func (l *ConfigLoader) UpdateBusiness(ctx context.Context, biz *model.BusinessConfig) error {
|
||||
data, _ := json.Marshal(biz)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_business_config").
|
||||
Where("id", biz.Id).
|
||||
Data(m).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新业务配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateBusinessCache(biz.BusinessCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteBusiness 删除业务配置(软删除)
|
||||
func (l *ConfigLoader) DeleteBusiness(ctx context.Context, id int64, businessCode string) error {
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_business_config").
|
||||
Where("id", id).
|
||||
Data(map[string]interface{}{
|
||||
"status": model.StatusInactive,
|
||||
"deleted_at": "NOW()",
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除业务配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateBusinessCache(businessCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetBusinessByID 根据ID获取业务配置
|
||||
func (l *ConfigLoader) GetBusinessByID(ctx context.Context, id int64) (*model.BusinessConfig, error) {
|
||||
var biz model.BusinessConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_business_config WHERE id = $1 LIMIT 1", id)
|
||||
if err != nil {
|
||||
g.Log().Infof(ctx, "[GetBusinessByID] id=%d, err=%v", id, err)
|
||||
return nil, fmt.Errorf("查询业务配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("业务配置不存在: id=%d", id)
|
||||
}
|
||||
if err = r[0].Struct(&biz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.Log().Infof(ctx, "[GetBusinessByID] id=%d, biz.Id=%d, biz.BusinessCode=%s",
|
||||
id, biz.Id, biz.BusinessCode)
|
||||
return &biz, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CRUD: ReportConfig
|
||||
// ============================================================
|
||||
|
||||
// CreateReport 创建报表配置
|
||||
func (l *ConfigLoader) CreateReport(ctx context.Context, rpt *model.ReportConfig) (int64, error) {
|
||||
data, _ := json.Marshal(rpt)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "updated_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
result, err := gfdb.DB(ctx).Model(ctx, "report_report_config").Data(m).Insert()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建报表配置失败: %w", err)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
l.InvalidateCache(rpt.BusinessCode, rpt.ReportCode)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateReport 更新报表配置
|
||||
func (l *ConfigLoader) UpdateReport(ctx context.Context, rpt *model.ReportConfig) error {
|
||||
data, _ := json.Marshal(rpt)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_report_config").
|
||||
Where("id", rpt.Id).
|
||||
Data(m).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新报表配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(rpt.BusinessCode, rpt.ReportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteReport 删除报表配置(软删除)
|
||||
func (l *ConfigLoader) DeleteReport(ctx context.Context, id int64, businessCode, reportCode string) error {
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_report_config").
|
||||
Where("id", id).
|
||||
Data(map[string]interface{}{
|
||||
"status": model.StatusInactive,
|
||||
"deleted_at": "NOW()",
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除报表配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(businessCode, reportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetReportByID 根据ID获取报表配置
|
||||
func (l *ConfigLoader) GetReportByID(ctx context.Context, id int64) (*model.ReportConfig, error) {
|
||||
var rpt model.ReportConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_report_config WHERE id = $1 LIMIT 1", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询报表配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("报表配置不存在: id=%d", id)
|
||||
}
|
||||
if err = r[0].Struct(&rpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &rpt, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CRUD: FieldConfig
|
||||
// ============================================================
|
||||
|
||||
// CreateField 创建字段配置
|
||||
func (l *ConfigLoader) CreateField(ctx context.Context, field *model.FieldConfig) (int64, error) {
|
||||
data, _ := json.Marshal(field)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "updated_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
result, err := gfdb.DB(ctx).Model(ctx, "report_field_config").Data(m).Insert()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建字段配置失败: %w", err)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
l.InvalidateCache(field.BusinessCode, field.ReportCode)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateField 更新字段配置
|
||||
func (l *ConfigLoader) UpdateField(ctx context.Context, field *model.FieldConfig) error {
|
||||
data, _ := json.Marshal(field)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_field_config").
|
||||
Where("id", field.Id).
|
||||
Data(m).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新字段配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(field.BusinessCode, field.ReportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteField 删除字段配置(软删除)
|
||||
func (l *ConfigLoader) DeleteField(ctx context.Context, id int64, businessCode, reportCode string) error {
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_field_config").
|
||||
Where("id", id).
|
||||
Data(map[string]interface{}{
|
||||
"status": model.StatusInactive,
|
||||
"deleted_at": "NOW()",
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除字段配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(businessCode, reportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetFieldByID 根据ID获取字段配置
|
||||
func (l *ConfigLoader) GetFieldByID(ctx context.Context, id int64) (*model.FieldConfig, error) {
|
||||
var field model.FieldConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_field_config WHERE id = $1 LIMIT 1", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询字段配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("字段配置不存在: id=%d", id)
|
||||
}
|
||||
if err = r[0].Struct(&field); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &field, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// CRUD: ExtractConfig
|
||||
// ============================================================
|
||||
|
||||
// CreateExtractConfig 创建抽取配置
|
||||
func (l *ConfigLoader) CreateExtractConfig(ctx context.Context, ec *model.ExtractConfig) (int64, error) {
|
||||
data, _ := json.Marshal(ec)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "updated_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
result, err := gfdb.DB(ctx).Model(ctx, "report_extract_config").Data(m).Insert()
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("创建抽取配置失败: %w", err)
|
||||
}
|
||||
id, _ := result.LastInsertId()
|
||||
l.InvalidateCache(ec.BusinessCode, ec.ReportCode)
|
||||
return id, nil
|
||||
}
|
||||
|
||||
// UpdateExtractConfig 更新抽取配置
|
||||
func (l *ConfigLoader) UpdateExtractConfig(ctx context.Context, ec *model.ExtractConfig) error {
|
||||
data, _ := json.Marshal(ec)
|
||||
var m map[string]interface{}
|
||||
json.Unmarshal(data, &m)
|
||||
delete(m, "id")
|
||||
delete(m, "created_at")
|
||||
delete(m, "deleted_at")
|
||||
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_extract_config").
|
||||
Where("id", ec.Id).
|
||||
Data(m).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("更新抽取配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(ec.BusinessCode, ec.ReportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteExtractConfig 删除抽取配置(软删除)
|
||||
func (l *ConfigLoader) DeleteExtractConfig(ctx context.Context, id int64, businessCode, reportCode string) error {
|
||||
_, err := gfdb.DB(ctx).Model(ctx, "report_extract_config").
|
||||
Where("id", id).
|
||||
Data(map[string]interface{}{
|
||||
"status": model.StatusInactive,
|
||||
"deleted_at": "NOW()",
|
||||
}).
|
||||
Update()
|
||||
if err != nil {
|
||||
return fmt.Errorf("删除抽取配置失败: %w", err)
|
||||
}
|
||||
l.InvalidateCache(businessCode, reportCode)
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetExtractConfigByID 根据ID获取抽取配置
|
||||
func (l *ConfigLoader) GetExtractConfigByID(ctx context.Context, id int64) (*model.ExtractConfig, error) {
|
||||
var ec model.ExtractConfig
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_extract_config WHERE id = $1 LIMIT 1", id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("查询抽取配置失败: %w", err)
|
||||
}
|
||||
if r.IsEmpty() {
|
||||
return nil, fmt.Errorf("抽取配置不存在: id=%d", id)
|
||||
}
|
||||
if err = r[0].Struct(&ec); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ec, nil
|
||||
}
|
||||
|
||||
// GetAllBusinesses 获取所有业务配置
|
||||
func (l *ConfigLoader) GetAllBusinesses(ctx context.Context) ([]model.BusinessConfig, error) {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_business_config WHERE deleted_at IS NULL ORDER BY id ASC",
|
||||
)
|
||||
// (removed status filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var businesses []model.BusinessConfig
|
||||
for _, record := range r {
|
||||
var biz model.BusinessConfig
|
||||
if err := record.Struct(&biz); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
businesses = append(businesses, biz)
|
||||
}
|
||||
return businesses, nil
|
||||
}
|
||||
|
||||
// GetAllReports 获取所有报表配置
|
||||
func (l *ConfigLoader) GetAllReports(ctx context.Context, businessCode string) ([]model.ReportConfig, error) {
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL ORDER BY id ASC",
|
||||
businessCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var reports []model.ReportConfig
|
||||
for _, record := range r {
|
||||
var rpt model.ReportConfig
|
||||
if err := record.Struct(&rpt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reports = append(reports, rpt)
|
||||
}
|
||||
return reports, nil
|
||||
}
|
||||
|
||||
// ListBusinesses 分页获取业务列表
|
||||
func (l *ConfigLoader) ListBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {
|
||||
total, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT COUNT(*) AS cnt FROM report_business_config WHERE deleted_at IS NULL")
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
count := 0
|
||||
if !total.IsEmpty() {
|
||||
count = total[0]["cnt"].Int()
|
||||
}
|
||||
offset := (pageNum - 1) * pageSize
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_business_config WHERE deleted_at IS NULL ORDER BY id ASC LIMIT $1 OFFSET $2",
|
||||
pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var businesses []model.BusinessConfig
|
||||
for _, record := range r {
|
||||
var biz model.BusinessConfig
|
||||
if err := record.Struct(&biz); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
businesses = append(businesses, biz)
|
||||
}
|
||||
return businesses, count, nil
|
||||
}
|
||||
|
||||
// ListReports 分页获取报表列表
|
||||
func (l *ConfigLoader) ListReports(ctx context.Context, businessCode, reportName string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {
|
||||
namePattern := "%" + reportName + "%"
|
||||
total, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT COUNT(*) AS cnt FROM report_report_config WHERE business_code = $1 AND report_name ILIKE $2 AND deleted_at IS NULL",
|
||||
businessCode, namePattern)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
count := 0
|
||||
if !total.IsEmpty() {
|
||||
count = total[0]["cnt"].Int()
|
||||
}
|
||||
offset := (pageNum - 1) * pageSize
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_report_config WHERE business_code = $1 AND report_name ILIKE $2 AND deleted_at IS NULL ORDER BY id ASC LIMIT $3 OFFSET $4",
|
||||
businessCode, namePattern, pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var reports []model.ReportConfig
|
||||
for _, record := range r {
|
||||
var rpt model.ReportConfig
|
||||
if err := record.Struct(&rpt); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
reports = append(reports, rpt)
|
||||
}
|
||||
return reports, count, nil
|
||||
}
|
||||
|
||||
// ListExtractConfigs 分页获取抽取配置列表
|
||||
func (l *ConfigLoader) ListExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {
|
||||
total, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT COUNT(*) AS cnt FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL",
|
||||
businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
count := 0
|
||||
if !total.IsEmpty() {
|
||||
count = total[0]["cnt"].Int()
|
||||
}
|
||||
offset := (pageNum - 1) * pageSize
|
||||
r, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY id ASC LIMIT $3 OFFSET $4",
|
||||
businessCode, reportCode, pageSize, offset)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
var configs []model.ExtractConfig
|
||||
for _, record := range r {
|
||||
var ec model.ExtractConfig
|
||||
if err := record.Struct(&ec); err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
configs = append(configs, ec)
|
||||
}
|
||||
l.mu.Lock()
|
||||
l.extractCache[businessCode+":"+reportCode] = configs
|
||||
l.mu.Unlock()
|
||||
return configs, count, nil
|
||||
}
|
||||
|
||||
// GetReportFields 获取报表可用字段(按角色分类)
|
||||
func (l *ConfigLoader) GetReportFields(ctx context.Context, businessCode, reportCode string) (*model.GetReportFieldsResp, error) {
|
||||
fields, err := l.GetFields(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &model.GetReportFieldsResp{
|
||||
BusinessCode: businessCode,
|
||||
ReportCode: reportCode,
|
||||
Dimensions: []model.FieldConfig{},
|
||||
Indicators: []model.FieldConfig{},
|
||||
Filters: []model.FieldConfig{},
|
||||
}
|
||||
|
||||
for _, f := range fields {
|
||||
switch f.FieldRole {
|
||||
case model.RoleDimension:
|
||||
resp.Dimensions = append(resp.Dimensions, f)
|
||||
case model.RoleIndicator:
|
||||
resp.Indicators = append(resp.Indicators, f)
|
||||
case model.RoleFilter, model.RoleFilterOnly:
|
||||
resp.Filters = append(resp.Filters, f)
|
||||
}
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/os/gcron"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ExecDailyExtract 执行一次全量每日抽取(遍历所有启用业务+报表)
|
||||
// 提取出独立函数,供定时任务和 HTTP 端点共同使用
|
||||
func ExecDailyExtract(ctx context.Context) {
|
||||
svc := GetService()
|
||||
logrus.Info("[报表引擎] 开始全量每日抽取任务")
|
||||
|
||||
businesses, err := svc.GetAllBusinesses(ctx)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取业务列表失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02")
|
||||
|
||||
for _, biz := range businesses {
|
||||
reports, err := svc.GetAllReports(ctx, biz.BusinessCode)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取报表列表失败 [%s]: %v", biz.BusinessCode, err)
|
||||
continue
|
||||
}
|
||||
|
||||
for _, rpt := range reports {
|
||||
logrus.Infof("[报表引擎] 开始抽取 %s/%s 日期 %s", biz.BusinessCode, rpt.ReportCode, yesterday)
|
||||
|
||||
resp, err := svc.ExtractDailyData(ctx, biz.BusinessCode, rpt.ReportCode, yesterday, "system")
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 抽取失败 %s/%s %s: %v", biz.BusinessCode, rpt.ReportCode, yesterday, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if resp.Success {
|
||||
logrus.Infof("[报表引擎] 抽取成功 %s/%s %s 总数:%d 耗时:%dms",
|
||||
biz.BusinessCode, rpt.ReportCode, yesterday, resp.TotalCount, resp.ExecTimeMs)
|
||||
} else {
|
||||
logrus.Errorf("[报表引擎] 抽取异常 %s/%s %s: %s", biz.BusinessCode, rpt.ReportCode, yesterday, resp.ErrorMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logrus.Info("[报表引擎] 全量每日抽取任务完成")
|
||||
}
|
||||
|
||||
// StartDailyExtractJob 启动每日自动抽取定时任务
|
||||
// 每天凌晨 2:00 遍历所有已启用的业务+报表,抽取前一天数据
|
||||
// 在 main.go 中调用:report.StartDailyExtractJob()
|
||||
func StartDailyExtractJob() {
|
||||
// 每天凌晨 2:00 执行
|
||||
_, err := gcron.Add(gctx.New(), "0 0 2 * * *", func(ctx context.Context) {
|
||||
ExecDailyExtract(ctx)
|
||||
}, "daily-report-extract")
|
||||
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 启动定时任务失败: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
logrus.Info("[报表引擎] 每日自动抽取定时任务已启动 (02:00)")
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
package ddlsync
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"dataengine/common/report/config"
|
||||
"dataengine/common/report/model"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// StatTableCreator 统计宽表创建器
|
||||
type StatTableCreator struct {
|
||||
loader *config.ConfigLoader
|
||||
}
|
||||
|
||||
// NewStatTableCreator 创建统计宽表创建器
|
||||
func NewStatTableCreator() *StatTableCreator {
|
||||
return &StatTableCreator{
|
||||
loader: config.GetLoader(),
|
||||
}
|
||||
}
|
||||
|
||||
// AutoCreateStatTable 根据配置自动创建统计宽表
|
||||
func (c *StatTableCreator) AutoCreateStatTable(ctx context.Context, businessCode, reportCode string) (*model.AutoCreateStatTableResp, error) {
|
||||
start := time.Now()
|
||||
logger := logrus.WithFields(logrus.Fields{
|
||||
"businessCode": businessCode,
|
||||
"reportCode": reportCode,
|
||||
})
|
||||
|
||||
// 1. 获取报表配置
|
||||
report, err := c.loader.GetReport(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取报表配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 获取字段配置
|
||||
fields, err := c.loader.GetFields(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取字段配置失败: %w", err)
|
||||
}
|
||||
|
||||
if len(fields) == 0 {
|
||||
return nil, fmt.Errorf("报表字段配置为空,请先配置字段")
|
||||
}
|
||||
|
||||
// 3. 构建建表SQL
|
||||
tableName := report.StatTableName
|
||||
sql := c.buildCreateTableSQL(tableName, report, fields)
|
||||
|
||||
logger.Infof("创建统计宽表: %s", tableName)
|
||||
|
||||
// 4. 执行建表
|
||||
_, err = gfdb.DB(ctx).Exec(ctx, sql)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("建表失败: %w", err)
|
||||
}
|
||||
|
||||
// 5. 创建冲突唯一索引
|
||||
if len(report.ConflictKeys) > 0 {
|
||||
indexName := fmt.Sprintf("uq_%s_conflict", tableName)
|
||||
indexCols := strings.Join(report.ConflictKeys, ", ")
|
||||
indexSQL := fmt.Sprintf("CREATE UNIQUE INDEX IF NOT EXISTS %s ON %s (%s)", indexName, tableName, indexCols)
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, indexSQL); err != nil {
|
||||
logger.Warnf("创建冲突索引失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 添加字段注释
|
||||
for _, f := range fields {
|
||||
if f.FieldName != "" {
|
||||
commentSQL := fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", tableName, f.FieldCode, strings.ReplaceAll(f.FieldName, "'", "''"))
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, commentSQL); err != nil {
|
||||
logger.Warnf("添加字段注释失败 [%s]: %v", f.FieldCode, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
execTime := time.Since(start).Milliseconds()
|
||||
logger.Infof("统计宽表 %s 创建完成,字段数: %d,耗时: %dms", tableName, len(fields), execTime)
|
||||
|
||||
return &model.AutoCreateStatTableResp{
|
||||
Success: true,
|
||||
TableName: tableName,
|
||||
ColumnCount: len(fields),
|
||||
ExecTimeMs: execTime,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// buildCreateTableSQL 构建建表SQL
|
||||
func (c *StatTableCreator) buildCreateTableSQL(tableName string, report *model.ReportConfig, fields []model.FieldConfig) string {
|
||||
var cols []string
|
||||
|
||||
// 基础字段
|
||||
cols = append(cols, "id BIGSERIAL PRIMARY KEY")
|
||||
cols = append(cols, "tenant_id BIGINT NOT NULL DEFAULT 0")
|
||||
cols = append(cols, "business_code VARCHAR(64) NOT NULL DEFAULT ''")
|
||||
cols = append(cols, "creator VARCHAR(64) DEFAULT ''")
|
||||
cols = append(cols, "created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()")
|
||||
cols = append(cols, "updater VARCHAR(64) DEFAULT ''")
|
||||
cols = append(cols, "updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()")
|
||||
cols = append(cols, "deleted_at TIMESTAMP WITH TIME ZONE")
|
||||
|
||||
// 日期维度字段
|
||||
dateField := report.DateField
|
||||
if dateField == "" {
|
||||
dateField = "stat_date"
|
||||
}
|
||||
cols = append(cols, fmt.Sprintf("%s VARCHAR(16) NOT NULL DEFAULT ''", dateField))
|
||||
|
||||
// 业务字段
|
||||
for _, f := range fields {
|
||||
if f.FieldCode == dateField {
|
||||
continue
|
||||
}
|
||||
cols = append(cols, fmt.Sprintf("%s %s", f.FieldCode, model.FieldTypeToPG(f.FieldType)))
|
||||
}
|
||||
|
||||
// 原始数据
|
||||
cols = append(cols, "raw_data JSONB DEFAULT '{}'")
|
||||
|
||||
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n %s\n)", tableName, strings.Join(cols, ",\n "))
|
||||
|
||||
return sql
|
||||
}
|
||||
|
||||
// DropStatTable 删除统计宽表
|
||||
func (c *StatTableCreator) DropStatTable(ctx context.Context, businessCode, reportCode string) error {
|
||||
report, err := c.loader.GetReport(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sql := fmt.Sprintf("DROP TABLE IF EXISTS %s CASCADE", report.StatTableName)
|
||||
_, err = gfdb.DB(ctx).Exec(ctx, sql)
|
||||
return err
|
||||
}
|
||||
|
||||
// TableExists 检查表是否存在
|
||||
func (c *StatTableCreator) TableExists(ctx context.Context, tableName string) (bool, error) {
|
||||
result, err := gfdb.DB(ctx).GetAll(ctx, "SELECT COUNT(*) FROM pg_tables WHERE tablename = $1", strings.ToLower(tableName))
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if len(result) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
count := result[0]["count"].Int()
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
// GetTableColumns 获取表字段列表
|
||||
func (c *StatTableCreator) GetTableColumns(ctx context.Context, tableName string) ([]string, error) {
|
||||
var columns []string
|
||||
sql := `SELECT column_name FROM information_schema.columns WHERE table_name = $1 AND table_schema = 'public' ORDER BY ordinal_position`
|
||||
rows, err := gfdb.DB(ctx).GetAll(ctx, sql, strings.ToLower(tableName))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, row := range rows.List() {
|
||||
if col, ok := row["column_name"].(string); ok {
|
||||
columns = append(columns, col)
|
||||
}
|
||||
}
|
||||
return columns, nil
|
||||
}
|
||||
|
||||
// AlterTableAddColumns 为已存在的表添加新字段
|
||||
func (c *StatTableCreator) AlterTableAddColumns(ctx context.Context, tableName string, newFields []model.FieldConfig) error {
|
||||
existingCols, err := c.GetTableColumns(ctx, tableName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
existingMap := make(map[string]bool)
|
||||
for _, col := range existingCols {
|
||||
existingMap[col] = true
|
||||
}
|
||||
|
||||
for _, f := range newFields {
|
||||
if existingMap[f.FieldCode] {
|
||||
continue
|
||||
}
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN IF NOT EXISTS %s %s", tableName, f.FieldCode, model.FieldTypeToPG(f.FieldType))
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, sql); err != nil {
|
||||
return fmt.Errorf("添加字段 %s 失败: %w", f.FieldCode, err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,666 @@
|
||||
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
|
||||
*/
|
||||
@@ -0,0 +1,199 @@
|
||||
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 {
|
||||
firstRow := result.List()[0]
|
||||
// 遍历第一行所有列,找到第一个可解析为 int64 的值作为总数
|
||||
for _, v := range firstRow {
|
||||
if n, ok := v.(int64); ok {
|
||||
total = n
|
||||
break
|
||||
}
|
||||
}
|
||||
// 如果遍历未找到 int64 类型值,尝试通过 gfdb 的 Int64 方法获取
|
||||
if total == 0 && len(firstRow) > 0 {
|
||||
for k := range firstRow {
|
||||
total = result[0][k].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,
|
||||
"year": 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
|
||||
}
|
||||
@@ -0,0 +1,670 @@
|
||||
package extract
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"dataengine/common/report/config"
|
||||
"dataengine/common/report/model"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DailyExtractor 天级数据抽取器
|
||||
type DailyExtractor struct {
|
||||
loader *config.ConfigLoader
|
||||
}
|
||||
|
||||
// NewDailyExtractor 创建抽取器
|
||||
func NewDailyExtractor() *DailyExtractor {
|
||||
return &DailyExtractor{
|
||||
loader: config.GetLoader(),
|
||||
}
|
||||
}
|
||||
|
||||
// ExtractDailyData 按天抽取数据(业务层定时任务调用)
|
||||
func (e *DailyExtractor) ExtractDailyData(ctx context.Context, businessCode, reportCode, statDate, executor string) (*model.ExtractDailyDataResp, error) {
|
||||
start := time.Now()
|
||||
logger := logrus.WithFields(logrus.Fields{
|
||||
"businessCode": businessCode,
|
||||
"reportCode": reportCode,
|
||||
"statDate": statDate,
|
||||
})
|
||||
|
||||
// 1. 获取报表配置
|
||||
report, err := e.loader.GetReport(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取报表配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 2. 获取抽取配置
|
||||
extractConfigs, err := e.loader.GetExtractConfigs(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取抽取配置失败: %w", err)
|
||||
}
|
||||
|
||||
if len(extractConfigs) == 0 {
|
||||
return nil, fmt.Errorf("没有可用的抽取配置")
|
||||
}
|
||||
|
||||
// 3. 获取字段配置
|
||||
fieldMap, err := e.loader.GetFieldMap(ctx, businessCode, reportCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取字段配置失败: %w", err)
|
||||
}
|
||||
|
||||
// 4. 确保统计宽表存在
|
||||
if err := e.ensureStatTableExists(ctx, report, fieldMap); err != nil {
|
||||
return nil, fmt.Errorf("确保统计宽表存在失败: %w", err)
|
||||
}
|
||||
|
||||
totalCount := 0
|
||||
successCount := 0
|
||||
failCount := 0
|
||||
var lastErr error
|
||||
|
||||
// 5. 遍历每个抽取配置
|
||||
for _, ec := range extractConfigs {
|
||||
// 检查幂等性
|
||||
exLog, err := e.loader.GetExtractLog(ctx, businessCode, reportCode, ec.ExtractCode, statDate)
|
||||
if err != nil {
|
||||
logger.Errorf("获取抽取记录失败: %v", err)
|
||||
}
|
||||
|
||||
if exLog != nil && exLog.Status == model.ExtractStatusSuccess {
|
||||
logger.Infof("抽取配置 %s 日期 %s 已完成,跳过", ec.ExtractCode, statDate)
|
||||
continue
|
||||
}
|
||||
|
||||
// 创建抽取记录
|
||||
extractLog := &model.ExtractLog{
|
||||
BusinessCode: businessCode,
|
||||
ReportCode: reportCode,
|
||||
ExtractCode: ec.ExtractCode,
|
||||
StatDate: statDate,
|
||||
ExtractType: ec.ExtractType,
|
||||
Status: model.ExtractStatusRunning,
|
||||
Executor: executor,
|
||||
StartTime: &start,
|
||||
}
|
||||
if err := e.loader.CreateExtractLog(ctx, extractLog); err != nil {
|
||||
logger.Errorf("创建抽取日志失败: %v", err)
|
||||
}
|
||||
|
||||
// 执行抽取
|
||||
c, s, f, err := e.executeExtract(ctx, &ec, report, fieldMap, statDate)
|
||||
totalCount += c
|
||||
successCount += s
|
||||
failCount += f
|
||||
|
||||
// 更新抽取记录
|
||||
now := time.Now()
|
||||
extractLog.EndTime = &now
|
||||
extractLog.TotalCount = c
|
||||
extractLog.SuccessCount = s
|
||||
extractLog.FailCount = f
|
||||
|
||||
if err != nil {
|
||||
extractLog.Status = model.ExtractStatusFailed
|
||||
extractLog.ErrorMessage = err.Error()
|
||||
lastErr = err
|
||||
logger.Errorf("抽取配置 %s 执行失败: %v", ec.ExtractCode, err)
|
||||
} else {
|
||||
extractLog.Status = model.ExtractStatusSuccess
|
||||
logger.Infof("抽取配置 %s 完成, 总数:%d 成功:%d 失败:%d", ec.ExtractCode, c, s, f)
|
||||
}
|
||||
|
||||
if updateErr := e.loader.UpdateExtractLog(ctx, extractLog); updateErr != nil {
|
||||
logger.Errorf("更新抽取记录失败: %v", updateErr)
|
||||
}
|
||||
}
|
||||
|
||||
execTime := time.Since(start).Milliseconds()
|
||||
logger.Infof("按天抽取完成, 总数:%d 成功:%d 失败:%d 耗时:%dms", totalCount, successCount, failCount, execTime)
|
||||
|
||||
resp := &model.ExtractDailyDataResp{
|
||||
Success: lastErr == nil,
|
||||
TotalCount: totalCount,
|
||||
SuccessCount: successCount,
|
||||
FailCount: failCount,
|
||||
ExecTimeMs: execTime,
|
||||
}
|
||||
|
||||
if lastErr != nil {
|
||||
resp.ErrorMsg = lastErr.Error()
|
||||
}
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// executeExtract 执行单个抽取配置
|
||||
func (e *DailyExtractor) executeExtract(ctx context.Context, ec *model.ExtractConfig, report *model.ReportConfig, fieldMap map[string]*model.FieldConfig, statDate string) (total, success, fail int, err error) {
|
||||
logger := logrus.WithField("extractCode", ec.ExtractCode)
|
||||
|
||||
// 1. 构建抽取SQL
|
||||
extractSQL, whereArgs, err := e.buildExtractSQL(ctx, ec, report, statDate)
|
||||
if err != nil {
|
||||
return 0, 0, 0, fmt.Errorf("构建抽取SQL失败: %w", err)
|
||||
}
|
||||
|
||||
logger.Debugf("抽取SQL: %s", extractSQL)
|
||||
|
||||
// 2. 分批抽取
|
||||
batchSize := ec.BatchSize
|
||||
if batchSize <= 0 {
|
||||
batchSize = 1000
|
||||
}
|
||||
|
||||
offset := 0
|
||||
for {
|
||||
// 添加分页
|
||||
pagedSQL := fmt.Sprintf("%s LIMIT %d OFFSET %d", extractSQL, batchSize, offset)
|
||||
args := whereArgs
|
||||
|
||||
rows, queryErr := gfdb.DB(ctx).GetAll(ctx, pagedSQL, args...)
|
||||
if queryErr != nil {
|
||||
return total, success, fail, fmt.Errorf("抽取查询失败: %w", queryErr)
|
||||
}
|
||||
|
||||
batchCount := rows.Len()
|
||||
if batchCount == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
// 3. 应用转换规则(仅 DIRECT 模式需注入审计字段,AGGREGATE 模式已由SQL处理)
|
||||
dataList := rows.List()
|
||||
if ec.ExtractMode != model.ExtractModeAggregate {
|
||||
for i := range dataList {
|
||||
e.applyTransformRules(ec, dataList[i])
|
||||
dataList[i]["tenant_id"] = 1
|
||||
dataList[i]["business_code"] = ec.BusinessCode
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 写入统计宽表
|
||||
c, _, writeErr := e.batchUpsert(ctx, report.StatTableName, report.ConflictKeys, dataList)
|
||||
if writeErr != nil {
|
||||
logger.Errorf("批量写入失败 (offset=%d): %v", offset, writeErr)
|
||||
fail += batchCount
|
||||
} else {
|
||||
success += c
|
||||
}
|
||||
|
||||
total += batchCount
|
||||
offset += batchSize
|
||||
|
||||
if batchCount < batchSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return total, success, fail, nil
|
||||
}
|
||||
|
||||
// buildExtractSQL 构建抽取SQL
|
||||
func (e *DailyExtractor) buildExtractSQL(ctx context.Context, ec *model.ExtractConfig, report *model.ReportConfig, statDate string) (string, []interface{}, error) {
|
||||
var args []interface{}
|
||||
|
||||
sourceTable := ec.SourceTableName
|
||||
if ec.SourceTableAlias != "" {
|
||||
sourceTable = ec.SourceTableAlias
|
||||
} else {
|
||||
sourceTable = "s"
|
||||
}
|
||||
|
||||
// 日期字段
|
||||
dateField := report.DateField
|
||||
if dateField == "" {
|
||||
dateField = "stat_date"
|
||||
}
|
||||
|
||||
// 判断抽取模式
|
||||
mode := ec.ExtractMode
|
||||
if mode == "" {
|
||||
mode = model.ExtractModeDirect
|
||||
}
|
||||
|
||||
if mode == model.ExtractModeAggregate {
|
||||
return e.buildAggregateExtractSQL(ec, report, sourceTable, dateField, statDate)
|
||||
}
|
||||
|
||||
// === 默认 DIRECT 模式:逐行抽取 ===
|
||||
return e.buildDirectExtractSQL(ec, report, sourceTable, dateField, statDate), args, nil
|
||||
}
|
||||
|
||||
// buildDirectExtractSQL 逐行抽取模式SQL(直接映射,不做聚合)
|
||||
func (e *DailyExtractor) buildDirectExtractSQL(ec *model.ExtractConfig, report *model.ReportConfig, sourceTable, dateField, statDate string) string {
|
||||
var selectParts []string
|
||||
|
||||
// 基础审计字段(常量注入)
|
||||
selectParts = append(selectParts, "0 AS id")
|
||||
selectParts = append(selectParts, "1 AS tenant_id")
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS business_code", ec.BusinessCode))
|
||||
selectParts = append(selectParts, "'system' AS creator")
|
||||
selectParts = append(selectParts, "NOW() AS created_at")
|
||||
selectParts = append(selectParts, "'system' AS updater")
|
||||
selectParts = append(selectParts, "NOW() AS updated_at")
|
||||
selectParts = append(selectParts, "NULL::TIMESTAMP AS deleted_at")
|
||||
|
||||
// 日期字段
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS %s", statDate, dateField))
|
||||
|
||||
// 原始数据
|
||||
selectParts = append(selectParts, "'{}'::JSONB AS raw_data")
|
||||
|
||||
// 字段映射
|
||||
for _, mapping := range ec.FieldMappings {
|
||||
targetField := mapping.TargetField
|
||||
sourceField := mapping.SourceField
|
||||
|
||||
var expr string
|
||||
if mapping.TransformRule != nil {
|
||||
expr = e.applyTransformExpr(mapping.TransformRule, fmt.Sprintf("%s.%s", sourceTable, sourceField))
|
||||
} else {
|
||||
expr = fmt.Sprintf("%s.%s", sourceTable, sourceField)
|
||||
}
|
||||
|
||||
if mapping.DefaultValue != nil {
|
||||
expr = fmt.Sprintf("COALESCE(%s, '%v')", expr, mapping.DefaultValue)
|
||||
}
|
||||
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s AS %s", expr, targetField))
|
||||
}
|
||||
|
||||
// FROM + JOIN
|
||||
fromClause := e.buildFromClause(ec, sourceTable)
|
||||
|
||||
// JOIN 字段映射
|
||||
selectParts = append(selectParts, e.buildJoinFieldSelects(ec)...)
|
||||
|
||||
// WHERE
|
||||
whereClause := e.buildWhereClause(ec, sourceTable, statDate)
|
||||
|
||||
return fmt.Sprintf("SELECT %s FROM %s %s", strings.Join(selectParts, ", "), fromClause, whereClause)
|
||||
}
|
||||
|
||||
// buildAggregateExtractSQL 聚合抽取模式SQL(GROUP BY + SUM/COUNT/AVG)
|
||||
func (e *DailyExtractor) buildAggregateExtractSQL(ec *model.ExtractConfig, report *model.ReportConfig, sourceTable, dateField, statDate string) (string, []interface{}, error) {
|
||||
var selectParts []string
|
||||
var groupByParts []string
|
||||
var args []interface{}
|
||||
|
||||
// 基础审计字段(聚合模式下用常量)
|
||||
selectParts = append(selectParts, "ROW_NUMBER() OVER () AS id") // 伪自增ID
|
||||
selectParts = append(selectParts, "1 AS tenant_id")
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS business_code", ec.BusinessCode))
|
||||
selectParts = append(selectParts, "'system' AS creator")
|
||||
selectParts = append(selectParts, "NOW() AS created_at")
|
||||
selectParts = append(selectParts, "'system' AS updater")
|
||||
selectParts = append(selectParts, "NOW() AS updated_at")
|
||||
selectParts = append(selectParts, "NULL::TIMESTAMP AS deleted_at")
|
||||
|
||||
// 日期字段(常量)
|
||||
selectParts = append(selectParts, fmt.Sprintf("'%s' AS %s", statDate, dateField))
|
||||
|
||||
// 原始数据
|
||||
selectParts = append(selectParts, "'{}'::JSONB AS raw_data")
|
||||
|
||||
// GroupByFields 集合(快速查找)
|
||||
gbySet := make(map[string]bool)
|
||||
for _, gbf := range ec.GroupByFields {
|
||||
gbySet[gbf] = true
|
||||
}
|
||||
|
||||
// 添加 GroupBy 字段到 SELECT 和 GROUP BY
|
||||
for _, gbf := range ec.GroupByFields {
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s.%s", sourceTable, gbf))
|
||||
groupByParts = append(groupByParts, fmt.Sprintf("%s.%s", sourceTable, gbf))
|
||||
}
|
||||
|
||||
// 字段映射:根据 AggregateFunction 决定聚合方式
|
||||
for _, mapping := range ec.FieldMappings {
|
||||
targetField := mapping.TargetField
|
||||
sourceField := mapping.SourceField
|
||||
|
||||
// 构建源表达式
|
||||
var sourceExpr string
|
||||
if mapping.TransformRule != nil {
|
||||
sourceExpr = e.applyTransformExpr(mapping.TransformRule, fmt.Sprintf("%s.%s", sourceTable, sourceField))
|
||||
} else {
|
||||
sourceExpr = fmt.Sprintf("%s.%s", sourceTable, sourceField)
|
||||
}
|
||||
|
||||
// 判断是否需要聚合
|
||||
aggFunc := strings.ToUpper(mapping.AggregateFunction)
|
||||
if aggFunc != "" && !gbySet[sourceField] {
|
||||
// 聚合字段:SUM(s.xxx) / COUNT(s.xxx) / AVG(s.xxx)
|
||||
expr := fmt.Sprintf("%s(%s)", aggFunc, sourceExpr)
|
||||
if mapping.DefaultValue != nil {
|
||||
expr = fmt.Sprintf("COALESCE(%s, %v)", expr, mapping.DefaultValue)
|
||||
}
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s AS %s", expr, targetField))
|
||||
} else if gbySet[sourceField] {
|
||||
// GroupBy 字段不需要重复加入 SELECT(已通过 groupByFields 处理)
|
||||
continue
|
||||
} else {
|
||||
// 非聚合字段,也未在 GroupBy 中 → 用 MAX/MIN 取值(兼容 PG only_full_group_by)
|
||||
expr := fmt.Sprintf("MAX(%s)", sourceExpr)
|
||||
if mapping.DefaultValue != nil {
|
||||
expr = fmt.Sprintf("COALESCE(%s, %v)", expr, mapping.DefaultValue)
|
||||
}
|
||||
selectParts = append(selectParts, fmt.Sprintf("%s AS %s", expr, targetField))
|
||||
}
|
||||
}
|
||||
|
||||
// FROM + JOIN
|
||||
fromClause := e.buildFromClause(ec, sourceTable)
|
||||
|
||||
// WHERE
|
||||
whereClause := e.buildWhereClause(ec, sourceTable, statDate)
|
||||
|
||||
// 组合 SQL
|
||||
sql := fmt.Sprintf("SELECT %s FROM %s %s",
|
||||
strings.Join(selectParts, ", "),
|
||||
fromClause,
|
||||
whereClause)
|
||||
|
||||
// GROUP BY
|
||||
if len(groupByParts) > 0 {
|
||||
sql += " GROUP BY " + strings.Join(groupByParts, ", ")
|
||||
}
|
||||
|
||||
return sql, args, nil
|
||||
}
|
||||
|
||||
// buildFromClause 构建FROM + JOIN子句
|
||||
func (e *DailyExtractor) buildFromClause(ec *model.ExtractConfig, sourceTable string) string {
|
||||
fromClause := fmt.Sprintf("%s %s", ec.SourceTableName, sourceTable)
|
||||
|
||||
for _, join := range ec.JoinConfigs {
|
||||
joinType := "LEFT JOIN"
|
||||
jType := strings.ToUpper(join.JoinType)
|
||||
if jType == "INNER" {
|
||||
joinType = "INNER JOIN"
|
||||
} else if jType == "RIGHT" {
|
||||
joinType = "RIGHT JOIN"
|
||||
}
|
||||
|
||||
joinAlias := join.JoinAlias
|
||||
if joinAlias == "" {
|
||||
joinAlias = join.JoinTable
|
||||
}
|
||||
|
||||
fromClause += fmt.Sprintf(" %s %s %s ON %s", joinType, join.JoinTable, joinAlias, join.JoinCondition)
|
||||
}
|
||||
|
||||
return fromClause
|
||||
}
|
||||
|
||||
// buildJoinFieldSelects 构建JOIN表的字段映射SELECT部分
|
||||
func (e *DailyExtractor) buildJoinFieldSelects(ec *model.ExtractConfig) []string {
|
||||
var parts []string
|
||||
for _, join := range ec.JoinConfigs {
|
||||
joinAlias := join.JoinAlias
|
||||
if joinAlias == "" {
|
||||
joinAlias = join.JoinTable
|
||||
}
|
||||
for _, jm := range join.FieldMappings {
|
||||
targetField := jm.TargetField
|
||||
sourceExpr := fmt.Sprintf("%s.%s", joinAlias, jm.SourceField)
|
||||
if jm.TransformRule != nil {
|
||||
sourceExpr = e.applyTransformExpr(jm.TransformRule, sourceExpr)
|
||||
}
|
||||
parts = append(parts, fmt.Sprintf("%s AS %s", sourceExpr, targetField))
|
||||
}
|
||||
}
|
||||
return parts
|
||||
}
|
||||
|
||||
// buildWhereClause 构建WHERE子句
|
||||
func (e *DailyExtractor) buildWhereClause(ec *model.ExtractConfig, sourceTable, statDate string) string {
|
||||
var whereConditions []string
|
||||
|
||||
// 日期范围(增量抽取)
|
||||
if ec.ExtractType == model.ExtractTypeIncremental && ec.ExtractKeyField != "" {
|
||||
dateCondition := fmt.Sprintf("%s.%s::date = '%s'", sourceTable, ec.ExtractKeyField, statDate)
|
||||
whereConditions = append(whereConditions, dateCondition)
|
||||
}
|
||||
|
||||
// 自定义过滤条件
|
||||
if ec.FilterExpression != "" {
|
||||
whereConditions = append(whereConditions, ec.FilterExpression)
|
||||
}
|
||||
|
||||
if len(whereConditions) == 0 {
|
||||
return ""
|
||||
}
|
||||
return "WHERE " + strings.Join(whereConditions, " AND ")
|
||||
}
|
||||
|
||||
// applyTransformExpr 应用转换表达式
|
||||
func (e *DailyExtractor) applyTransformExpr(rule *model.TransformRule, sourceExpr string) string {
|
||||
switch rule.RuleType {
|
||||
case "CALCULATE":
|
||||
if rule.Expression != "" {
|
||||
return strings.ReplaceAll(rule.Expression, "{source}", sourceExpr)
|
||||
}
|
||||
case "FORMAT":
|
||||
if rule.Format != "" {
|
||||
return fmt.Sprintf("TO_CHAR(%s, '%s')", sourceExpr, rule.Format)
|
||||
}
|
||||
case "MAPPING":
|
||||
// 在代码中运行时做映射
|
||||
return sourceExpr
|
||||
}
|
||||
return sourceExpr
|
||||
}
|
||||
|
||||
// applyTransformRules 应用运行时转换规则(映射等代码转换)
|
||||
func (e *DailyExtractor) applyTransformRules(ec *model.ExtractConfig, row map[string]interface{}) {
|
||||
for _, rule := range ec.TransformRules {
|
||||
if rule.RuleType != "MAPPING" {
|
||||
continue
|
||||
}
|
||||
|
||||
sourceField := rule.Expression // 存储源字段名
|
||||
targetField := rule.RuleCode // 存储目标字段名
|
||||
|
||||
if sourceVal, ok := row[sourceField]; ok {
|
||||
strVal := gconv.String(sourceVal)
|
||||
if mapped, exists := rule.Mapping[strVal]; exists {
|
||||
row[targetField] = mapped
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ensureStatTableExists 确保统计宽表存在,如有新增字段自动加列
|
||||
func (e *DailyExtractor) ensureStatTableExists(ctx context.Context, report *model.ReportConfig, fieldMap map[string]*model.FieldConfig) error {
|
||||
tableName := report.StatTableName
|
||||
|
||||
// 检查表是否存在
|
||||
result, err := gfdb.DB(ctx).GetAll(ctx, "SELECT COUNT(*) FROM pg_tables WHERE tablename = $1", strings.ToLower(tableName))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
count := 0
|
||||
if len(result) > 0 {
|
||||
count = result[0]["count"].Int()
|
||||
}
|
||||
|
||||
if count == 0 {
|
||||
// 需要建表
|
||||
return e.createStatTable(ctx, report, fieldMap)
|
||||
}
|
||||
|
||||
// 表已存在,检查并添加新增字段
|
||||
if err := e.alterTableAddColumns(ctx, tableName, fieldMap); err != nil {
|
||||
logrus.Warnf("自动加列失败 %s: %v", tableName, err)
|
||||
// 不返回 error,加列失败不应阻塞抽取
|
||||
}
|
||||
|
||||
logrus.Infof("统计宽表 %s 已存在", tableName)
|
||||
return nil
|
||||
}
|
||||
|
||||
// alterTableAddColumns 为已存在的统计宽表添加新增字段
|
||||
func (e *DailyExtractor) alterTableAddColumns(ctx context.Context, tableName string, fieldMap map[string]*model.FieldConfig) error {
|
||||
// 获取已有列
|
||||
rows, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
"SELECT column_name FROM information_schema.columns WHERE table_name = $1 AND table_schema = 'public'",
|
||||
strings.ToLower(tableName))
|
||||
if err != nil {
|
||||
return fmt.Errorf("查询表结构失败: %w", err)
|
||||
}
|
||||
|
||||
existingCols := make(map[string]bool)
|
||||
for _, row := range rows.List() {
|
||||
if col, ok := row["column_name"].(string); ok {
|
||||
existingCols[col] = true
|
||||
}
|
||||
}
|
||||
|
||||
for _, fc := range fieldMap {
|
||||
if existingCols[fc.FieldCode] {
|
||||
continue
|
||||
}
|
||||
colType := model.FieldTypeToPG(fc.FieldType)
|
||||
sql := fmt.Sprintf("ALTER TABLE %s ADD COLUMN IF NOT EXISTS %s %s", tableName, fc.FieldCode, colType)
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, sql); err != nil {
|
||||
return fmt.Errorf("添加字段 %s 失败: %w", fc.FieldCode, err)
|
||||
}
|
||||
logrus.Infof("统计宽表 %s 自动添加字段 %s %s", tableName, fc.FieldCode, colType)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// createStatTable 创建统计宽表
|
||||
func (e *DailyExtractor) createStatTable(ctx context.Context, report *model.ReportConfig, fieldMap map[string]*model.FieldConfig) error {
|
||||
var cols []string
|
||||
|
||||
// 标准审计字段
|
||||
cols = append(cols, "id BIGSERIAL PRIMARY KEY")
|
||||
cols = append(cols, "tenant_id BIGINT NOT NULL DEFAULT 0")
|
||||
cols = append(cols, "business_code VARCHAR(64) NOT NULL DEFAULT ''")
|
||||
cols = append(cols, "creator VARCHAR(64) DEFAULT ''")
|
||||
cols = append(cols, "created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()")
|
||||
cols = append(cols, "updater VARCHAR(64) DEFAULT ''")
|
||||
cols = append(cols, "updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()")
|
||||
cols = append(cols, "deleted_at TIMESTAMP WITH TIME ZONE")
|
||||
|
||||
// 日期字段
|
||||
dateField := report.DateField
|
||||
if dateField == "" {
|
||||
dateField = "stat_date"
|
||||
}
|
||||
cols = append(cols, fmt.Sprintf("%s VARCHAR(16) NOT NULL DEFAULT ''", dateField))
|
||||
|
||||
// 业务字段
|
||||
for _, fc := range fieldMap {
|
||||
fc := fc
|
||||
if fc.FieldCode == dateField {
|
||||
continue
|
||||
}
|
||||
colType := model.FieldTypeToPG(fc.FieldType)
|
||||
cols = append(cols, fmt.Sprintf("%s %s", fc.FieldCode, colType))
|
||||
}
|
||||
|
||||
// 原始数据
|
||||
cols = append(cols, "raw_data JSONB DEFAULT '{}'")
|
||||
|
||||
tableName := report.StatTableName
|
||||
sql := fmt.Sprintf("CREATE TABLE IF NOT EXISTS %s (\n %s\n)", tableName, strings.Join(cols, ",\n "))
|
||||
|
||||
logrus.Infof("创建统计宽表: %s", tableName)
|
||||
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, sql); err != nil {
|
||||
return fmt.Errorf("建表失败: %w", err)
|
||||
}
|
||||
|
||||
// 冲突唯一索引
|
||||
if len(report.ConflictKeys) > 0 {
|
||||
indexName := fmt.Sprintf("uq_%s_conflict", tableName)
|
||||
indexCols := strings.Join(report.ConflictKeys, ", ")
|
||||
indexSQL := fmt.Sprintf("CREATE UNIQUE INDEX IF NOT EXISTS %s ON %s (%s)", indexName, tableName, indexCols)
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, indexSQL); err != nil {
|
||||
logrus.Warnf("创建冲突索引失败: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// 字段注释
|
||||
for _, fc := range fieldMap {
|
||||
fc := fc
|
||||
if fc.FieldName != "" {
|
||||
escaped := strings.ReplaceAll(fc.FieldName, "'", "''")
|
||||
commentSQL := fmt.Sprintf("COMMENT ON COLUMN %s.%s IS '%s'", tableName, fc.FieldCode, escaped)
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, commentSQL); err != nil {
|
||||
logrus.Warnf("添加字段注释失败 [%s.%s]: %v", tableName, fc.FieldCode, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// batchUpsert 批量写入统计宽表(使用原始SQL INSERT,避免ORM兼容问题)
|
||||
func (e *DailyExtractor) batchUpsert(ctx context.Context, tableName string, conflictKeys []string, rows []map[string]interface{}) (int, []string, error) {
|
||||
if len(rows) == 0 {
|
||||
return 0, nil, nil
|
||||
}
|
||||
|
||||
// 去除 id 和 created_at 列(数据库自增/默认值)
|
||||
var keys []string
|
||||
for k := range rows[0] {
|
||||
if k == "id" || k == "created_at" || k == "updated_at" || k == "deleted_at" {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
cols := strings.Join(keys, ", ")
|
||||
placeholders := make([]string, len(keys))
|
||||
for i := range keys {
|
||||
placeholders[i] = fmt.Sprintf("$%d", i+1)
|
||||
}
|
||||
phStr := strings.Join(placeholders, ", ")
|
||||
|
||||
sqlStr := fmt.Sprintf("INSERT INTO %s (%s) VALUES (%s)", tableName, cols, phStr)
|
||||
if len(conflictKeys) > 0 {
|
||||
var updateParts []string
|
||||
for _, k := range keys {
|
||||
if k == "id" || k == "created_at" {
|
||||
continue
|
||||
}
|
||||
updateParts = append(updateParts, fmt.Sprintf("%s = EXCLUDED.%s", k, k))
|
||||
}
|
||||
sqlStr += " ON CONFLICT (" + strings.Join(conflictKeys, ", ") + ") DO UPDATE SET " + strings.Join(updateParts, ", ")
|
||||
}
|
||||
|
||||
batchSize := 100
|
||||
total := 0
|
||||
|
||||
for i := 0; i < len(rows); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(rows) {
|
||||
end = len(rows)
|
||||
}
|
||||
batch := rows[i:end]
|
||||
|
||||
for _, row := range batch {
|
||||
var args []interface{}
|
||||
for _, k := range keys {
|
||||
args = append(args, row[k])
|
||||
}
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, sqlStr, args...); err != nil {
|
||||
logrus.Errorf("逐行写入 %s 失败: %v", tableName, err)
|
||||
} else {
|
||||
total++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return total, nil, nil
|
||||
}
|
||||
@@ -0,0 +1,473 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// 实体定义
|
||||
// ============================================================
|
||||
|
||||
// BusinessConfig 业务配置
|
||||
type BusinessConfig struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
BusinessCode string `orm:"business_code" json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
BusinessName string `orm:"business_name" json:"businessName" dc:"业务名称" eg:"快手电商"`
|
||||
Description string `orm:"description" json:"description" dc:"描述" eg:"快手电商店铺每日统计报表"`
|
||||
Status string `orm:"status" json:"status" dc:"status" eg:"ACTIVE"`
|
||||
Config map[string]interface{} `orm:"config" json:"config" dc:"扩展配置"`
|
||||
}
|
||||
|
||||
// ReportConfig 报表配置
|
||||
type ReportConfig struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
BusinessCode string `orm:"business_code" json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `orm:"report_code" json:"reportCode" dc:"报表编码" eg:"shop_daily"`
|
||||
ReportName string `orm:"report_name" json:"reportName" dc:"报表名称" eg:"店铺日报"`
|
||||
Description string `orm:"description" json:"description" dc:"描述" eg:"快手电商店铺每日统计报表"`
|
||||
Status string `orm:"status" json:"status" dc:"状态 ACTIVE/INACTIVE" eg:"ACTIVE"`
|
||||
StatTableName string `orm:"stat_table_name" json:"statTableName" dc:"统计宽表名" eg:"stat_kuaishou_shop_daily"`
|
||||
StatTableComment string `orm:"stat_table_comment" json:"statTableComment" dc:"统计宽表注释" eg:"快手电商-店铺日报"`
|
||||
DateField string `orm:"date_field" json:"dateField" dc:"日期字段名" eg:"stat_date"`
|
||||
PrimaryKeys []string `orm:"primary_keys" json:"primaryKeys" dc:"主键字段列表"`
|
||||
ConflictKeys []string `orm:"conflict_keys" json:"conflictKeys" dc:"冲突键(唯一索引)"`
|
||||
Config map[string]interface{} `orm:"config" json:"config" dc:"扩展配置"`
|
||||
ExtractConfigIDs string `orm:"-" json:"extractConfigIds" dc:"抽取配置ID列表,逗号分隔" eg:"1,2,3"`
|
||||
Fields []FieldConfig `orm:"-" json:"fields,omitempty" dc:"字段配置列表(查询时返回)"`
|
||||
}
|
||||
|
||||
// FieldConfig 字段配置
|
||||
type FieldConfig struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
BusinessCode string `orm:"business_code" json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `orm:"report_code" json:"reportCode" dc:"reportCode" eg:"shop_daily"`
|
||||
FieldCode string `orm:"field_code" json:"fieldCode" dc:"字段编码" eg:"order_amount"`
|
||||
FieldName string `orm:"field_name" json:"fieldName" dc:"字段名称" eg:"订单金额"`
|
||||
FieldType string `orm:"field_type" json:"fieldType" dc:"字段类型 STRING/INT/FLOAT/DATE/DATETIME/JSONB" eg:"FLOAT"`
|
||||
DataType string `orm:"data_type" json:"dataType" dc:"数据存储类型" eg:"STRING"`
|
||||
FieldRole string `orm:"field_role" json:"fieldRole" dc:"字段角色 DIMENSION(维度)/INDICATOR(指标)/FILTER(筛选)/FILTER_ONLY(仅筛选)" eg:"DIMENSION"`
|
||||
IsAggregatable bool `orm:"is_aggregatable" json:"isAggregatable" dc:"是否可聚合" eg:"true"`
|
||||
IsFilterable bool `orm:"is_filterable" json:"isFilterable" dc:"是否可筛选" eg:"true"`
|
||||
IsQueryable bool `orm:"is_queryable" json:"isQueryable" dc:"是否可查询" eg:"true"`
|
||||
IsSortable bool `orm:"is_sortable" json:"isSortable" dc:"是否可排序" eg:"true"`
|
||||
DefaultAggregate string `orm:"default_aggregate" json:"defaultAggregate" dc:"默认聚合方式 SUM/COUNT/AVG/MAX/MIN" eg:"SUM"`
|
||||
ValidAggregates []string `orm:"valid_aggregates" json:"validAggregates" dc:"可选聚合列表"`
|
||||
FilterOperators []string `orm:"filter_operators" json:"filterOperators" dc:"可选操作符列表 =/!=/>/</>=/<=/IN/LIKE/BETWEEN"`
|
||||
Expression string `orm:"expression" json:"expression" dc:"表达式(衍生字段)" eg:"order_amount - refund_amount"`
|
||||
ExpressionType string `orm:"expression_type" json:"expressionType" dc:"表达式类型 DIRECT/CALCULATED" eg:"DIRECT"`
|
||||
FormatPattern string `orm:"format_pattern" json:"formatPattern" dc:"格式化模板" eg:"#,##0.00"`
|
||||
Unit string `orm:"unit" json:"unit" dc:"单位"`
|
||||
DictCode string `orm:"dict_code" json:"dictCode" dc:"字典编码" eg:"dict_order_type"`
|
||||
SortOrder int `orm:"sort_order" json:"sortOrder" dc:"排序号" eg:"1"`
|
||||
GroupName string `orm:"group_name" json:"groupName" dc:"分组名称" eg:"交易指标"`
|
||||
Status string `orm:"status" json:"status" dc:"status" eg:"ACTIVE"`
|
||||
}
|
||||
|
||||
// ExtractConfig 抽取配置
|
||||
type ExtractConfig struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
BusinessCode string `orm:"business_code" json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `orm:"report_code" json:"reportCode" dc:"reportCode" eg:"shop_daily"`
|
||||
ExtractCode string `orm:"extract_code" json:"extractCode" dc:"抽取编码" eg:"kuaishou_order_extract"`
|
||||
ExtractName string `orm:"extract_name" json:"extractName" dc:"抽取名称" eg:"订单抽取"`
|
||||
SourceTableName string `orm:"source_table_name" json:"sourceTableName" dc:"源表名" eg:"ods_kuaishou_order"`
|
||||
SourceTableAlias string `orm:"source_table_alias" json:"sourceTableAlias" dc:"源表别名" eg:"o"`
|
||||
TargetTableName string `orm:"target_table_name" json:"targetTableName" dc:"目标表名" eg:"stat_kuaishou_shop_daily"`
|
||||
IsEnabled bool `orm:"is_enabled" json:"isEnabled" dc:"是否启用" eg:"true"`
|
||||
ExtractType string `orm:"extract_type" json:"extractType" dc:"抽取类型 FULL/INCREMENTAL" eg:"INCREMENTAL"`
|
||||
ExtractMode string `orm:"extract_mode" json:"extractMode" dc:"抽取模式 DIRECT(逐行)/AGGREGATE(聚合)" eg:"DIRECT"`
|
||||
ExtractKeyField string `orm:"extract_key_field" json:"extractKeyField" dc:"抽取关键字段(增量依据)" eg:"updated_at"`
|
||||
ExtractKeyFormat string `orm:"extract_key_format" json:"extractKeyFormat" dc:"关键字段格式" eg:"yyyy-MM-dd HH:mm:ss"`
|
||||
GroupByFields []string `orm:"group_by_fields" json:"groupByFields" dc:"GROUP BY 字段列表"`
|
||||
FilterExpression string `orm:"filter_expression" json:"filterExpression" dc:"过滤表达式" eg:"status = 'PAID'"`
|
||||
JoinConfigs []JoinConfig `orm:"join_configs" json:"joinConfigs" dc:"JOIN配置"`
|
||||
FieldMappings []FieldMapping `orm:"field_mappings" json:"fieldMappings" dc:"字段映射列表"`
|
||||
TransformRules []TransformRule `orm:"transform_rules" json:"transformRules" dc:"转换规则列表"`
|
||||
BatchSize int `orm:"batch_size" json:"batchSize" dc:"批处理大小" eg:"1000"`
|
||||
Status string `orm:"status" json:"status" dc:"status" eg:"ACTIVE"`
|
||||
}
|
||||
|
||||
// ExtractLog 抽取记录(不含 Creator/Updater/DeletedAt,表结构不同)
|
||||
type ExtractLog struct {
|
||||
ID int64 `orm:"id" json:"id" dc:"主键ID" eg:"1"`
|
||||
TenantId uint64 `orm:"tenant_id" json:"tenantId" dc:"租户ID" eg:"1"`
|
||||
BusinessCode string `orm:"business_code" json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `orm:"report_code" json:"reportCode" dc:"reportCode" eg:"shop_daily"`
|
||||
ExtractCode string `orm:"extract_code" json:"extractCode" dc:"抽取编码" eg:"kuaishou_order_extract"`
|
||||
StatDate string `orm:"stat_date" json:"statDate" dc:"统计日期" eg:"2026-06-01"`
|
||||
ExtractType string `orm:"extract_type" json:"extractType" dc:"抽取类型 FULL/INCREMENTAL" eg:"INCREMENTAL"`
|
||||
Status string `orm:"status" json:"status" dc:"status" eg:"ACTIVE"`
|
||||
TotalCount int `orm:"total_count" json:"totalCount" dc:"总记录数" eg:"100"`
|
||||
SuccessCount int `orm:"success_count" json:"successCount" dc:"成功记录数" eg:"98"`
|
||||
FailCount int `orm:"fail_count" json:"failCount" dc:"失败记录数" eg:"2"`
|
||||
StartTime *time.Time `orm:"start_time" json:"startTime" dc:"开始时间"`
|
||||
EndTime *time.Time `orm:"end_time" json:"endTime" dc:"结束时间"`
|
||||
ErrorMessage string `orm:"error_message" json:"errorMessage" dc:"错误信息"`
|
||||
Executor string `orm:"executor" json:"executor" dc:"执行人" eg:"system"`
|
||||
CreatedAt *time.Time `orm:"created_at" json:"createdAt" dc:"创建时间"`
|
||||
UpdatedAt *time.Time `orm:"updated_at" json:"updatedAt" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 辅助结构
|
||||
// ============================================================
|
||||
|
||||
// JoinConfig 关联配置
|
||||
type JoinConfig struct {
|
||||
JoinTable string `json:"joinTable" dc:"关联表名" eg:"ods_kuaishou_shop"`
|
||||
JoinAlias string `json:"joinAlias" dc:"关联表别名" eg:"s"`
|
||||
JoinType string `json:"joinType" dc:"LEFT/RIGHT/INNER" eg:"LEFT"`
|
||||
JoinCondition string `json:"joinCondition" dc:"关联条件" eg:"o.shop_id = s.shop_id"`
|
||||
FieldMappings []FieldMapping `json:"fieldMappings" dc:"字段映射列表"`
|
||||
}
|
||||
|
||||
// FieldMapping 字段映射
|
||||
type FieldMapping struct {
|
||||
SourceField string `json:"sourceField" dc:"源字段"`
|
||||
TargetField string `json:"targetField" dc:"目标字段"`
|
||||
FieldType string `json:"fieldType" dc:"字段类型 STRING/INT/FLOAT/DATE/DATETIME/JSONB" eg:"FLOAT"`
|
||||
AggregateFunction string `json:"aggregateFunction" dc:"聚合函数 SUM/COUNT/AVG/MAX/MIN"`
|
||||
DefaultValue interface{} `json:"defaultValue" dc:"默认值"`
|
||||
TransformRule *TransformRule `json:"transformRule,omitempty" dc:"transformRule"`
|
||||
}
|
||||
|
||||
// TransformRule 转换规则
|
||||
type TransformRule struct {
|
||||
RuleCode string `json:"ruleCode" dc:"规则编码" eg:"AMOUNT_CONVERT"`
|
||||
RuleType string `json:"ruleType" dc:"DIRECT/MAPPING/FORMAT/CALCULATE" eg:"FORMAT"`
|
||||
Expression string `json:"expression" dc:"表达式(衍生字段)" eg:"order_amount - refund_amount"`
|
||||
Format string `json:"format" dc:"format" eg:"%Y-%m-%d"`
|
||||
Mapping map[string]interface{} `json:"mapping" dc:"映射表(MAPPING类型时使用)"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 前端请求/响应结构体
|
||||
// ============================================================
|
||||
|
||||
// UserSelectQueryReq 用户选择查询请求
|
||||
type UserSelectQueryReq struct {
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
Dimensions []string `json:"dimensions" dc:"统计维度列表,如 shop_id/anchor_id/date"`
|
||||
Indicators []IndicatorSelect `json:"indicators" dc:"统计指标列表(含聚合方式)"`
|
||||
Filters []FilterCondition `json:"filters" dc:"筛选条件列表"`
|
||||
TimeRange *TimeRange `json:"timeRange" dc:"时间范围"`
|
||||
TimeGroup string `json:"timeGroup" dc:"时间分组: day/week/month/quarter" eg:"day"`
|
||||
OrderBy []OrderCondition `json:"orderBy" dc:"排序条件"`
|
||||
Page int `json:"page" dc:"页码" d:"1" eg:"1"`
|
||||
PageSize int `json:"pageSize" dc:"每页条数" d:"20" eg:"20"`
|
||||
}
|
||||
|
||||
// IndicatorSelect 指标选择
|
||||
type IndicatorSelect struct {
|
||||
FieldCode string `json:"fieldCode" dc:"字段编码" eg:"order_amount"`
|
||||
Aggregate string `json:"aggregate" dc:"聚合方式: SUM/COUNT/AVG/MAX/MIN" eg:"SUM"`
|
||||
Alias string `json:"alias" dc:"别名" eg:"总订单金额"`
|
||||
}
|
||||
|
||||
// FilterCondition 筛选条件
|
||||
type FilterCondition struct {
|
||||
FieldCode string `json:"fieldCode" dc:"字段编码" eg:"order_amount"`
|
||||
Operator string `json:"operator" dc:"操作符: =/!=/>/</>=/<=/IN/LIKE/BETWEEN" eg:"="`
|
||||
Value interface{} `json:"value" dc:"值"`
|
||||
Value2 interface{} `json:"value2" dc:"第二个值(BETWEEN时使用)"`
|
||||
}
|
||||
|
||||
// TimeRange 时间范围
|
||||
type TimeRange struct {
|
||||
StartDate string `json:"startDate" dc:"开始日期 yyyy-MM-dd" eg:"2026-06-01"`
|
||||
EndDate string `json:"endDate" dc:"结束日期 yyyy-MM-dd" eg:"2026-06-30"`
|
||||
}
|
||||
|
||||
// OrderCondition 排序条件
|
||||
type OrderCondition struct {
|
||||
FieldCode string `json:"fieldCode" dc:"字段编码" eg:"order_amount"`
|
||||
Direction string `json:"direction" dc:"排序方向: ASC/DESC" eg:"DESC"`
|
||||
}
|
||||
|
||||
// UserSelectQueryResp 用户选择查询响应
|
||||
type UserSelectQueryResp struct {
|
||||
List []map[string]interface{} `json:"list" dc:"数据列表"`
|
||||
Total int64 `json:"total" dc:"总数" eg:"100"`
|
||||
Page int `json:"page" dc:"当前页" eg:"1"`
|
||||
PageSize int `json:"pageSize" dc:"每页条数" eg:"20"`
|
||||
TotalPages int `json:"totalPages" dc:"总页数" eg:"5"`
|
||||
Sql string `json:"sql,omitempty" dc:"执行的SQL(调试用)" eg:"SELECT 1"`
|
||||
ExecTimeMs int64 `json:"execTimeMs" dc:"执行耗时(毫秒)" eg:"1234"`
|
||||
}
|
||||
|
||||
// ExtractDailyDataReq 按天抽取数据请求
|
||||
type ExtractDailyDataReq struct {
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
StatDate string `json:"statDate" v:"required" dc:"统计日期 yyyy-MM-dd" eg:"2026-06-01"`
|
||||
Executor string `json:"executor" dc:"执行人" eg:"system"`
|
||||
}
|
||||
|
||||
// ExtractDailyDataResp 按天抽取数据响应
|
||||
type ExtractDailyDataResp struct {
|
||||
Success bool `json:"success" dc:"是否成功" eg:"true"`
|
||||
TotalCount int `json:"totalCount" dc:"总记录数" eg:"100"`
|
||||
SuccessCount int `json:"successCount" dc:"成功记录数" eg:"98"`
|
||||
FailCount int `json:"failCount" dc:"失败记录数" eg:"2"`
|
||||
ExecTimeMs int64 `json:"execTimeMs" dc:"执行耗时(毫秒)" eg:"1234"`
|
||||
ErrorMsg string `json:"errorMsg" dc:"错误信息"`
|
||||
}
|
||||
|
||||
// AutoCreateStatTableReq 自动创建统计宽表请求
|
||||
type AutoCreateStatTableReq struct {
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
Creator string `json:"creator" dc:"创建人" eg:"admin"`
|
||||
}
|
||||
|
||||
// AutoCreateStatTableResp 自动创建统计宽表响应
|
||||
type AutoCreateStatTableResp struct {
|
||||
Success bool `json:"success" dc:"是否成功" eg:"true"`
|
||||
TableName string `json:"tableName" dc:"创建的表名" eg:"stat_kuaishou_shop_daily"`
|
||||
ColumnCount int `json:"columnCount" dc:"字段数量" eg:"15"`
|
||||
ExecTimeMs int64 `json:"execTimeMs" dc:"执行耗时(毫秒)" eg:"1234"`
|
||||
}
|
||||
|
||||
// GetReportFieldsResp 获取报表可用字段响应
|
||||
type GetReportFieldsResp struct {
|
||||
BusinessCode string `json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" dc:"报表编码" eg:"shop_daily"`
|
||||
Dimensions []FieldConfig `json:"dimensions" dc:"维度字段列表"`
|
||||
Indicators []FieldConfig `json:"indicators" dc:"指标字段列表"`
|
||||
Filters []FieldConfig `json:"filters" dc:"筛选字段列表"`
|
||||
}
|
||||
|
||||
// BackfillReq 批量回填请求
|
||||
type BackfillReq struct {
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
StartDate string `json:"startDate" v:"required" dc:"开始日期 yyyy-MM-dd" eg:"2026-06-01"`
|
||||
EndDate string `json:"endDate" v:"required" dc:"结束日期 yyyy-MM-dd" eg:"2026-06-30"`
|
||||
Executor string `json:"executor" dc:"执行人" eg:"admin"`
|
||||
}
|
||||
|
||||
// BackfillResp 批量回填响应
|
||||
type BackfillResp struct {
|
||||
Success bool `json:"success" dc:"是否全部成功" eg:"true"`
|
||||
TotalDays int `json:"totalDays" dc:"总天数" eg:"30"`
|
||||
SuccessDays int `json:"successDays" dc:"成功天数" eg:"28"`
|
||||
FailDays int `json:"failDays" dc:"失败天数" eg:"2"`
|
||||
ExecTimeMs int64 `json:"execTimeMs" dc:"总耗时(毫秒)" eg:"5678"`
|
||||
ErrorMsg string `json:"errorMsg" dc:"错误信息"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 配置 CRUD 请求/响应
|
||||
// ============================================================
|
||||
|
||||
// SaveBusinessReq 保存业务配置请求(新增/修改合一)
|
||||
type SaveBusinessReq struct {
|
||||
ID *int64 `json:"id" dc:"有值为更新,无值为新增" eg:"1"`
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
BusinessName string `json:"businessName" v:"required" dc:"业务名称" eg:"快手电商"`
|
||||
Description string `json:"description" dc:"描述" eg:"快手电商店铺每日统计报表"`
|
||||
Status string `json:"status" dc:"状态 ACTIVE/INACTIVE" d:"ACTIVE" eg:"ACTIVE"`
|
||||
Config map[string]interface{} `json:"config" dc:"扩展配置"`
|
||||
Operator string `json:"operator" dc:"操作人" eg:"="`
|
||||
}
|
||||
|
||||
// SaveReportReq 保存报表配置请求
|
||||
type SaveReportReq struct {
|
||||
ID *int64 `json:"id" dc:"主键ID" eg:"1"`
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
ReportName string `json:"reportName" v:"required" dc:"报表名称" eg:"店铺日报"`
|
||||
Description string `json:"description" dc:"描述" eg:"快手电商店铺每日统计报表"`
|
||||
Status string `json:"status" dc:"状态" d:"ACTIVE" eg:"ACTIVE"`
|
||||
StatTableName string `json:"statTableName" v:"required" dc:"统计宽表名"`
|
||||
StatTableComment string `json:"statTableComment" dc:"统计宽表注释"`
|
||||
DateField string `json:"dateField" dc:"日期字段" d:"stat_date" eg:"stat_date"`
|
||||
PrimaryKeys []string `json:"primaryKeys" dc:"主键字段"`
|
||||
ConflictKeys []string `json:"conflictKeys" dc:"冲突键(唯一索引)"`
|
||||
Config map[string]interface{} `json:"config" dc:"扩展配置"`
|
||||
Operator string `json:"operator" dc:"操作人" eg:"="`
|
||||
}
|
||||
|
||||
// SaveFieldReq 保存字段配置请求
|
||||
type SaveFieldReq struct {
|
||||
ID *int64 `json:"id" dc:"主键ID" eg:"1"`
|
||||
BusinessCode string `json:"businessCode" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" dc:"报表编码" eg:"shop_daily"`
|
||||
FieldCode string `json:"fieldCode" v:"required" dc:"字段编码" eg:"order_amount"`
|
||||
FieldName string `json:"fieldName" v:"required" dc:"字段名称" eg:"订单金额"`
|
||||
FieldType string `json:"fieldType" v:"required" dc:"字段类型 STRING/INT/FLOAT/DATE/DATETIME/JSONB" eg:"FLOAT"`
|
||||
DataType string `json:"dataType" dc:"数据存储类型" d:"STRING" eg:"STRING"`
|
||||
FieldRole string `json:"fieldRole" v:"required" dc:"字段角色 DIMENSION/INDICATOR/FILTER/FILTER_ONLY" eg:"DIMENSION"`
|
||||
IsAggregatable bool `json:"isAggregatable" dc:"是否可聚合" eg:"true"`
|
||||
IsFilterable bool `json:"isFilterable" dc:"是否可筛选" d:"true" eg:"true"`
|
||||
IsQueryable bool `json:"isQueryable" dc:"是否可查询" d:"true" eg:"true"`
|
||||
IsSortable bool `json:"isSortable" dc:"是否可排序" d:"true" eg:"true"`
|
||||
DefaultAggregate string `json:"defaultAggregate" dc:"默认聚合方式" eg:"SUM"`
|
||||
ValidAggregates []string `json:"validAggregates" dc:"可选聚合列表"`
|
||||
FilterOperators []string `json:"filterOperators" dc:"可选操作符列表"`
|
||||
Expression string `json:"expression" dc:"表达式(衍生字段)" eg:"order_amount - refund_amount"`
|
||||
ExpressionType string `json:"expressionType" dc:"表达式类型 DIRECT/CALCULATED" eg:"DIRECT"`
|
||||
FormatPattern string `json:"formatPattern" dc:"格式化模板" eg:"#,##0.00"`
|
||||
Unit string `json:"unit" dc:"单位" eg:"元"`
|
||||
DictCode string `json:"dictCode" dc:"字典编码" eg:"dict_order_type"`
|
||||
SortOrder int `json:"sortOrder" dc:"排序" eg:"1"`
|
||||
GroupName string `json:"groupName" dc:"分组名称" eg:"交易指标"`
|
||||
Status string `json:"status" dc:"状态" d:"ACTIVE" eg:"ACTIVE"`
|
||||
Operator string `json:"operator" dc:"操作人" eg:"admin"`
|
||||
}
|
||||
|
||||
// SaveReportWithFieldsReq 保存报表及字段配置请求(字段全量替换)
|
||||
type SaveReportWithFieldsReq struct {
|
||||
ID *int64 `json:"id" dc:"主键ID(更新时必填,新建为空)"`
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
ReportName string `json:"reportName" v:"required" dc:"报表名称" eg:"店铺日报"`
|
||||
Description string `json:"description" dc:"描述" eg:"快手电商店铺每日统计报表"`
|
||||
Status string `json:"status" dc:"状态" d:"ACTIVE" eg:"ACTIVE"`
|
||||
StatTableName string `json:"statTableName" v:"required" dc:"统计宽表名" eg:"stat_kuaishou_shop_daily"`
|
||||
StatTableComment string `json:"statTableComment" dc:"统计宽表注释" eg:"快手电商-店铺日报"`
|
||||
DateField string `json:"dateField" dc:"日期字段" d:"stat_date" eg:"stat_date"`
|
||||
PrimaryKeys []string `json:"primaryKeys" dc:"主键字段"`
|
||||
ConflictKeys []string `json:"conflictKeys" dc:"冲突键(唯一索引)"`
|
||||
Config map[string]interface{} `json:"config" dc:"扩展配置"`
|
||||
Fields []SaveFieldReq `json:"fields" dc:"字段配置列表(全量替换,不在列表中的字段将被删除)"`
|
||||
}
|
||||
|
||||
// SaveExtractConfigReq 保存抽取配置请求
|
||||
type SaveExtractConfigReq struct {
|
||||
ID *int64 `json:"id" dc:"主键ID" eg:"1"`
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
ExtractCode string `json:"extractCode" v:"required" dc:"抽取编码" eg:"kuaishou_order_extract"`
|
||||
ExtractName string `json:"extractName" v:"required" dc:"抽取名称" eg:"订单抽取"`
|
||||
SourceTableName string `json:"sourceTableName" v:"required" dc:"源表名" eg:"ods_kuaishou_order"`
|
||||
SourceTableAlias string `json:"sourceTableAlias" dc:"源表别名" eg:"o"`
|
||||
TargetTableName string `json:"targetTableName" v:"required" dc:"目标表名" eg:"stat_kuaishou_shop_daily"`
|
||||
IsEnabled bool `json:"isEnabled" dc:"是否启用" d:"true" eg:"true"`
|
||||
ExtractType string `json:"extractType" dc:"抽取类型 FULL/INCREMENTAL" d:"INCREMENTAL" eg:"INCREMENTAL"`
|
||||
ExtractMode string `json:"extractMode" dc:"抽取模式 DIRECT/AGGREGATE" d:"DIRECT" eg:"DIRECT"`
|
||||
ExtractKeyField string `json:"extractKeyField" dc:"抽取关键字段(增量依据)" eg:"updated_at"`
|
||||
ExtractKeyFormat string `json:"extractKeyFormat" dc:"关键字段格式" eg:"yyyy-MM-dd HH:mm:ss"`
|
||||
GroupByFields []string `json:"groupByFields" dc:"GROUP BY 字段列表"`
|
||||
FilterExpression string `json:"filterExpression" dc:"过滤表达式" eg:"status = 'PAID'"`
|
||||
JoinConfigs []JoinConfig `json:"joinConfigs" dc:"JOIN配置"`
|
||||
FieldMappings []FieldMapping `json:"fieldMappings" dc:"字段映射列表"`
|
||||
TransformRules []TransformRule `json:"transformRules" dc:"转换规则列表"`
|
||||
BatchSize int `json:"batchSize" dc:"批处理大小" d:"1000" eg:"1000"`
|
||||
Status string `json:"status" dc:"状态" d:"ACTIVE" eg:"ACTIVE"`
|
||||
Operator string `json:"operator" dc:"操作人" eg:"="`
|
||||
}
|
||||
|
||||
// PageReq 分页请求参数
|
||||
type PageReq struct {
|
||||
PageNum int `json:"pageNum" d:"1" dc:"页码"`
|
||||
PageSize int `json:"pageSize" d:"20" dc:"每页条数"`
|
||||
}
|
||||
|
||||
// PageRes 分页响应
|
||||
type PageRes struct {
|
||||
Total int `json:"total" dc:"总数"`
|
||||
Page int `json:"page" dc:"当前页"`
|
||||
PageSize int `json:"pageSize" dc:"每页条数"`
|
||||
TotalPages int `json:"totalPages" dc:"总页数"`
|
||||
}
|
||||
|
||||
// IdReq 通用 ID 请求
|
||||
type IdReq struct {
|
||||
ID int64 `json:"id" v:"required" dc:"主键ID" eg:"1"`
|
||||
}
|
||||
|
||||
// SaveResult 写操作通用返回
|
||||
type SaveResult struct {
|
||||
Success bool `json:"success" dc:"是否成功" eg:"true"`
|
||||
ID int64 `json:"id" dc:"主键ID" eg:"1"`
|
||||
Message string `json:"message" dc:"结果消息" eg:"操作成功"`
|
||||
}
|
||||
|
||||
// DeleteResult 删除结果
|
||||
type DeleteResult struct {
|
||||
Success bool `json:"success" dc:"是否成功" eg:"true"`
|
||||
Message string `json:"message" dc:"结果消息" eg:"操作成功"`
|
||||
}
|
||||
|
||||
// GetExtractConfigsReq 获取抽取配置列表请求
|
||||
type GetExtractConfigsReq struct {
|
||||
BusinessCode string `json:"businessCode" v:"required" dc:"业务编码" eg:"KUAISHOU"`
|
||||
ReportCode string `json:"reportCode" v:"required" dc:"报表编码" eg:"shop_daily"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 常量定义
|
||||
// ============================================================
|
||||
|
||||
const (
|
||||
// 状态
|
||||
StatusActive = "ACTIVE"
|
||||
StatusInactive = "INACTIVE"
|
||||
|
||||
// 字段角色
|
||||
RoleDimension = "DIMENSION"
|
||||
RoleIndicator = "INDICATOR"
|
||||
RoleFilter = "FILTER"
|
||||
RoleFilterOnly = "FILTER_ONLY"
|
||||
|
||||
// 字段类型
|
||||
FieldTypeString = "STRING"
|
||||
FieldTypeInt = "INT"
|
||||
FieldTypeFloat = "FLOAT"
|
||||
FieldTypeDate = "DATE"
|
||||
FieldTypeDatetime = "DATETIME"
|
||||
FieldTypeJsonb = "JSONB"
|
||||
|
||||
// 聚合方式
|
||||
AggregateSum = "SUM"
|
||||
AggregateCount = "COUNT"
|
||||
AggregateAvg = "AVG"
|
||||
AggregateMax = "MAX"
|
||||
AggregateMin = "MIN"
|
||||
|
||||
// 操作符
|
||||
OperatorEq = "="
|
||||
OperatorNe = "!="
|
||||
OperatorGt = ">"
|
||||
OperatorLt = "<"
|
||||
OperatorGe = ">="
|
||||
OperatorLe = "<="
|
||||
OperatorIn = "IN"
|
||||
OperatorLike = "LIKE"
|
||||
OperatorBetween = "BETWEEN"
|
||||
|
||||
// 抽取类型
|
||||
ExtractTypeFull = "FULL"
|
||||
ExtractTypeIncremental = "INCREMENTAL"
|
||||
|
||||
// 抽取模式
|
||||
ExtractModeDirect = "DIRECT" // 逐行抽取(默认,源表每行 → 宽表一行)
|
||||
ExtractModeAggregate = "AGGREGATE" // 聚合抽取(按 GROUP BY 聚合,SUM/COUNT/AVG)
|
||||
|
||||
// 抽取状态
|
||||
ExtractStatusRunning = "RUNNING"
|
||||
ExtractStatusSuccess = "SUCCESS"
|
||||
ExtractStatusFailed = "FAILED"
|
||||
)
|
||||
|
||||
// FieldTypeToPG 字段类型转PG列类型定义
|
||||
func FieldTypeToPG(fieldType string) string {
|
||||
switch fieldType {
|
||||
case FieldTypeInt:
|
||||
return "NUMERIC(20,0) DEFAULT 0"
|
||||
case FieldTypeFloat:
|
||||
return "NUMERIC(20,4) DEFAULT 0"
|
||||
case FieldTypeDate:
|
||||
return "VARCHAR(16) DEFAULT ''"
|
||||
case FieldTypeDatetime:
|
||||
return "TIMESTAMP WITH TIME ZONE"
|
||||
case FieldTypeJsonb:
|
||||
return "JSONB DEFAULT '{}'"
|
||||
default:
|
||||
return "VARCHAR(256) DEFAULT ''"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
)
|
||||
|
||||
// initTables 初始化系统表
|
||||
func initTables(ctx context.Context) error {
|
||||
ddls := []string{
|
||||
// 业务配置表
|
||||
`CREATE TABLE IF NOT EXISTS report_business_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
business_name VARCHAR(128) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
config JSONB DEFAULT '{}',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE
|
||||
)`,
|
||||
|
||||
// 报表配置表
|
||||
`CREATE TABLE IF NOT EXISTS report_report_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
report_name VARCHAR(128) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
stat_table_name VARCHAR(128) NOT NULL,
|
||||
stat_table_comment VARCHAR(256) DEFAULT '',
|
||||
date_field VARCHAR(64) DEFAULT 'stat_date',
|
||||
primary_keys JSONB DEFAULT '["id"]'::jsonb,
|
||||
conflict_keys JSONB DEFAULT '["stat_date"]'::jsonb,
|
||||
config JSONB DEFAULT '{}',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_code UNIQUE (tenant_id, business_code, report_code)
|
||||
)`,
|
||||
|
||||
// 字段配置表
|
||||
`CREATE TABLE IF NOT EXISTS report_field_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
field_code VARCHAR(64) NOT NULL,
|
||||
field_name VARCHAR(128) NOT NULL,
|
||||
field_type VARCHAR(32) NOT NULL,
|
||||
data_type VARCHAR(32) NOT NULL DEFAULT 'STRING',
|
||||
field_role VARCHAR(32) NOT NULL,
|
||||
is_aggregatable BOOLEAN DEFAULT FALSE,
|
||||
is_filterable BOOLEAN DEFAULT TRUE,
|
||||
is_queryable BOOLEAN DEFAULT TRUE,
|
||||
is_sortable BOOLEAN DEFAULT TRUE,
|
||||
default_aggregate VARCHAR(32) DEFAULT '',
|
||||
valid_aggregates JSONB DEFAULT '[]'::jsonb,
|
||||
filter_operators JSONB DEFAULT '["=","!=",">","<",">=","<=","IN","LIKE","BETWEEN"]'::jsonb,
|
||||
expression VARCHAR(512) DEFAULT '',
|
||||
expression_type VARCHAR(32) DEFAULT '',
|
||||
format_pattern VARCHAR(64) DEFAULT '',
|
||||
unit VARCHAR(32) DEFAULT '',
|
||||
dict_code VARCHAR(64) DEFAULT '',
|
||||
sort_order INT DEFAULT 0,
|
||||
group_name VARCHAR(64) DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_field_code UNIQUE (tenant_id, business_code, report_code, field_code)
|
||||
)`,
|
||||
|
||||
// 抽取配置表
|
||||
`CREATE TABLE IF NOT EXISTS report_extract_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
extract_code VARCHAR(64) NOT NULL,
|
||||
extract_name VARCHAR(128) NOT NULL,
|
||||
source_table_name VARCHAR(128) NOT NULL,
|
||||
source_table_alias VARCHAR(64) DEFAULT '',
|
||||
target_table_name VARCHAR(128) NOT NULL,
|
||||
is_enabled BOOLEAN DEFAULT TRUE,
|
||||
extract_type VARCHAR(32) NOT NULL DEFAULT 'FULL',
|
||||
extract_mode VARCHAR(32) NOT NULL DEFAULT 'DIRECT',
|
||||
extract_key_field VARCHAR(64) DEFAULT '',
|
||||
extract_key_format VARCHAR(64) DEFAULT '',
|
||||
group_by_fields JSONB DEFAULT '[]'::jsonb,
|
||||
filter_expression TEXT DEFAULT '',
|
||||
join_configs JSONB DEFAULT '[]'::jsonb,
|
||||
field_mappings JSONB DEFAULT '[]'::jsonb,
|
||||
transform_rules JSONB DEFAULT '[]'::jsonb,
|
||||
batch_size INT DEFAULT 1000,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_extract_code UNIQUE (tenant_id, business_code, report_code, extract_code)
|
||||
)`,
|
||||
|
||||
// 抽取记录表
|
||||
`CREATE TABLE IF NOT EXISTS report_extract_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
extract_code VARCHAR(64) NOT NULL,
|
||||
stat_date VARCHAR(16) NOT NULL,
|
||||
extract_type VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'RUNNING',
|
||||
total_count INT DEFAULT 0,
|
||||
success_count INT DEFAULT 0,
|
||||
fail_count INT DEFAULT 0,
|
||||
start_time TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
end_time TIMESTAMP WITH TIME ZONE,
|
||||
error_message TEXT DEFAULT '',
|
||||
executor VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
CONSTRAINT uk_extract_keys UNIQUE (tenant_id, business_code, report_code, extract_code, stat_date)
|
||||
)`,
|
||||
}
|
||||
|
||||
for _, ddl := range ddls {
|
||||
if _, err := gfdb.DB(ctx).Exec(ctx, ddl); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+48
-11
@@ -1,5 +1,5 @@
|
||||
server:
|
||||
address : ":3001"
|
||||
address : ":3013"
|
||||
name: "data-engine"
|
||||
workerId: 1
|
||||
logPath: "resource/log/server"
|
||||
@@ -9,15 +9,37 @@ rate:
|
||||
limit: 200
|
||||
burst: 300
|
||||
|
||||
# 报表引擎配置
|
||||
report:
|
||||
# 是否由本进程内部自动调度每日抽取(gcron "0 0 2 * * *")
|
||||
# true — 进程启动后自动在每天 02:00 执行全量抽取,无需外部依赖
|
||||
# false — 不自启调度,由 PPGo_Job 通过 POST /report/extractAll 触发(推荐)
|
||||
extract_enabled: false
|
||||
|
||||
# 数据同步配置
|
||||
sync:
|
||||
page_size: 100 # 每次分页请求条数
|
||||
concurrency: 5 # 同步并发数
|
||||
retry_count: 3 # 最大重试次数
|
||||
sync_interval_minutes: 60 # 自动同步间隔(分钟)
|
||||
compensation_interval_seconds: 300 # 补偿调度器扫描间隔(秒)
|
||||
# 是否由本进程内部自动循环执行全平台同步(每次完成后等待 sync_interval_minutes 再继续)
|
||||
# true — 进程启动后自动在后台循环同步所有 ACTIVE 平台的接口数据
|
||||
# false — 不自启调度,由 PPGo_Job 通过 POST /sync/ctrl/triggerAll 触发(推荐)
|
||||
auto_sync_enabled: false
|
||||
sync_timeout_minutes: 120 # 单次同步超时(分钟),全量超大表可适当调大
|
||||
default_lookback_days: 10 # 全量同步默认回溯天数(快手限制90天内,留1天余量)(单接口可通过 request_config.full_sync_start_time 覆盖)
|
||||
default_tenant_id: 1 # 自动同步使用的租户 ID(多租户部署时配置)
|
||||
|
||||
# Database.
|
||||
database:
|
||||
default:
|
||||
- type: "pgsql"
|
||||
host: "localhost"
|
||||
port: "5432"
|
||||
user: "postgres"
|
||||
pass: "root"
|
||||
name: "cid"
|
||||
default:
|
||||
type: "pgsql"
|
||||
host: "192.168.0.83"
|
||||
port: "15432"
|
||||
user: "sql9f15b63fd203b36e"
|
||||
pass: "1ec94b1acdaf57b66030242d418fad5a"
|
||||
name: "engine"
|
||||
role: "master"
|
||||
maxIdle: "5"
|
||||
maxOpen: "20"
|
||||
@@ -33,7 +55,7 @@ database:
|
||||
redis:
|
||||
# 集群模式配置方法
|
||||
default:
|
||||
address: 192.168.3.30:6379
|
||||
address: 192.168.0.83:6379
|
||||
db: 0
|
||||
idleTimeout: "60s" #连接最大空闲时间,使用时间字符串例如30s/1m/1d
|
||||
maxConnLifetime: "90s" #连接最长存活时间,使用时间字符串例如30s/1m/1d
|
||||
@@ -43,7 +65,22 @@ redis:
|
||||
writeTimeout: "30s" #TCP的Write操作超时时间,使用时间字符串例如30s/1m/1d
|
||||
maxActive: 100
|
||||
consul:
|
||||
address: 116.204.74.41:8500
|
||||
address: 192.168.0.83:8500
|
||||
#k8s:
|
||||
# apiServer: "https://192.168.3.37:6443"
|
||||
# token: "eyJhbGciOiJSUzI1NiIsImtpZCI6IlR6X0QtelVDYkZPYnpjTDlfamZzOEFjbERJb2dUMTRqMjNfYUNncGcwRW8ifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiLCJrM3MiXSwiZXhwIjoxODA3ODY3MTA1LCJpYXQiOjE3NzYzMzExMDUsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNjAwMGY0ODctZTQ4Ni00NTUxLWIyNjgtMzE1MWE1MDE5YjU4Iiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsInNlcnZpY2VhY2NvdW50Ijp7Im5hbWUiOiJkYXNoYm9hcmQtYWRtaW4iLCJ1aWQiOiJlZjAxY2IxNS0xNTc0LTRlYTYtODg4Ny03YjZhODY3OWFkYmIifX0sIm5iZiI6MTc3NjMzMTEwNSwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmRhc2hib2FyZC1hZG1pbiJ9.Z5MEy-dhWq6lruuR6QSI--cYRZzDoeEes78a4lLMtXa8lSLt1FZy16kNVn2pJli74RSk0kKS2F5GAatyuyGjF_yB7Tm1Tb7iCjzeUM7xRpdvXEmk4MUImlzwGeW31NXpwFrfR0oPS-9TLlbahEmsgwL0DUOC0CekSiToVbIduwEmmrB0FMPFayr5_wqGDmxdqtaAH3K-LJNCqPiAMqevsxTebrhyJSrThU-Pi7iJYm_sUd8VHKrRxrwDiGA77j4lfita_hZSdyrZe8qsrbGqomHxFWk9ZzcJJ9q7OLS13OXo-Xbcu-_TZcfDOreZhune9ctM2lmuOtmYnad47gYRcA"
|
||||
# kubeconfig: ""
|
||||
# namespace: "default"
|
||||
# pass: jiahui8888
|
||||
jaeger: #链路追踪
|
||||
addr: 116.204.74.41:4318
|
||||
addr: 192.168.0.83:4318
|
||||
|
||||
# 平台认证信息(已迁移到 api_datasource_platform 表中管理)
|
||||
# 保留此处仅作参考,系统启动后从数据库读取
|
||||
tencent:
|
||||
oauth:
|
||||
client_id: "1112038234"
|
||||
client_secret: "GxyjXFbZAs5dnsNQ"
|
||||
refresh_token: "d15b37363a42449026d337708516e95e"
|
||||
access_token: "4bacfc7c9b0a31f70ec0eb4771f8b542"
|
||||
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
package api_feature
|
||||
|
||||
// AppStatus 应用状态
|
||||
type AppStatus string
|
||||
// ApplicationStatus 应用状态(暂未使用,保留供未来扩展)
|
||||
type ApplicationStatus string
|
||||
|
||||
const (
|
||||
AppStatusActive AppStatus = "active" // 启用
|
||||
AppStatusInactive AppStatus = "inactive" // 停用
|
||||
ApplicationStatusPending ApplicationStatus = "pending"
|
||||
ApplicationStatusApproved ApplicationStatus = "approved"
|
||||
ApplicationStatusRejected ApplicationStatus = "rejected"
|
||||
)
|
||||
|
||||
func (s AppStatus) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
@@ -1,17 +1,10 @@
|
||||
package api_feature
|
||||
|
||||
// AppType 应用类型
|
||||
type AppType string
|
||||
// ApplicationType 应用类型(暂未使用,保留供未来扩展)
|
||||
type ApplicationType string
|
||||
|
||||
const (
|
||||
AppTypeWeb AppType = "web" // Web应用
|
||||
AppTypeMobile AppType = "mobile" // 移动应用
|
||||
AppTypeMiniApp AppType = "mini_app" // 小程序
|
||||
AppTypeH5 AppType = "h5" // H5应用
|
||||
AppTypeDesktop AppType = "desktop" // 桌面应用
|
||||
AppTypeThirdParty AppType = "third_party" // 第三方应用
|
||||
ApplicationTypeAPI ApplicationType = "api"
|
||||
ApplicationTypeWebhook ApplicationType = "webhook"
|
||||
ApplicationTypeSDK ApplicationType = "sdk"
|
||||
)
|
||||
|
||||
func (s AppType) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
package api_feature
|
||||
|
||||
// FetchStatus 数据获取状态
|
||||
// FetchStatus 数据拉取状态(暂未使用,保留供未来扩展)
|
||||
type FetchStatus string
|
||||
|
||||
const (
|
||||
FetchStatusPending FetchStatus = "pending" // 待执行
|
||||
FetchStatusRunning FetchStatus = "running" // 执行中
|
||||
FetchStatusSuccess FetchStatus = "success" // 成功
|
||||
FetchStatusFailed FetchStatus = "failed" // 失败
|
||||
FetchStatusRateLimit FetchStatus = "rate_limit" // 触发限流
|
||||
FetchStatusPending FetchStatus = "pending"
|
||||
FetchStatusRunning FetchStatus = "running"
|
||||
FetchStatusCompleted FetchStatus = "completed"
|
||||
FetchStatusFailed FetchStatus = "failed"
|
||||
)
|
||||
|
||||
func (f FetchStatus) String() string {
|
||||
return string(f)
|
||||
}
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
package api_feature
|
||||
|
||||
// LimitType 限流类型
|
||||
// LimitType 限流类型(暂未使用,保留供未来扩展)
|
||||
type LimitType string
|
||||
|
||||
const (
|
||||
LimitTypeApp LimitType = "app" // 应用维度限流
|
||||
LimitTypeTenant LimitType = "tenant" // 租户维度限流
|
||||
LimitTypeApi LimitType = "api" // 接口维度限流
|
||||
LimitTypeRate LimitType = "rate"
|
||||
LimitTypeBurst LimitType = "burst"
|
||||
)
|
||||
|
||||
func (l LimitType) String() string {
|
||||
return string(l)
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package api_feature
|
||||
|
||||
// MappingStatus 映射状态
|
||||
// MappingStatus 字段映射状态(暂未使用,保留供未来扩展)
|
||||
type MappingStatus string
|
||||
|
||||
const (
|
||||
MappingStatusActive MappingStatus = "active" // 启用
|
||||
MappingStatusInactive MappingStatus = "inactive" // 停用
|
||||
MappingStatusMapped MappingStatus = "mapped"
|
||||
MappingStatusUnmapped MappingStatus = "unmapped"
|
||||
)
|
||||
|
||||
func (s MappingStatus) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
@@ -1,21 +1,11 @@
|
||||
package api_feature
|
||||
|
||||
// SyncPlatform 同步平台类型
|
||||
type SyncPlatform string
|
||||
// PlatformType 平台类型(暂未使用,保留供未来扩展)
|
||||
type PlatformType string
|
||||
|
||||
const (
|
||||
PlatformTaobao SyncPlatform = "taobao" // 淘宝
|
||||
PlatformJD SyncPlatform = "jd" // 京东
|
||||
PlatformKuaishou SyncPlatform = "kuaishou" // 快手
|
||||
PlatformDouyin SyncPlatform = "douyin" // 抖音
|
||||
PlatformXhs SyncPlatform = "xhs" // 小红书
|
||||
PlatformPdd SyncPlatform = "pdd" // 拼多多
|
||||
PlatformXianyu SyncPlatform = "xianyu" // 闲鱼
|
||||
PlatformTmall SyncPlatform = "tmall" // 天猫
|
||||
PlatformWechat SyncPlatform = "wechat" // 微信
|
||||
PlatformCustom SyncPlatform = "custom" // 自定义平台
|
||||
PlatformTypeAds PlatformType = "ads"
|
||||
PlatformTypeEcom PlatformType = "ecom"
|
||||
PlatformTypeSocial PlatformType = "social"
|
||||
PlatformTypeDingTalk PlatformType = "dingtalk"
|
||||
)
|
||||
|
||||
func (s SyncPlatform) String() string {
|
||||
return string(s)
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package api_feature
|
||||
|
||||
// TransformType 转换类型
|
||||
// TransformType 字段转换类型(暂未使用,保留供未来扩展)
|
||||
type TransformType string
|
||||
|
||||
const (
|
||||
TransformTypeFixed TransformType = "fixed" // 固定值
|
||||
TransformTypeMapping TransformType = "mapping" // 值映射
|
||||
TransformTypeRegex TransformType = "regex" // 正则转换
|
||||
TransformTypeFunction TransformType = "function" // 函数转换
|
||||
TransformTypeScript TransformType = "script" // 脚本转换
|
||||
TransformTypeDirect TransformType = "direct"
|
||||
TransformTypeFormatDate TransformType = "formatDate"
|
||||
TransformTypeMapValue TransformType = "mapValue"
|
||||
TransformTypeConcat TransformType = "concat"
|
||||
TransformTypeCalc TransformType = "calc"
|
||||
TransformTypeRegex TransformType = "regex"
|
||||
)
|
||||
|
||||
func (t TransformType) String() string {
|
||||
return string(t)
|
||||
}
|
||||
|
||||
+9
-22
@@ -1,6 +1,12 @@
|
||||
package dict
|
||||
|
||||
// 转换类型
|
||||
// 注意:请不要在此文件添加与 consts/api-feature 包重复的类型定义。
|
||||
// PlatformStatus 和 ApiMethod 等类型已在 dataengine/consts/api-feature 中定义,
|
||||
// 请使用 api_feature.PlatformStatus / api_feature.ApiMethod 替代。
|
||||
|
||||
// Package dict 仅提供该包特有时使用。
|
||||
|
||||
// 转换类型(暂未使用,保留供未来扩展)
|
||||
const (
|
||||
TransformTypeDirect = "direct" // 直接映射
|
||||
TransformTypeFormatDate = "formatDate" // 日期格式化
|
||||
@@ -10,7 +16,7 @@ const (
|
||||
TransformTypeRegex = "regex" // 正则提取
|
||||
)
|
||||
|
||||
// 业务域
|
||||
// 业务域(暂未使用,保留供未来扩展)
|
||||
const (
|
||||
BusinessDomainUser = "user" // 用户
|
||||
BusinessDomainOrder = "order" // 订单
|
||||
@@ -18,7 +24,7 @@ const (
|
||||
BusinessDomainPayment = "payment" // 支付
|
||||
)
|
||||
|
||||
// 字段类型
|
||||
// 字段类型(暂未使用,保留供未来扩展)
|
||||
const (
|
||||
FieldTypeString = "string"
|
||||
FieldTypeNumber = "number"
|
||||
@@ -30,22 +36,3 @@ const (
|
||||
FieldTypeTime = "time"
|
||||
FieldTypeDateTime = "datetime"
|
||||
)
|
||||
|
||||
// 平台状态
|
||||
type PlatformStatus string
|
||||
|
||||
const (
|
||||
PlatformStatusActive PlatformStatus = "active"
|
||||
PlatformStatusInactive PlatformStatus = "inactive"
|
||||
)
|
||||
|
||||
// 接口方法
|
||||
type ApiMethod string
|
||||
|
||||
const (
|
||||
ApiMethodGET ApiMethod = "GET"
|
||||
ApiMethodPOST ApiMethod = "POST"
|
||||
ApiMethodPUT ApiMethod = "PUT"
|
||||
ApiMethodDELETE ApiMethod = "DELETE"
|
||||
ApiMethodPATCH ApiMethod = "PATCH"
|
||||
)
|
||||
|
||||
@@ -2,18 +2,8 @@ package public
|
||||
|
||||
// PostgreSQL表名常量
|
||||
const (
|
||||
ApiInterfaceTable = "cid_api_interface" // 接口管理表
|
||||
CidAccountReportDetailTable = "cid_account_report_detail" // 广告账户数据明细表
|
||||
CidAccountReportSumTable = "cid_account_report_sum" // 广告账户数据汇总表
|
||||
FieldMappingConfigTable = "field_mapping_config" // 字段映射表
|
||||
DatasourcePlatformTable = "datasource_platform" // 数据源平台表
|
||||
TaskReportTable = "task_report" // 调控任务数据表
|
||||
PopulationReportTable = "population_report" // 人群报表表
|
||||
MaterialReportTable = "material_report" // 素材报表表
|
||||
StorewideReportSumTable = "storewide_report_sum" // 全站数据sum表
|
||||
StorewideReportDetailTable = "storewide_report_detail" // 全站数据detail表
|
||||
CreativeReportSumTable = "creative_report_sum" // 广告创意数据sum表
|
||||
CreativeReportDetailTable = "creative_report_detail" // 广告创意数据detail表
|
||||
UnitReportSumTable = "unit_report_sum" // 广告创意数据detail表
|
||||
UnitReportDetailTable = "unit_report_detail" // 广告创意数据detail表
|
||||
ApiInterfaceTable = "api_interface" // 接口管理表
|
||||
DatasourcePlatformTable = "api_datasource_platform" // 数据源平台表
|
||||
SyncTaskLogTable = "sync_task_log" // 同步任务日志表
|
||||
SyncTrackerTable = "sync_tracker" // 同步跟踪表(增量同步时间记录)
|
||||
)
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
)
|
||||
|
||||
type AccountReport struct{}
|
||||
|
||||
// AccountReportDetail CidAccountReportDetail 广告数据报表详情控制器
|
||||
var AccountReportDetail = new(AccountReport)
|
||||
|
||||
// CreateCidAccountReportDetail 创建广告数据报表详情
|
||||
func (c *AccountReport) CreateCidAccountReportDetail(ctx context.Context, req *dto.CreateCidAccountReportDetailReq) (res *dto.CreateCidAccountReportDetailRes, err error) {
|
||||
return service.CidAccountReportDetail.Create(ctx, req.CidAccountReportDetailItem)
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportDetail 批量创建广告数据报表详情
|
||||
func (c *AccountReport) BatchCreateCidAccountReportDetail(ctx context.Context, req *dto.BatchCreateCidAccountReportDetailReq) (res *dto.BatchCreateCidAccountReportDetailRes, err error) {
|
||||
return service.CidAccountReportDetail.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
// CreateCidAccountReportSum 创建广告数据报表汇总
|
||||
func (c *AccountReport) CreateCidAccountReportSum(ctx context.Context, req *dto.CreateCidAccountReportSumReq) (res *dto.CreateCidAccountReportSumRes, err error) {
|
||||
return service.CidAccountReportDetail.CreateSum(ctx, req.CidAccountReportSumItem)
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportSum 批量创建广告数据报表汇总
|
||||
func (c *AccountReport) BatchCreateCidAccountReportSum(ctx context.Context, req *dto.BatchCreateCidAccountReportSumReq) (res *dto.BatchCreateCidAccountReportSumRes, err error) {
|
||||
return service.CidAccountReportDetail.BatchCreateSum(ctx, req)
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
)
|
||||
|
||||
type creativeReport struct{}
|
||||
|
||||
// CreativeReport 广告效果指标表控制器
|
||||
var CreativeReport = new(creativeReport)
|
||||
|
||||
// CreateCreativeReportSum 创建广告效果指标表
|
||||
func (c *creativeReport) CreateCreativeReportSum(ctx context.Context, req *dto.CreateCreativeReportSumReq) (res *dto.CreateCreativeReportSumRes, err error) {
|
||||
return service.CreativeReportSum.Create(ctx, req.CreativeReportSumItem)
|
||||
}
|
||||
|
||||
// BatchCreateCreativeReportSum 批量创建广告效果指标表
|
||||
func (c *creativeReport) BatchCreateCreativeReportSum(ctx context.Context, req *dto.BatchCreateCreativeReportSumReq) (res *dto.BatchCreateCreativeReportSumRes, err error) {
|
||||
return service.CreativeReportSum.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
func (c *creativeReport) CreateCreativeReportDetail(ctx context.Context, req *dto.CreateCreativeReportDetailReq) (res *dto.CreateCreativeReportDetailRes, err error) {
|
||||
return service.CreativeReportSum.CreateDetail(ctx, req.CreativeReportDetailItem)
|
||||
}
|
||||
|
||||
func (c *creativeReport) BatchCreateCreativeReportDetail(ctx context.Context, req *dto.BatchCreateCreativeReportDetailReq) (res *dto.BatchCreateCreativeReportDetailRes, err error) {
|
||||
return service.CreativeReportSum.BatchCreateDetail(ctx, req)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
)
|
||||
|
||||
type materialReport struct{}
|
||||
|
||||
// MaterialReport 素材报表数据控制器
|
||||
var MaterialReport = new(materialReport)
|
||||
|
||||
// CreateMaterialReport 创建素材报表数据
|
||||
func (c *materialReport) CreateMaterialReport(ctx context.Context, req *dto.CreateMaterialReportReq) (res *dto.CreateMaterialReportRes, err error) {
|
||||
return service.MaterialReport.Create(ctx, req.MaterialReportItem)
|
||||
}
|
||||
|
||||
// BatchCreateMaterialReport 批量创建素材报表数据
|
||||
func (c *materialReport) BatchCreateMaterialReport(ctx context.Context, req *dto.BatchCreateMaterialReportReq) (res *dto.BatchCreateMaterialReportRes, err error) {
|
||||
return service.MaterialReport.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
// ListMaterialReport 获取素材报表数据列表
|
||||
func (c *materialReport) ListMaterialReport(ctx context.Context, req *dto.ListMaterialReportReq) (res *dto.ListMaterialReportRes, err error) {
|
||||
return service.MaterialReport.List(ctx, req)
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
var PopulationReport = new(populationReport)
|
||||
|
||||
type populationReport struct{}
|
||||
|
||||
// CreatePopulationReport 创建人群报表数据
|
||||
func (c *populationReport) CreatePopulationReport(ctx context.Context, req *dto.CreatePopulationReportReq) (*dto.CreatePopulationReportRes, error) {
|
||||
return service.PopulationReportService.Create(ctx, req.PopulationReportItem)
|
||||
}
|
||||
|
||||
// BatchCreatePopulationReport 批量创建人群报表数据
|
||||
func (c *populationReport) BatchCreatePopulationReport(ctx context.Context, req *dto.BatchCreatePopulationReportReq) (*dto.BatchCreatePopulationReportRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.PopulationReportService.BatchCreate(ctx, req.Items)
|
||||
}
|
||||
|
||||
// ListPopulationReport 查询人群报表数据列表
|
||||
func (c *populationReport) ListPopulationReport(ctx context.Context, req *dto.ListPopulationReportReq) (*dto.ListPopulationReportRes, error) {
|
||||
return service.PopulationReportService.List(ctx, req)
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
)
|
||||
|
||||
type storewideReport struct{}
|
||||
|
||||
// StorewideReport 广告效果指标控制器
|
||||
var StorewideReport = new(storewideReport)
|
||||
|
||||
// CreateStorewideReport 创建广告效果指标表
|
||||
func (c *storewideReport) CreateStorewideReport(ctx context.Context, req *dto.CreateStorewideReportSumReq) (res *dto.CreateStorewideReportSumRes, err error) {
|
||||
return service.StorewideReportSum.Create(ctx, req.StorewideReportSumItem)
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReport 批量创建广告效果指标表
|
||||
func (c *storewideReport) BatchCreateStorewideReport(ctx context.Context, req *dto.BatchCreateStorewideReportSumReq) (res *dto.BatchCreateStorewideReportSumRes, err error) {
|
||||
return service.StorewideReportSum.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
// CreateStorewideReportDetail 创建广告效果指标表
|
||||
func (c *storewideReport) CreateStorewideReportDetail(ctx context.Context, req *dto.CreateStorewideReportDetailReq) (res *dto.CreateStorewideReportDetailRes, err error) {
|
||||
return service.StorewideReportSum.CreateDetail(ctx, req.StorewideReportDetailItem)
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReportDetail 批量创建广告效果指标表
|
||||
func (c *storewideReport) BatchCreateStorewideReportDetail(ctx context.Context, req *dto.BatchCreateStorewideReportDetailReq) (res *dto.BatchCreateStorewideReportDetailRes, err error) {
|
||||
return service.StorewideReportSum.BatchCreateDetail(ctx, req)
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
service "cid/service/copydata"
|
||||
"context"
|
||||
)
|
||||
|
||||
type taskReport struct{}
|
||||
|
||||
// TaskReport 调控任务数据控制器
|
||||
var TaskReport = new(taskReport)
|
||||
|
||||
// CreateTaskReport 创建调控任务数据
|
||||
func (c *taskReport) CreateTaskReport(ctx context.Context, req *dto.CreateTaskReportReq) (res *dto.CreateTaskReportRes, err error) {
|
||||
return service.TaskReport.Create(ctx, req.TaskReportItem)
|
||||
}
|
||||
|
||||
// BatchCreateTaskReport 批量创建调控任务数据
|
||||
func (c *taskReport) BatchCreateTaskReport(ctx context.Context, req *dto.BatchCreateTaskReportReq) (res *dto.BatchCreateTaskReportRes, err error) {
|
||||
return service.TaskReport.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
// ListTaskReport 获取调控任务数据列表
|
||||
func (c *taskReport) ListTaskReport(ctx context.Context, req *dto.ListTaskReportReq) (res *dto.ListTaskReportRes, err error) {
|
||||
return service.TaskReport.List(ctx, req)
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/copydata"
|
||||
"cid/service/copydata"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type unitReport struct{}
|
||||
|
||||
// UnitReport 获取广告单元数据控制器
|
||||
var UnitReport = new(unitReport)
|
||||
|
||||
// Create 创建广告效果指标
|
||||
func (c *unitReport) Create(ctx context.Context, req *dto.CreateUnitReportSumReq) (res *dto.CreateUnitReportSumRes, err error) {
|
||||
return copydata.UnitReportSumService.Create(ctx, req.UnitReportSumItem)
|
||||
}
|
||||
|
||||
// BatchCreate 批量创建广告效果指标
|
||||
func (c *unitReport) BatchCreate(ctx context.Context, req *dto.BatchCreateUnitReportSumReq) (res *dto.BatchCreateUnitReportSumRes, err error) {
|
||||
return copydata.UnitReportSumService.BatchCreate(ctx, req)
|
||||
}
|
||||
|
||||
// CreateDetail 创建广告效果指标详情
|
||||
func (c *unitReport) CreateDetail(ctx context.Context, req *dto.CreateUnitReportDetailReq) (res *dto.CreateUnitReportDetailRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return copydata.UnitReportSumService.CreateDetail(ctx, req.UnitReportDetailItem)
|
||||
}
|
||||
|
||||
// BatchCreateDetail 批量创建广告效果指标详情
|
||||
func (c *unitReport) BatchCreateDetail(ctx context.Context, req *dto.BatchCreateUnitReportDetailReq) (res *dto.BatchCreateUnitReportDetailRes, err error) {
|
||||
return copydata.UnitReportSumService.BatchCreateDetail(ctx, req)
|
||||
}
|
||||
@@ -0,0 +1,360 @@
|
||||
package debug
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
var DebugController = new(debugCtrl)
|
||||
|
||||
type debugCtrl struct{}
|
||||
|
||||
// DebugPage 调试页面
|
||||
func (c *debugCtrl) DebugPage(r *ghttp.Request) {
|
||||
r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
r.Response.Write(adminHTML)
|
||||
r.Exit()
|
||||
}
|
||||
|
||||
var adminHTML = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>数据引擎管理后台</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f0f2f5; color: #333; }
|
||||
.header { background: #001529; color: #fff; padding: 14px 24px; display: flex; align-items: center; gap: 12px; }
|
||||
.header h1 { font-size: 18px; font-weight: 600; }
|
||||
.container { max-width: 1400px; margin: 0 auto; padding: 20px; }
|
||||
.tabs { display: flex; gap: 0; margin-bottom: 20px; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.tab { padding: 12px 28px; cursor: pointer; font-size: 14px; color: #666; border-bottom: 2px solid transparent; transition: all .2s; }
|
||||
.tab:hover { color: #1890ff; }
|
||||
.tab.active { color: #1890ff; border-bottom-color: #1890ff; font-weight: 600; }
|
||||
.panel { display: none; }
|
||||
.panel.active { display: block; }
|
||||
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; }
|
||||
.toolbar h2 { font-size: 16px; color: #1a1a2e; }
|
||||
.btn { display: inline-flex; align-items: center; gap: 4px; padding: 8px 16px; border: none; border-radius: 6px; font-size: 13px; cursor: pointer; transition: all .2s; }
|
||||
.btn-primary { background: #1890ff; color: #fff; }
|
||||
.btn-primary:hover { background: #40a9ff; }
|
||||
.btn-danger { background: #ff4d4f; color: #fff; }
|
||||
.btn-danger:hover { background: #ff7875; }
|
||||
.btn-sm { padding: 4px 10px; font-size: 12px; }
|
||||
.btn-outline { background: #fff; border: 1px solid #d9d9d9; color: #333; }
|
||||
.btn-outline:hover { border-color: #1890ff; color: #1890ff; }
|
||||
.btn-success { background: #52c41a; color: #fff; }
|
||||
.btn-success:hover { background: #73d13d; }
|
||||
table { width: 100%; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.06); border-collapse: collapse; }
|
||||
th { background: #fafafa; padding: 12px 16px; text-align: left; font-size: 13px; color: #666; font-weight: 600; border-bottom: 1px solid #f0f0f0; }
|
||||
td { padding: 12px 16px; font-size: 13px; border-bottom: 1px solid #f0f0f0; }
|
||||
tr:hover td { background: #fafafa; }
|
||||
.badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; }
|
||||
.badge.active { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
|
||||
.badge.inactive { background: #fff2f0; color: #ff4d4f; border: 1px solid #ffccc7; }
|
||||
.actions { display: flex; gap: 6px; }
|
||||
.modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.45); z-index: 1000; }
|
||||
.modal-overlay.open { display: block; }
|
||||
.modal { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background: #fff; border-radius: 8px; width: 600px; max-width: 90%; max-height: 85vh; overflow-y: auto; z-index: 1001; box-shadow: 0 4px 24px rgba(0,0,0,0.15); }
|
||||
.modal.open { display: block; }
|
||||
.modal-header { padding: 16px 20px; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; }
|
||||
.modal-header h3 { font-size: 16px; }
|
||||
.modal-close { background: none; border: none; font-size: 20px; cursor: pointer; color: #999; }
|
||||
.modal-body { padding: 20px; }
|
||||
.modal-footer { padding: 12px 20px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 8px; }
|
||||
.form-group { margin-bottom: 14px; }
|
||||
.form-group label { display: block; font-size: 13px; color: #666; margin-bottom: 4px; font-weight: 500; }
|
||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 8px 12px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 13px; outline: none; transition: border .2s; }
|
||||
.form-group input:focus, .form-group select:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24,144,255,0.1); }
|
||||
.form-group textarea { font-family: "Fira Code", "Consolas", monospace; font-size: 12px; min-height: 80px; resize: vertical; }
|
||||
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
|
||||
.loading { text-align: center; padding: 40px; color: #999; }
|
||||
.empty { text-align: center; padding: 32px; color: #999; font-size: 14px; }
|
||||
.interfaces-section { margin-top: 8px; }
|
||||
.sub-table { background: #fafafa; border-radius: 6px; overflow: hidden; margin-bottom: 8px; box-shadow: none; }
|
||||
.sub-table th { background: #f0f0f0; font-size: 12px; padding: 8px 12px; }
|
||||
.sub-table td { padding: 8px 12px; font-size: 12px; }
|
||||
.json-preview { max-width: 200px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: pointer; color: #1890ff; }
|
||||
.json-preview:hover { text-decoration: underline; }
|
||||
.platform-row { cursor: pointer; }
|
||||
.platform-row td:first-child { position: relative; }
|
||||
.expand-icon { margin-right: 8px; font-size: 10px; color: #999; transition: transform .2s; display: inline-block; }
|
||||
.expand-icon.open { transform: rotate(90deg); }
|
||||
.interface-detail { display: none; }
|
||||
.interface-detail.open { display: table-row; }
|
||||
.interface-detail td { padding: 0; }
|
||||
.interface-detail-inner { padding: 12px 16px 12px 44px; background: #fafafa; }
|
||||
.toast { position: fixed; top: 20px; right: 20px; padding: 12px 20px; border-radius: 6px; color: #fff; font-size: 13px; z-index: 2000; animation: slideIn .3s; }
|
||||
.toast.success { background: #52c41a; }
|
||||
.toast.error { background: #ff4d4f; }
|
||||
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header"><h1>📊 数据引擎管理后台</h1></div>
|
||||
<div class="container">
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="switchTab('platforms')">平台管理</div>
|
||||
<div class="tab" onclick="switchTab('interfaces')">接口管理</div>
|
||||
</div>
|
||||
|
||||
<div id="platforms-panel" class="panel active">
|
||||
<div class="toolbar"><h2>平台列表</h2><button class="btn btn-primary" onclick="openPlatformModal()">+ 新建平台</button></div>
|
||||
<div id="platform-loading" class="loading">加载中...</div>
|
||||
<div id="platform-list"></div>
|
||||
</div>
|
||||
|
||||
<div id="interfaces-panel" class="panel">
|
||||
<div class="toolbar"><h2>接口列表</h2><button class="btn btn-primary" onclick="openInterfaceModal()">+ 新建接口</button></div>
|
||||
<div id="interface-loading" class="loading">加载中...</div>
|
||||
<div id="interface-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal-overlay" id="modalOverlay" onclick="closeModal()"></div>
|
||||
<div class="modal" id="modal">
|
||||
<div class="modal-header"><h3 id="modalTitle"></h3><button class="modal-close" onclick="closeModal()">×</button></div>
|
||||
<div class="modal-body" id="modalBody"></div>
|
||||
<div class="modal-footer" id="modalFooter"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const BASE = '';
|
||||
|
||||
function toast(msg, type) {
|
||||
const t = document.createElement('div');
|
||||
t.className = 'toast ' + type;
|
||||
t.textContent = msg;
|
||||
document.body.appendChild(t);
|
||||
setTimeout(() => t.remove(), 2500);
|
||||
}
|
||||
|
||||
async function api(method, url, body) {
|
||||
const opt = { method, headers: {} };
|
||||
if (body) { opt.headers['Content-Type'] = 'application/json'; opt.body = JSON.stringify(body); }
|
||||
const resp = await fetch(BASE + url, opt);
|
||||
if (!resp.ok) { const t = await resp.text(); throw new Error(t || resp.statusText); }
|
||||
const json = await resp.json();
|
||||
// 解包 GoFrame 标准响应: {"code":0,"message":"success","data":{...}}
|
||||
if (json.code !== undefined && json.data !== undefined) {
|
||||
if (json.code !== 0) { throw new Error(json.message || '请求失败'); }
|
||||
return json.data;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
const P = { platform: '/datasource/platform/controller', iface: '/api/interface/controller' };
|
||||
|
||||
function switchTab(name) {
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
||||
document.querySelector('.tab[onclick*="' + name + '"]').classList.add('active');
|
||||
document.getElementById(name + '-panel').classList.add('active');
|
||||
if (name === 'platforms') loadPlatforms();
|
||||
else loadInterfaces();
|
||||
}
|
||||
|
||||
// ========== 平台管理 ==========
|
||||
async function loadPlatforms() {
|
||||
document.getElementById('platform-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', P.platform + '/listDatasourcePlatforms?page=1&pageSize=100');
|
||||
document.getElementById('platform-loading').style.display = 'none';
|
||||
if (!data.list || data.list.length === 0) {
|
||||
document.getElementById('platform-list').innerHTML = '<div class="empty">暂无平台,点击"新建平台"添加</div>';
|
||||
return;
|
||||
}
|
||||
let html = '<table><thead><tr><th style="width:30px"></th><th>编码</th><th>名称</th><th>API地址</th><th>认证</th><th>状态</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const p of data.list) {
|
||||
const sc = p.status === 'ACTIVE' ? 'active' : 'inactive';
|
||||
html += '<tr class="platform-row" onclick="togglePlatform(' + p.id + ')">' +
|
||||
'<td><span class="expand-icon" id="expand-' + p.id + '">▶</span></td>' +
|
||||
'<td><strong>' + esc(p.platformCode) + '</strong></td>' +
|
||||
'<td>' + esc(p.platformName) + '</td>' +
|
||||
'<td style="font-size:12px;color:#666">' + esc(p.apiBaseUrl || '-') + '</td>' +
|
||||
'<td>' + esc(p.authType || '-') + '</td>' +
|
||||
'<td><span class="badge ' + sc + '">' + p.status + '</span></td>' +
|
||||
'<td class="actions" onclick="event.stopPropagation()">' +
|
||||
'<button class="btn btn-sm btn-outline" onclick="openPlatformModal(' + p.id + ')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deletePlatform(' + p.id + ')">删除</button></td></tr>' +
|
||||
'<tr class="interface-detail" id="ifaces-' + p.id + '"><td colspan="7"><div class="interface-detail-inner" id="ifaces-content-' + p.id + '">点击展开...</div></td></tr>';
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
document.getElementById('platform-list').innerHTML = html;
|
||||
} catch (e) { document.getElementById('platform-loading').style.display = 'none'; toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function togglePlatform(id) {
|
||||
const row = document.getElementById('ifaces-' + id);
|
||||
const icon = document.getElementById('expand-' + id);
|
||||
row.classList.toggle('open');
|
||||
icon.classList.toggle('open');
|
||||
if (row.classList.contains('open') && document.getElementById('ifaces-content-' + id).textContent === '点击展开...') {
|
||||
try {
|
||||
const data = await api('GET', P.iface + '/listApiInterfaces?platformId=' + id + '&page=1&pageSize=100');
|
||||
const content = document.getElementById('ifaces-content-' + id);
|
||||
if (!data.list || data.list.length === 0) { content.innerHTML = '<div class="empty" style="padding:8px">暂无接口</div>'; return; }
|
||||
let h = '<table class="sub-table"><thead><tr><th>编码</th><th>名称</th><th>URL</th><th>方法</th><th>状态</th><th>请求配置</th><th>响应配置</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const fi of data.list) {
|
||||
h += '<tr><td>' + esc(fi.code) + '</td><td>' + esc(fi.name) + '</td><td style="font-size:11px">' + esc(fi.url) + '</td>' +
|
||||
'<td><span class="badge ' + (fi.method === 'GET' ? 'active' : '') + '">' + fi.method + '</span></td>' +
|
||||
'<td><span class="badge ' + (fi.status === 'active' ? 'active' : 'inactive') + '">' + fi.status + '</span></td>' +
|
||||
'<td class="json-preview" title="' + esc(JSON.stringify(fi.requestConfig||{})) + '">' + esc(JSON.stringify(fi.requestConfig||{}).substring(0,25)) + '</td>' +
|
||||
'<td class="json-preview" title="' + esc(JSON.stringify(fi.responseConfig||{})) + '">' + esc(JSON.stringify(fi.responseConfig||{}).substring(0,25)) + '</td>' +
|
||||
'<td class="actions"><button class="btn btn-sm btn-outline" onclick="event.stopPropagation();openInterfaceModal(' + fi.id + ')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="event.stopPropagation();deleteInterface(' + fi.id + ')">删</button></td></tr>';
|
||||
}
|
||||
h += '</tbody></table>';
|
||||
content.innerHTML = h;
|
||||
} catch (e) { document.getElementById('ifaces-content-' + id).textContent = '加载失败'; }
|
||||
}
|
||||
}
|
||||
|
||||
async function deletePlatform(id) {
|
||||
if (!confirm('确定删除此平台?')) return;
|
||||
try { await api('DELETE', P.platform + '/deleteDatasourcePlatform?id=' + id); toast('删除成功', 'success'); loadPlatforms(); }
|
||||
catch (e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ========== 接口管理 ==========
|
||||
async function loadInterfaces() {
|
||||
document.getElementById('interface-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', P.iface + '/listApiInterfaces?page=1&pageSize=100');
|
||||
document.getElementById('interface-loading').style.display = 'none';
|
||||
if (!data.list || data.list.length === 0) {
|
||||
document.getElementById('interface-list').innerHTML = '<div class="empty">暂无接口,点击"新建接口"添加</div>';
|
||||
return;
|
||||
}
|
||||
let html = '<table><thead><tr><th>编码</th><th>名称</th><th>URL</th><th>方法</th><th>状态</th><th>平台</th><th>请求配置</th><th>响应配置</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const fi of data.list) {
|
||||
html += '<tr><td><strong>' + esc(fi.code) + '</strong></td><td>' + esc(fi.name) + '</td>' +
|
||||
'<td style="font-size:12px;color:#666">' + esc(fi.url) + '</td>' +
|
||||
'<td><span class="badge ' + (fi.method === 'GET' ? 'active' : '') + '">' + fi.method + '</span></td>' +
|
||||
'<td><span class="badge ' + (fi.status === 'active' ? 'active' : 'inactive') + '">' + fi.status + '</span></td>' +
|
||||
'<td>' + esc(fi.platformName || '-') + '</td>' +
|
||||
'<td class="json-preview" title="' + esc(JSON.stringify(fi.requestConfig||{})) + '">' + esc(JSON.stringify(fi.requestConfig||{}).substring(0,30)) + '</td>' +
|
||||
'<td class="json-preview" title="' + esc(JSON.stringify(fi.responseConfig||{})) + '">' + esc(JSON.stringify(fi.responseConfig||{}).substring(0,30)) + '</td>' +
|
||||
'<td class="actions"><button class="btn btn-sm btn-outline" onclick="openInterfaceModal(' + fi.id + ')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deleteInterface(' + fi.id + ')">删除</button></td></tr>';
|
||||
}
|
||||
html += '</tbody></table>';
|
||||
document.getElementById('interface-list').innerHTML = html;
|
||||
} catch (e) { document.getElementById('interface-loading').style.display = 'none'; toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteInterface(id) {
|
||||
if (!confirm('确定删除此接口?')) return;
|
||||
try { await api('DELETE', P.iface + '/deleteApiInterface?id=' + id); toast('删除成功', 'success'); loadInterfaces(); loadPlatforms(); }
|
||||
catch (e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ========== 平台表单 ==========
|
||||
async function openPlatformModal(id) {
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
try {
|
||||
if (id) {
|
||||
const data = await api('GET', P.platform + '/getDatasourcePlatform?id=' + id);
|
||||
title.textContent = '编辑平台';
|
||||
body.innerHTML = pfHTML(data);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveP(' + id + ')">保存</button>';
|
||||
} else {
|
||||
title.textContent = '新建平台';
|
||||
body.innerHTML = pfHTML(null);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveP(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
} catch (e) { toast('加载平台详情失败: ' + e.message, 'error'); }
|
||||
}
|
||||
function pfHTML(d) {
|
||||
d = d || {};
|
||||
const authCfg = d.authConfig ? JSON.stringify(d.authConfig, null, 2) : '{}';
|
||||
return '<div class="form-row"><div class="form-group"><label>编码 *</label><input id="f-pcode" value="' + esc(d.platformCode||'') + '"></div>' +
|
||||
'<div class="form-group"><label>名称 *</label><input id="f-pname" value="' + esc(d.platformName||'') + '"></div></div>' +
|
||||
'<div class="form-group"><label>描述</label><input id="f-pdesc" value="' + esc(d.description||'') + '"></div>' +
|
||||
'<div class="form-group"><label>API地址</label><input id="f-apiurl" value="' + esc(d.apiBaseUrl||'') + '"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>认证类型 *</label><select id="f-auth"><option value="OAUTH2"' + (d.authType==='OAUTH2'?' selected':'') + '>OAuth2</option><option value="TOKEN"' + (d.authType==='TOKEN'?' selected':'') + '>Token</option><option value="API_KEY"' + (d.authType==='API_KEY'?' selected':'') + '>API Key</option><option value="SIGN"' + (d.authType==='SIGN'?' selected':'') + '>签名</option></select></div>' +
|
||||
'<div class="form-group"><label>状态</label><select id="f-ps"><option value="ACTIVE"' + ((d.status||'ACTIVE')==='ACTIVE'?' selected':'') + '>启用</option><option value="INACTIVE"' + (d.status==='INACTIVE'?' selected':'') + '>停用</option></select></div></div>' +
|
||||
'<div class="form-group"><label>Token / access_token</label><input id="f-tk" value="' + esc(d.token||'') + '"></div>' +
|
||||
'<div class="form-group"><label>API Key</label><input id="f-apik" value="' + esc(d.apiKey||'') + '"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>Client ID</label><input id="f-cid" value="' + esc(d.clientId||'') + '"></div>' +
|
||||
'<div class="form-group"><label>Client Secret</label><input id="f-cs" value="' + esc(d.clientSecret||'') + '"></div></div>' +
|
||||
'<div class="form-group"><label>认证配置 JSON</label><textarea id="f-ac" rows="4">' + esc(authCfg) + '</textarea></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>限流/分钟</label><input id="f-rpm" type="number" value="' + (d.rateLimitPerMinute||60) + '"></div>' +
|
||||
'<div class="form-group"><label>超时(ms)</label><input id="f-to" type="number" value="' + (d.requestTimeoutMs||30000) + '"></div></div>';
|
||||
}
|
||||
async function saveP(id) {
|
||||
const body = {
|
||||
platformCode: v('f-pcode'), platformName: v('f-pname'), description: v('f-pdesc'),
|
||||
apiBaseUrl: v('f-apiurl'), authType: v('f-auth'), status: v('f-ps'),
|
||||
token: v('f-tk'), apiKey: v('f-apik'), clientId: v('f-cid'), clientSecret: v('f-cs'),
|
||||
rateLimitPerMinute: parseInt(v('f-rpm'))||60, requestTimeoutMs: parseInt(v('f-to'))||30000,
|
||||
};
|
||||
try { body.authConfig = JSON.parse(v('f-ac')); } catch(e) { toast('auth_config JSON 格式错误', 'error'); return; }
|
||||
try {
|
||||
if (id) { await api('PUT', P.platform + '/updateDatasourcePlatform', { ...body, id }); toast('更新成功', 'success'); }
|
||||
else { await api('POST', P.platform + '/createDatasourcePlatform', body); toast('创建成功', 'success'); }
|
||||
closeModal(); loadPlatforms();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ========== 接口表单 ==========
|
||||
async function openInterfaceModal(id) {
|
||||
try {
|
||||
const pl = await api('GET', P.platform + '/listDatasourcePlatforms?page=1&pageSize=100');
|
||||
const opts = (pl.list||[]).map(p => '<option value="' + p.id + '">' + esc(p.platformName) + '</option>').join('');
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
if (id) {
|
||||
const data = await api('GET', P.iface + '/getApiInterface?id=' + id);
|
||||
title.textContent = '编辑接口';
|
||||
body.innerHTML = iFormHTML(data, opts);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveI(' + id + ')">保存</button>';
|
||||
} else {
|
||||
title.textContent = '新建接口';
|
||||
body.innerHTML = iFormHTML(null, opts);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveI(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
} catch (e) { toast('加载接口详情失败: ' + e.message, 'error'); }
|
||||
}
|
||||
function iFormHTML(d, opts) {
|
||||
d = d || {};
|
||||
const pid = (d.platformId != null) ? d.platformId : '';
|
||||
const sel = opts.replace('value="' + pid + '"', 'value="' + pid + '" selected');
|
||||
return '<div class="form-group"><label>所属平台</label><select id="f-pid">' + sel + '</select></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>编码 *</label><input id="f-ic" value="' + esc(d.code||'') + '"></div>' +
|
||||
'<div class="form-group"><label>名称 *</label><input id="f-in" value="' + esc(d.name||'') + '"></div></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>URL *</label><input id="f-iu" value="' + esc(d.url||'') + '"></div>' +
|
||||
'<div class="form-group"><label>方法 *</label><select id="f-im"><option value="GET"' + ((d.method||'GET')==='GET'?' selected':'') + '>GET</option><option value="POST"' + (d.method==='POST'?' selected':'') + '>POST</option></select></div></div>' +
|
||||
'<div class="form-group"><label>请求配置 JSON</label><textarea id="f-rqc" rows="6">' + esc(d.requestConfig ? JSON.stringify(d.requestConfig, null, 2) : '{}') + '</textarea></div>' +
|
||||
'<div class="form-group"><label>响应配置 JSON</label><textarea id="f-rsc" rows="4">' + esc(d.responseConfig ? JSON.stringify(d.responseConfig, null, 2) : '{}') + '</textarea></div>' +
|
||||
'<div class="form-group"><label>表结构定义 JSON</label><textarea id="f-td" rows="6">' + esc(d.tableDefinition ? JSON.stringify(d.tableDefinition, null, 2) : '{}') + '</textarea></div>';
|
||||
}
|
||||
async function saveI(id) {
|
||||
let rqc, rsc, td;
|
||||
try { rqc = JSON.parse(v('f-rqc')); } catch(e) { toast('请求配置 JSON 错误', 'error'); return; }
|
||||
try { rsc = JSON.parse(v('f-rsc')); } catch(e) { toast('响应配置 JSON 错误', 'error'); return; }
|
||||
try { td = JSON.parse(v('f-td')); } catch(e) { toast('表结构定义 JSON 错误', 'error'); return; }
|
||||
const body = { platformId: parseInt(v('f-pid')), code: v('f-ic'), name: v('f-in'), url: v('f-iu'),
|
||||
method: v('f-im'), requestConfig: rqc, responseConfig: rsc, tableDefinition: td };
|
||||
try {
|
||||
if (id) { await api('PUT', P.iface + '/updateApiInterface', { ...body, id }); toast('更新成功', 'success'); }
|
||||
else { await api('POST', P.iface + '/createApiInterface', body); toast('创建成功', 'success'); }
|
||||
closeModal(); loadInterfaces(); loadPlatforms();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function closeModal() { document.getElementById('modalOverlay').classList.remove('open'); document.getElementById('modal').classList.remove('open'); }
|
||||
function v(id) { return document.getElementById(id).value; }
|
||||
function esc(s) { if (!s && s!==0) return ''; const d = document.createElement('div'); d.textContent = s; return d.innerHTML; }
|
||||
|
||||
loadPlatforms();
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -1,11 +1,11 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/dict"
|
||||
service "cid/service/dict"
|
||||
"context"
|
||||
dto "dataengine/model/dto/dict"
|
||||
service "dataengine/service/dict"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type datasourcePlatformController struct{}
|
||||
@@ -15,25 +15,25 @@ var DatasourcePlatform = new(datasourcePlatformController)
|
||||
|
||||
// CreateDatasourcePlatform 创建数据源平台
|
||||
func (c *datasourcePlatformController) CreateDatasourcePlatform(ctx context.Context, req *dto.CreateDatasourcePlatformReq) (res *dto.CreateDatasourcePlatformRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.DatasourcePlatform.Create(ctx, req)
|
||||
}
|
||||
|
||||
// ListDatasourcePlatforms 获取数据源平台列表
|
||||
func (c *datasourcePlatformController) ListDatasourcePlatforms(ctx context.Context, req *dto.ListDatasourcePlatformReq) (res *dto.ListDatasourcePlatformRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.DatasourcePlatform.List(ctx, req)
|
||||
}
|
||||
|
||||
// GetDatasourcePlatform 获取数据源平台详情
|
||||
func (c *datasourcePlatformController) GetDatasourcePlatform(ctx context.Context, req *dto.GetDatasourcePlatformReq) (res *dto.GetDatasourcePlatformRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.DatasourcePlatform.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// GetPlatformByCode 根据平台编码获取平台信息
|
||||
func (c *datasourcePlatformController) GetPlatformByCode(ctx context.Context, req *dto.GetPlatformByCodeReq) (res *dto.GetPlatformByCodeRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
platform, err := service.DatasourcePlatform.GetByPlatformCode(ctx, req.PlatformCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -45,27 +45,27 @@ func (c *datasourcePlatformController) GetPlatformByCode(ctx context.Context, re
|
||||
|
||||
// UpdateDatasourcePlatform 更新数据源平台
|
||||
func (c *datasourcePlatformController) UpdateDatasourcePlatform(ctx context.Context, req *dto.UpdateDatasourcePlatformReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.DatasourcePlatform.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateDatasourcePlatformStatus 更新数据源平台状态
|
||||
func (c *datasourcePlatformController) UpdateDatasourcePlatformStatus(ctx context.Context, req *dto.UpdateDatasourcePlatformStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.DatasourcePlatform.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteDatasourcePlatform 删除数据源平台
|
||||
func (c *datasourcePlatformController) DeleteDatasourcePlatform(ctx context.Context, req *dto.DeleteDatasourcePlatformReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.DatasourcePlatform.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// GetPlatformStatistics 获取平台统计信息
|
||||
func (c *datasourcePlatformController) GetPlatformStatistics(ctx context.Context, req *dto.GetPlatformStatisticsReq) (res *dto.GetPlatformStatisticsRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.DatasourcePlatform.GetStatistics(ctx)
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/dict"
|
||||
service "cid/service/dict"
|
||||
"context"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type fieldMappingConfigController struct{}
|
||||
|
||||
// FieldMappingConfig 字段映射配置控制器
|
||||
var FieldMappingConfig = new(fieldMappingConfigController)
|
||||
|
||||
// CreateFieldMappingConfig 创建字段映射配置
|
||||
func (c *fieldMappingConfigController) CreateFieldMappingConfig(ctx context.Context, req *dto.CreateFieldMappingConfigReq) (res *dto.CreateFieldMappingConfigRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.FieldMappingConfig.Create(ctx, req)
|
||||
}
|
||||
|
||||
// ListFieldMappingConfig 获取字段映射配置列表
|
||||
func (c *fieldMappingConfigController) ListFieldMappingConfig(ctx context.Context, req *dto.ListFieldMappingConfigReq) (res *dto.ListFieldMappingConfigRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.FieldMappingConfig.List(ctx, req)
|
||||
}
|
||||
|
||||
// GetFieldMappingConfig 获取字段映射配置详情
|
||||
func (c *fieldMappingConfigController) GetFieldMappingConfig(ctx context.Context, req *dto.GetFieldMappingConfigReq) (res *dto.GetFieldMappingConfigRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.FieldMappingConfig.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// UpdateFieldMappingConfig 更新字段映射配置
|
||||
func (c *fieldMappingConfigController) UpdateFieldMappingConfig(ctx context.Context, req *dto.UpdateFieldMappingConfigReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
err = service.FieldMappingConfig.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateFieldMappingConfigStatus 更新字段映射配置状态
|
||||
func (c *fieldMappingConfigController) UpdateFieldMappingConfigStatus(ctx context.Context, req *dto.UpdateFieldMappingConfigStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
err = service.FieldMappingConfig.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteFieldMappingConfig 删除字段映射配置
|
||||
func (c *fieldMappingConfigController) DeleteFieldMappingConfig(ctx context.Context, req *dto.DeleteFieldMappingConfigReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
err = service.FieldMappingConfig.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// QueryFieldMappingByVendorApi 根据厂商和接口查询字段映射
|
||||
func (c *fieldMappingConfigController) QueryFieldMappingByVendorApi(ctx context.Context, req *dto.QueryFieldMappingByVendorApiReq) (res *dto.QueryFieldMappingByVendorApiRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.FieldMappingConfig.QueryByVendorApi(ctx, req)
|
||||
}
|
||||
|
||||
// ValidateFieldMapping 验证字段映射配置
|
||||
func (c *fieldMappingConfigController) ValidateFieldMapping(ctx context.Context, req *dto.ValidateFieldMappingReq) (res *dto.ValidateFieldMappingRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
return service.FieldMappingConfig.Validate(ctx, req)
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
dto "cid/model/dto/dict"
|
||||
service "cid/service/dict"
|
||||
"context"
|
||||
dto "dataengine/model/dto/dict"
|
||||
service "dataengine/service/dict"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
)
|
||||
|
||||
type apiInterfaceController struct{}
|
||||
@@ -15,39 +15,39 @@ var ApiInterface = new(apiInterfaceController)
|
||||
|
||||
// CreateApiInterface 创建接口
|
||||
func (c *apiInterfaceController) CreateApiInterface(ctx context.Context, req *dto.CreateApiInterfaceReq) (res *dto.CreateApiInterfaceRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.ApiInterface.Create(ctx, req)
|
||||
}
|
||||
|
||||
// ListApiInterface 获取接口列表
|
||||
func (c *apiInterfaceController) ListApiInterface(ctx context.Context, req *dto.ListApiInterfaceReq) (res *dto.ListApiInterfaceRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.ApiInterface.List(ctx, req)
|
||||
}
|
||||
|
||||
// GetApiInterface 获取接口详情
|
||||
func (c *apiInterfaceController) GetApiInterface(ctx context.Context, req *dto.GetApiInterfaceReq) (res *dto.GetApiInterfaceRes, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
return service.ApiInterface.GetOne(ctx, req)
|
||||
}
|
||||
|
||||
// UpdateApiInterface 更新接口
|
||||
func (c *apiInterfaceController) UpdateApiInterface(ctx context.Context, req *dto.UpdateApiInterfaceReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.ApiInterface.Update(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// UpdateApiInterfaceStatus 更新接口状态
|
||||
func (c *apiInterfaceController) UpdateApiInterfaceStatus(ctx context.Context, req *dto.UpdateApiInterfaceStatusReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.ApiInterface.UpdateStatus(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteApiInterface 删除接口
|
||||
func (c *apiInterfaceController) DeleteApiInterface(ctx context.Context, req *dto.DeleteApiInterfaceReq) (res *beans.ResponseEmpty, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
err = service.ApiInterface.Delete(ctx, req)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
dto "dataengine/model/dto/public"
|
||||
svc "dataengine/service/public"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type publicQueryController struct{}
|
||||
|
||||
// PublicQuery 公共查询控制器
|
||||
var PublicQuery = new(publicQueryController)
|
||||
|
||||
// QueryReq 查询请求(包含空结构用于路径)
|
||||
type ClearCacheReq struct {
|
||||
g.Meta `path:"/public/cache/clear" method:"delete" tags:"公共查询" summary:"清除查询缓存"`
|
||||
}
|
||||
|
||||
// Query 执行公共查询
|
||||
func (c *publicQueryController) Query(ctx context.Context, req *dto.QueryReq) (res *dto.QueryRes, err error) {
|
||||
return svc.PublicQuery.Query(ctx, req)
|
||||
}
|
||||
|
||||
// GetTableList 获取可查询表列表
|
||||
func (c *publicQueryController) GetTableList(ctx context.Context, req *dto.TableListReq) (res *dto.TableListRes, err error) {
|
||||
return svc.PublicQuery.GetTableList(ctx)
|
||||
}
|
||||
|
||||
// GetColumnList 获取表字段列表
|
||||
func (c *publicQueryController) GetColumnList(ctx context.Context, req *dto.ColumnListReq) (res *dto.ColumnListRes, err error) {
|
||||
return svc.PublicQuery.GetColumnList(ctx, req.Table)
|
||||
}
|
||||
|
||||
// ClearCache 清除查询缓存(管理接口)
|
||||
func (c *publicQueryController) ClearCache(ctx context.Context, req *ClearCacheReq) (res *dto.TableListRes, err error) {
|
||||
svc.PublicQuery.ClearTableCache()
|
||||
return &dto.TableListRes{List: []dto.TableInfo{}}, nil
|
||||
}
|
||||
@@ -0,0 +1,968 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/net/ghttp"
|
||||
)
|
||||
|
||||
// ReportAdminPage 报表引擎管理页面
|
||||
func ReportAdminPage(r *ghttp.Request) {
|
||||
r.Response.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
r.Response.Write(reportAdminHTML)
|
||||
r.Exit()
|
||||
}
|
||||
|
||||
var reportAdminHTML = `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>报表引擎管理</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; background: #f0f2f5; color: #333; }
|
||||
.header { background: #001529; color: #fff; padding: 12px 24px; display: flex; align-items: center; gap: 16px; }
|
||||
.header h1 { font-size: 18px; font-weight: 600; }
|
||||
.header a { color: #91a7ff; text-decoration: none; font-size: 13px; }
|
||||
.container { max-width: 1500px; margin: 0 auto; padding: 16px; }
|
||||
.tabs { display: flex; gap: 0; margin-bottom: 16px; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.06); }
|
||||
.tab { padding: 10px 24px; cursor: pointer; font-size: 13px; color: #666; border-bottom: 2px solid transparent; transition: all .2s; }
|
||||
.tab:hover { color: #1890ff; }
|
||||
.tab.active { color: #1890ff; border-bottom-color: #1890ff; font-weight: 600; }
|
||||
.panel { display: none; }
|
||||
.panel.active { display: block; }
|
||||
.toolbar { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; flex-wrap: wrap; gap: 8px; }
|
||||
.toolbar h2 { font-size: 15px; color: #1a1a2e; }
|
||||
.toolbar-left { display: flex; align-items: center; gap: 8px; }
|
||||
.toolbar-right { display: flex; gap: 6px; }
|
||||
.btn { display: inline-flex; align-items: center; gap: 4px; padding: 6px 14px; border: none; border-radius: 6px; font-size: 12px; cursor: pointer; transition: all .2s; white-space: nowrap; }
|
||||
.btn-primary { background: #1890ff; color: #fff; }
|
||||
.btn-primary:hover { background: #40a9ff; }
|
||||
.btn-danger { background: #ff4d4f; color: #fff; }
|
||||
.btn-danger:hover { background: #ff7875; }
|
||||
.btn-success { background: #52c41a; color: #fff; }
|
||||
.btn-success:hover { background: #73d13d; }
|
||||
.btn-warning { background: #faad14; color: #fff; }
|
||||
.btn-warning:hover { background: #ffc53d; }
|
||||
.btn-sm { padding: 4px 10px; font-size: 11px; }
|
||||
.btn-outline { background: #fff; border: 1px solid #d9d9d9; color: #333; }
|
||||
.btn-outline:hover { border-color: #1890ff; color: #1890ff; }
|
||||
table { width: 100%; background: #fff; border-radius: 8px; overflow: hidden; box-shadow: 0 1px 4px rgba(0,0,0,0.06); border-collapse: collapse; }
|
||||
th { background: #fafafa; padding: 10px 14px; text-align: left; font-size: 12px; color: #666; font-weight: 600; border-bottom: 1px solid #f0f0f0; }
|
||||
td { padding: 10px 14px; font-size: 12px; border-bottom: 1px solid #f0f0f0; }
|
||||
tr:hover td { background: #fafafa; }
|
||||
.badge { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; font-weight: 500; }
|
||||
.badge.active { background: #f6ffed; color: #52c41a; border: 1px solid #b7eb8f; }
|
||||
.badge.inactive { background: #fff2f0; color: #ff4d4f; border: 1px solid #ffccc7; }
|
||||
.badge.info { background: #e6f7ff; color: #1890ff; border: 1px solid #91d5ff; }
|
||||
.actions { display: flex; gap: 4px; }
|
||||
.modal-overlay { display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.45); z-index: 1000; }
|
||||
.modal-overlay.open { display: block; }
|
||||
.modal { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background: #fff; border-radius: 8px; width: 720px; max-width: 95%; max-height: 88vh; overflow-y: auto; z-index: 1001; box-shadow: 0 4px 24px rgba(0,0,0,0.15); }
|
||||
.modal.open { display: block; }
|
||||
.modal-header { padding: 14px 20px; border-bottom: 1px solid #f0f0f0; display: flex; justify-content: space-between; align-items: center; position: sticky; top: 0; background: #fff; z-index: 1; }
|
||||
.modal-header h3 { font-size: 15px; }
|
||||
.modal-close { background: none; border: none; font-size: 20px; cursor: pointer; color: #999; }
|
||||
.modal-body { padding: 20px; }
|
||||
.modal-footer { padding: 12px 20px; border-top: 1px solid #f0f0f0; display: flex; justify-content: flex-end; gap: 8px; position: sticky; bottom: 0; background: #fff; }
|
||||
.form-group { margin-bottom: 12px; }
|
||||
.form-group label { display: block; font-size: 12px; color: #666; margin-bottom: 4px; font-weight: 500; }
|
||||
.form-group input, .form-group select, .form-group textarea { width: 100%; padding: 7px 10px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 12px; outline: none; transition: border .2s; }
|
||||
.form-group input:focus, .form-group select:focus, .form-group textarea:focus { border-color: #1890ff; box-shadow: 0 0 0 2px rgba(24,144,255,0.1); }
|
||||
.form-group textarea { font-family: "Fira Code", "Consolas", monospace; font-size: 11px; min-height: 60px; resize: vertical; }
|
||||
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
|
||||
.form-row-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 10px; }
|
||||
.form-inline { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; }
|
||||
.form-inline .form-group { margin-bottom: 0; flex: 1; min-width: 150px; }
|
||||
.checkbox-group { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.checkbox-group label { display: flex; align-items: center; gap: 4px; font-size: 12px; cursor: pointer; font-weight: normal; }
|
||||
.select-biz { padding: 7px 10px; border: 1px solid #d9d9d9; border-radius: 6px; font-size: 12px; }
|
||||
.loading { text-align: center; padding: 40px; color: #999; }
|
||||
.empty { text-align: center; padding: 32px; color: #999; font-size: 13px; }
|
||||
.toast { position: fixed; top: 16px; right: 16px; padding: 10px 20px; border-radius: 6px; color: #fff; font-size: 13px; z-index: 2000; animation: slideIn .3s; }
|
||||
.toast.success { background: #52c41a; }
|
||||
.toast.error { background: #ff4d4f; }
|
||||
.toast.info { background: #1890ff; }
|
||||
@keyframes slideIn { from { transform: translateX(100%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }
|
||||
.result-box { margin-top: 12px; background: #fafafa; border-radius: 6px; padding: 12px; max-height: 400px; overflow: auto; font-size: 12px; font-family: monospace; white-space: pre-wrap; }
|
||||
.query-section { background: #fff; border-radius: 8px; padding: 16px; box-shadow: 0 1px 4px rgba(0,0,0,0.06); margin-bottom: 12px; }
|
||||
.query-section h3 { font-size: 14px; margin-bottom: 10px; color: #333; }
|
||||
.chip { display: inline-flex; align-items: center; gap: 4px; padding: 2px 10px; background: #f0f5ff; border: 1px solid #adc6ff; border-radius: 12px; font-size: 11px; cursor: pointer; }
|
||||
.chip.selected { background: #1890ff; color: #fff; border-color: #1890ff; }
|
||||
.chip-group { display: flex; flex-wrap: wrap; gap: 6px; }
|
||||
.field-tag { display: inline-flex; align-items: center; background: #f6ffed; border: 1px solid #b7eb8f; border-radius: 4px; padding: 2px 8px; font-size: 11px; margin: 2px; }
|
||||
.field-tag .remove { margin-left: 4px; cursor: pointer; color: #ff4d4f; font-weight: bold; }
|
||||
.add-filter-row { display: grid; grid-template-columns: 1fr 1fr 1.5fr 40px; gap: 6px; align-items: end; margin-bottom: 6px; }
|
||||
.section-label { font-size: 12px; color: #999; margin-bottom: 6px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<h1>📊 报表引擎管理</h1>
|
||||
<a href="/admin">← 返回管理后台</a>
|
||||
</div>
|
||||
<div class="container">
|
||||
|
||||
<div class="tabs">
|
||||
<div class="tab active" onclick="switchTab('business',this)">业务管理</div>
|
||||
<div class="tab" onclick="switchTab('report',this)">报表配置</div>
|
||||
<div class="tab" onclick="switchTab('field',this)">字段配置</div>
|
||||
<div class="tab" onclick="switchTab('extract',this)">抽取配置</div>
|
||||
<div class="tab" onclick="switchTab('query',this)">数据查询</div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 业务管理 ========== -->
|
||||
<div id="tab-business" class="panel active">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left"><h2>业务列表</h2></div>
|
||||
<div class="toolbar-right">
|
||||
<button class="btn btn-outline btn-sm" onclick="initTables()">初始化系统表</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="openBizModal()">+ 新建业务</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="biz-loading" class="loading">加载中...</div>
|
||||
<div id="biz-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 报表配置 ========== -->
|
||||
<div id="tab-report" class="panel">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<h2>报表列表</h2>
|
||||
<select class="select-biz" id="report-biz-select" onchange="loadReportList()">
|
||||
<option value="">-- 选择业务 --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="btn btn-primary btn-sm" onclick="openReportModal()">+ 新建报表</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="report-loading" class="loading" style="display:none">加载中...</div>
|
||||
<div id="report-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 字段配置 ========== -->
|
||||
<div id="tab-field" class="panel">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<h2>字段列表</h2>
|
||||
<select class="select-biz" id="field-biz-select" onchange="onFieldBizChange()">
|
||||
<option value="">-- 选择业务 --</option>
|
||||
</select>
|
||||
<select class="select-biz" id="field-report-select" onchange="loadFieldList()">
|
||||
<option value="">-- 选择报表 --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="btn btn-primary btn-sm" onclick="openFieldModal()">+ 新建字段</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="field-loading" class="loading" style="display:none">加载中...</div>
|
||||
<div id="field-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 抽取配置 ========== -->
|
||||
<div id="tab-extract" class="panel">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<h2>抽取配置列表</h2>
|
||||
<select class="select-biz" id="extract-biz-select" onchange="onExtractBizChange()">
|
||||
<option value="">-- 选择业务 --</option>
|
||||
</select>
|
||||
<select class="select-biz" id="extract-report-select" onchange="loadExtractList()">
|
||||
<option value="">-- 选择报表 --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="toolbar-right">
|
||||
<button class="btn btn-success btn-sm" onclick="execExtract()">执行抽取</button>
|
||||
<button class="btn btn-warning btn-sm" onclick="execBackfill()">批量回填</button>
|
||||
<button class="btn btn-primary btn-sm" onclick="openExtractModal()">+ 新建抽取配置</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="extract-loading" class="loading" style="display:none">加载中...</div>
|
||||
<div id="extract-list"></div>
|
||||
</div>
|
||||
|
||||
<!-- ========== 数据查询 ========== -->
|
||||
<div id="tab-query" class="panel">
|
||||
<div class="toolbar">
|
||||
<div class="toolbar-left">
|
||||
<h2>数据查询</h2>
|
||||
<select class="select-biz" id="query-biz-select" onchange="onQueryBizChange()">
|
||||
<option value="">-- 选择业务 --</option>
|
||||
</select>
|
||||
<select class="select-biz" id="query-report-select" onchange="loadQueryFields()">
|
||||
<option value="">-- 选择报表 --</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="query-content" style="display:none">
|
||||
<div class="query-section">
|
||||
<h3>📊 统计维度</h3>
|
||||
<div id="dimension-chips" class="chip-group"></div>
|
||||
</div>
|
||||
<div class="query-section">
|
||||
<h3>📈 统计指标(点击后选择聚合方式)</h3>
|
||||
<div id="indicator-area"></div>
|
||||
<div id="selected-indicators" style="margin-top:8px"></div>
|
||||
</div>
|
||||
<div class="query-section">
|
||||
<h3>🔍 筛选条件</h3>
|
||||
<div id="filter-area"></div>
|
||||
</div>
|
||||
<div class="query-section">
|
||||
<h3>📅 时间范围</h3>
|
||||
<div class="form-inline">
|
||||
<div class="form-group"><label>开始日期</label><input type="date" id="query-start-date"></div>
|
||||
<div class="form-group"><label>结束日期</label><input type="date" id="query-end-date"></div>
|
||||
<div class="form-group"><label>排序字段</label><input id="query-order-field" placeholder="如 total_amount"></div>
|
||||
<div class="form-group"><label>排序方向</label><select id="query-order-dir"><option value="DESC">降序</option><option value="ASC">升序</option></select></div>
|
||||
<div class="form-group"><label>每页</label><input type="number" id="query-page-size" value="20" style="width:70px"></div>
|
||||
<div><button class="btn btn-primary" onclick="executeQuery()" style="margin-top:22px">查询</button></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="query-result" class="result-box" style="display:none"></div>
|
||||
</div>
|
||||
<div id="query-empty" class="empty" style="display:none">请先选择业务和报表</div>
|
||||
</div>
|
||||
|
||||
</div><!-- container -->
|
||||
|
||||
<!-- Modal -->
|
||||
<div class="modal-overlay" id="modalOverlay" onclick="closeModal()"></div>
|
||||
<div class="modal" id="modal">
|
||||
<div class="modal-header"><h3 id="modalTitle"></h3><button class="modal-close" onclick="closeModal()">×</button></div>
|
||||
<div class="modal-body" id="modalBody"></div>
|
||||
<div class="modal-footer" id="modalFooter"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
const BASE = '';
|
||||
let state = { businesses: [], currentBiz: null };
|
||||
|
||||
function toast(msg, type) {
|
||||
const t = document.createElement('div');
|
||||
t.className = 'toast ' + (type||'info');
|
||||
t.textContent = msg;
|
||||
document.body.appendChild(t);
|
||||
setTimeout(() => t.remove(), 2500);
|
||||
}
|
||||
|
||||
async function api(method, url, body) {
|
||||
const opt = { method, headers: {} };
|
||||
if (body) { opt.headers['Content-Type'] = 'application/json'; opt.body = JSON.stringify(body); }
|
||||
const resp = await fetch(BASE + url, opt);
|
||||
if (!resp.ok) { const t = await resp.text(); throw new Error(t || resp.statusText); }
|
||||
const json = await resp.json();
|
||||
console.log('[API] ' + method + ' ' + url, 'raw response:', json);
|
||||
// GoFrame MiddlewareHandlerResponse 包裹: {code:0, data: <returnValue>}
|
||||
// returnValue 可能是 {data: {...}} 或 {list: [...]} 等
|
||||
if (json.code !== undefined && json.data !== undefined) {
|
||||
if (json.code !== 0) throw new Error(json.message || '请求失败');
|
||||
console.log('[API] extracted data:', json.data);
|
||||
// 兼容 GoFrame 的 data 可能包含首字母大写的 Go 字段名
|
||||
// 如 getBusinessRes{Data: ...} 在某些版本可能序列化为 {"Data": ...}
|
||||
// 此时尝试统一:若有 Data 字段且无 data 字段,取 Data
|
||||
if (json.data && json.data.Data !== undefined && json.data.data === undefined) {
|
||||
return json.data.Data;
|
||||
}
|
||||
return json.data;
|
||||
}
|
||||
return json;
|
||||
}
|
||||
|
||||
function esc(s) {
|
||||
if (s == null || s === undefined) return '';
|
||||
if (typeof s === 'object') s = JSON.stringify(s);
|
||||
const d = document.createElement('div');
|
||||
d.textContent = String(s);
|
||||
return d.innerHTML;
|
||||
}
|
||||
function jsAttr(s) {
|
||||
// 用于 onclick 等 JS 属性中的字符串值转义(替换反斜杠和单引号)
|
||||
if (s == null || s === undefined) return '';
|
||||
return String(s).replace(/\\/g, '\\\\').replace(/'/g, "\\'");
|
||||
}
|
||||
function v(id) { return document.getElementById(id).value; }
|
||||
|
||||
async function initTables() {
|
||||
try {
|
||||
await api('POST', '/report/initTables');
|
||||
toast('系统表初始化完成', 'success');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 导航 ====================
|
||||
function switchTab(name, tabEl) {
|
||||
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
|
||||
document.querySelectorAll('.panel').forEach(p => p.classList.remove('active'));
|
||||
if (tabEl) tabEl.classList.add('active');
|
||||
document.getElementById('tab-' + name).classList.add('active');
|
||||
if (name === 'business') loadBizList();
|
||||
else if (name === 'report') loadBizSelects().then(() => loadReportList());
|
||||
else if (name === 'field') loadBizSelects().then(() => onFieldBizChange());
|
||||
else if (name === 'extract') loadBizSelects().then(() => onExtractBizChange());
|
||||
else if (name === 'query') loadBizSelects().then(() => { document.getElementById('query-content').style.display='none'; document.getElementById('query-empty').style.display='block'; });
|
||||
}
|
||||
|
||||
// ==================== 业务选择器 ====================
|
||||
async function loadBizSelects() {
|
||||
try {
|
||||
const data = await api('GET', '/report/businesses');
|
||||
state.businesses = data.list || [];
|
||||
const optHtml = '<option value="">-- 选择业务 --</option>' + state.businesses.map(b => '<option value="'+b.businessCode+'">'+esc(b.businessName)+' ('+b.businessCode+')</option>').join('');
|
||||
['report-biz-select','field-biz-select','extract-biz-select','query-biz-select'].forEach(id => {
|
||||
const sel = document.getElementById(id);
|
||||
const oldVal = sel.value;
|
||||
sel.innerHTML = optHtml;
|
||||
if (oldVal) sel.value = oldVal;
|
||||
});
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function onFieldBizChange() {
|
||||
const bc = v('field-biz-select');
|
||||
const rs = document.getElementById('field-report-select');
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>';
|
||||
if (!bc) return;
|
||||
try {
|
||||
const data = await api('GET', '/report/reports?businessCode=' + bc);
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>' + (data.list||[]).map(r => '<option value="'+r.reportCode+'">'+esc(r.reportName)+' ('+r.reportCode+')</option>').join('');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function onExtractBizChange() {
|
||||
const bc = v('extract-biz-select');
|
||||
const rs = document.getElementById('extract-report-select');
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>';
|
||||
if (!bc) return;
|
||||
try {
|
||||
const data = await api('GET', '/report/reports?businessCode=' + bc);
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>' + (data.list||[]).map(r => '<option value="'+r.reportCode+'">'+esc(r.reportName)+' ('+r.reportCode+')</option>').join('');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 业务 CRUD ====================
|
||||
async function loadBizList() {
|
||||
document.getElementById('biz-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', '/report/businesses');
|
||||
document.getElementById('biz-loading').style.display = 'none';
|
||||
const list = data.list || [];
|
||||
if (list.length === 0) {
|
||||
document.getElementById('biz-list').innerHTML = '<div class="empty">暂无业务,点击"新建业务"添加</div>';
|
||||
return;
|
||||
}
|
||||
let h = '<table><thead><tr><th>ID</th><th>编码</th><th>名称</th><th>描述</th><th>创建人</th><th>状态</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const b of list) {
|
||||
const sc = b.status === 'ACTIVE' ? 'active' : 'inactive';
|
||||
h += '<tr><td>'+b.id+'</td><td><strong>'+esc(b.businessCode)+'</strong></td><td>'+esc(b.businessName)+'</td>' +
|
||||
'<td style="max-width:200px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">'+esc(b.description||'-')+'</td>' +
|
||||
'<td>'+esc(b.creator||'-')+'</td>' +
|
||||
'<td><span class="badge '+sc+'">'+(b.status||'ACTIVE')+'</span></td>' +
|
||||
'<td class="actions">' +
|
||||
'<button class="btn btn-sm btn-outline" onclick="openBizModal('+b.id+')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deleteBiz('+b.id+')">删除</button></td></tr>';
|
||||
}
|
||||
h += '</tbody></table>';
|
||||
document.getElementById('biz-list').innerHTML = h;
|
||||
} catch(e) {
|
||||
document.getElementById('biz-loading').style.display = 'none';
|
||||
toast(e.message, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
function bizFormHTML(d) {
|
||||
d = d || {};
|
||||
return '<div class="form-row"><div class="form-group"><label>业务编码 *</label><input id="f-bcode" value="'+esc(d.businessCode||'')+'" placeholder="如 KUAISHOU"></div>' +
|
||||
'<div class="form-group"><label>业务名称 *</label><input id="f-bname" value="'+esc(d.businessName||'')+'" placeholder="如 快手电商"></div></div>' +
|
||||
'<div class="form-group"><label>描述</label><input id="f-bdesc" value="'+esc(d.description||'')+'" placeholder="业务描述"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>操作人</label><input id="f-bop" value="'+esc(d.creator||'admin')+'"></div>' +
|
||||
'<div class="form-group"><label>状态</label><select id="f-bs"><option value="ACTIVE"'+((d.status||'ACTIVE')==='ACTIVE'?' selected':'')+'>启用</option><option value="INACTIVE"'+(d.status==='INACTIVE'?' selected':'')+'>停用</option></select></div></div>';
|
||||
}
|
||||
|
||||
async function openBizModal(id) {
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
if (id) {
|
||||
try {
|
||||
const res = await api('GET', '/report/business?id=' + id);
|
||||
const d = res && res.data !== undefined ? res.data : (res && res.Data !== undefined ? res.Data : res);
|
||||
console.log('[BizEdit] id=' + id + ' raw:', res, 'data:', d);
|
||||
if (!d || !d.id) { toast('未获取到业务数据(ID='+id+')', 'error'); return; }
|
||||
title.textContent = '编辑业务';
|
||||
body.innerHTML = bizFormHTML(d);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveBiz('+id+')">保存</button>';
|
||||
} catch(e) { toast(e.message, 'error'); return; }
|
||||
} else {
|
||||
title.textContent = '新建业务';
|
||||
body.innerHTML = bizFormHTML(null);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveBiz(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
}
|
||||
|
||||
async function saveBiz(id) {
|
||||
const body = {
|
||||
businessCode: v('f-bcode'), businessName: v('f-bname'),
|
||||
description: v('f-bdesc'), status: v('f-bs'), operator: v('f-bop'),
|
||||
};
|
||||
if (id) body.id = id;
|
||||
try {
|
||||
await api('POST', '/report/business/save', body);
|
||||
toast(id ? '更新成功' : '创建成功', 'success');
|
||||
closeModal(); loadBizList(); loadBizSelects();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteBiz(id) {
|
||||
if (!confirm('确定删除此业务?')) return;
|
||||
try { await api('DELETE', '/report/business?id='+id); toast('删除成功', 'success'); loadBizList(); loadBizSelects(); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 报表 CRUD ====================
|
||||
async function loadReportList() {
|
||||
const bc = v('report-biz-select');
|
||||
if (!bc) { document.getElementById('report-list').innerHTML = '<div class="empty">请先选择业务</div>'; return; }
|
||||
document.getElementById('report-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', '/report/reports?businessCode=' + bc);
|
||||
document.getElementById('report-loading').style.display = 'none';
|
||||
const list = data.list || [];
|
||||
if (list.length === 0) {
|
||||
document.getElementById('report-list').innerHTML = '<div class="empty">暂无报表</div>';
|
||||
return;
|
||||
}
|
||||
let h = '<table><thead><tr><th>ID</th><th>编码</th><th>名称</th><th>统计宽表</th><th>日期字段</th><th>冲突键</th><th>状态</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const r of list) {
|
||||
const sc = r.status === 'ACTIVE' ? 'active' : 'inactive';
|
||||
h += '<tr><td>'+r.id+'</td><td><strong>'+esc(r.reportCode)+'</strong></td><td>'+esc(r.reportName)+'</td>' +
|
||||
'<td style="font-family:monospace;font-size:11px">'+esc(r.statTableName)+'</td>' +
|
||||
'<td>'+esc(r.dateField||'stat_date')+'</td>' +
|
||||
'<td style="font-size:11px">'+esc((r.conflictKeys||[]).join(', '))+'</td>' +
|
||||
'<td><span class="badge '+sc+'">'+(r.status||'ACTIVE')+'</span></td>' +
|
||||
'<td class="actions">' +
|
||||
'<button class="btn btn-sm btn-outline" onclick="openReportModal('+r.id+')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deleteReport('+r.id+')">删除</button></td></tr>';
|
||||
}
|
||||
h += '</tbody></table>';
|
||||
document.getElementById('report-list').innerHTML = h;
|
||||
} catch(e) { document.getElementById('report-loading').style.display = 'none'; toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function reportFormHTML(d) {
|
||||
d = d || {};
|
||||
return '<div class="form-group"><label>所属业务</label><input id="f-rbiz" value="'+esc(d.businessCode||v('report-biz-select')||'')+'" readonly style="background:#f5f5f5"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>报表编码 *</label><input id="f-rcode" value="'+esc(d.reportCode||'')+'" placeholder="如 shop_daily_report"></div>' +
|
||||
'<div class="form-group"><label>报表名称 *</label><input id="f-rname" value="'+esc(d.reportName||'')+'" placeholder="如 店铺日报"></div></div>' +
|
||||
'<div class="form-group"><label>描述</label><input id="f-rdesc" value="'+esc(d.description||'')+'"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>统计宽表名 *</label><input id="f-rtable" value="'+esc(d.statTableName||'')+'" placeholder="如 stat_kuaishou_shop_daily"></div>' +
|
||||
'<div class="form-group"><label>统计宽表注释</label><input id="f-rtcomment" value="'+esc(d.statTableComment||'')+'"></div></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>日期字段</label><input id="f-rdate" value="'+esc(d.dateField||'stat_date')+'"></div>' +
|
||||
'<div class="form-group"><label>操作人</label><input id="f-rop" value="'+esc(d.creator||'admin')+'"></div></div>' +
|
||||
'<div class="form-group"><label>冲突键(逗号分隔)</label><input id="f-rck" value="'+esc((d.conflictKeys||[]).join(','))+'"></div>' +
|
||||
'<div class="form-group"><label>主键(逗号分隔)</label><input id="f-rpk" value="'+esc((d.primaryKeys||['id']).join(','))+'"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>状态</label><select id="f-rs"><option value="ACTIVE"'+((d.status||'ACTIVE')==='ACTIVE'?' selected':'')+'>启用</option><option value="INACTIVE"'+(d.status==='INACTIVE'?' selected':'')+'>停用</option></select></div></div>';
|
||||
}
|
||||
|
||||
async function openReportModal(id) {
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
if (id) {
|
||||
try {
|
||||
const res = await api('GET', '/report/report?id=' + id);
|
||||
const d = res && res.data !== undefined ? res.data : (res && res.Data !== undefined ? res.Data : res);
|
||||
console.log('[ReportEdit] id=' + id + ' raw:', res, 'data:', d);
|
||||
if (!d || !d.id) { toast('未获取到报表数据(ID='+id+')', 'error'); return; }
|
||||
title.textContent = '编辑报表';
|
||||
body.innerHTML = reportFormHTML(d);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveReport('+id+')">保存</button>';
|
||||
} catch(e) { toast(e.message, 'error'); return; }
|
||||
} else {
|
||||
title.textContent = '新建报表';
|
||||
body.innerHTML = reportFormHTML(null);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveReport(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
}
|
||||
|
||||
async function saveReport(id) {
|
||||
const body = {
|
||||
businessCode: v('f-rbiz'), reportCode: v('f-rcode'), reportName: v('f-rname'),
|
||||
description: v('f-rdesc'), statTableName: v('f-rtable'), statTableComment: v('f-rtcomment'),
|
||||
dateField: v('f-rdate'), operator: v('f-rop'), status: v('f-rs'),
|
||||
conflictKeys: v('f-rck').split(',').map(s => s.trim()).filter(Boolean),
|
||||
primaryKeys: v('f-rpk').split(',').map(s => s.trim()).filter(Boolean),
|
||||
};
|
||||
if (id) body.id = id;
|
||||
try {
|
||||
await api('POST', '/report/report/save', body);
|
||||
toast(id ? '更新成功' : '创建成功', 'success');
|
||||
closeModal(); loadReportList();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteReport(id) {
|
||||
if (!confirm('确定删除此报表?')) return;
|
||||
try { await api('DELETE', '/report/report?id='+id); toast('删除成功', 'success'); loadReportList(); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 字段 CRUD ====================
|
||||
async function loadFieldList() {
|
||||
const bc = v('field-biz-select');
|
||||
const rc = v('field-report-select');
|
||||
if (!bc || !rc) { document.getElementById('field-list').innerHTML = '<div class="empty">请先选择业务和报表</div>'; return; }
|
||||
document.getElementById('field-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', '/report/fields?businessCode=' + bc + '&reportCode=' + rc);
|
||||
document.getElementById('field-loading').style.display = 'none';
|
||||
const dims = data.dimensions || [], inds = data.indicators || [], fils = data.filters || [];
|
||||
if (dims.length + inds.length + fils.length === 0) {
|
||||
document.getElementById('field-list').innerHTML = '<div class="empty">暂无字段</div>';
|
||||
return;
|
||||
}
|
||||
const allFields = [...dims.map(f => ({...f, _role: '维度'})), ...inds.map(f => ({...f, _role: '指标'})), ...fils.map(f => ({...f, _role: '筛选'}))];
|
||||
let h = '<table><thead><tr><th>ID</th><th>角色</th><th>编码</th><th>名称</th><th>类型</th><th>可聚合</th><th>默认聚合</th><th>分组</th><th>排序</th><th>状态</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const f of allFields) {
|
||||
const sc = f.status === 'ACTIVE' ? 'active' : 'inactive';
|
||||
h += '<tr><td>'+f.id+'</td><td><span class="badge '+(f.fieldRole==='DIMENSION'?'info':f.fieldRole==='INDICATOR'?'success':'')+'">'+f._role+'</span></td>' +
|
||||
'<td><strong>'+esc(f.fieldCode)+'</strong></td><td>'+esc(f.fieldName)+'</td>' +
|
||||
'<td>'+esc(f.fieldType)+'</td>' +
|
||||
'<td>'+(f.isAggregatable ? '✓' : '-')+'</td>' +
|
||||
'<td>'+esc(f.defaultAggregate||'-')+'</td>' +
|
||||
'<td>'+esc(f.groupName||'-')+'</td><td>'+f.sortOrder+'</td>' +
|
||||
'<td><span class="badge '+sc+'">'+(f.status||'ACTIVE')+'</span></td>' +
|
||||
'<td class="actions">' +
|
||||
'<button class="btn btn-sm btn-outline" onclick="openFieldModal('+f.id+')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deleteField('+f.id+')">删除</button></td></tr>';
|
||||
}
|
||||
h += '</tbody></table>';
|
||||
document.getElementById('field-list').innerHTML = h;
|
||||
} catch(e) { document.getElementById('field-loading').style.display = 'none'; toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function fieldFormHTML(d) {
|
||||
d = d || {};
|
||||
return '<div class="form-row"><div class="form-group"><label>所属业务</label><input id="f-fbiz" value="'+esc(d.businessCode||v('field-biz-select')||'')+'" readonly style="background:#f5f5f5"></div>' +
|
||||
'<div class="form-group"><label>所属报表</label><input id="f-frep" value="'+esc(d.reportCode||v('field-report-select')||'')+'" readonly style="background:#f5f5f5"></div></div>' +
|
||||
'<div class="form-row-3"><div class="form-group"><label>字段编码 *</label><input id="f-fcode" value="'+esc(d.fieldCode||'')+'" placeholder="如 order_amount"></div>' +
|
||||
'<div class="form-group"><label>字段名称 *</label><input id="f-fname" value="'+esc(d.fieldName||'')+'"></div>' +
|
||||
'<div class="form-group"><label>字段类型 *</label><select id="f-ftype"><option value="STRING"'+(d.fieldType==='STRING'||!d.fieldType?' selected':'')+'>STRING</option><option value="INT"'+(d.fieldType==='INT'?' selected':'')+'>INT</option><option value="FLOAT"'+(d.fieldType==='FLOAT'?' selected':'')+'>FLOAT</option><option value="DATE"'+(d.fieldType==='DATE'?' selected':'')+'>DATE</option><option value="DATETIME"'+(d.fieldType==='DATETIME'?' selected':'')+'>DATETIME</option><option value="JSONB"'+(d.fieldType==='JSONB'?' selected':'')+'>JSONB</option></select></div></div>' +
|
||||
'<div class="form-row-3"><div class="form-group"><label>字段角色 *</label><select id="f-frole"><option value="DIMENSION"'+(d.fieldRole==='DIMENSION'||!d.fieldRole?' selected':'')+'>维度(DIMENSION)</option><option value="INDICATOR"'+(d.fieldRole==='INDICATOR'?' selected':'')+'>指标(INDICATOR)</option><option value="FILTER"'+(d.fieldRole==='FILTER'?' selected':'')+'>筛选(FILTER)</option><option value="FILTER_ONLY"'+(d.fieldRole==='FILTER_ONLY'?' selected':'')+'>仅筛选(FILTER_ONLY)</option></select></div>' +
|
||||
'<div class="form-group"><label>数据存储类型</label><select id="f-fdtype"><option value="STRING"'+(d.dataType==='STRING'||!d.dataType?' selected':'')+'>STRING</option><option value="INT"'+(d.dataType==='INT'?' selected':'')+'>INT</option><option value="FLOAT"'+(d.dataType==='FLOAT'?' selected':'')+'>FLOAT</option><option value="DATE"'+(d.dataType==='DATE'?' selected':'')+'>DATE</option><option value="DATETIME"'+(d.dataType==='DATETIME'?' selected':'')+'>DATETIME</option><option value="JSONB"'+(d.dataType==='JSONB'?' selected':'')+'>JSONB</option></select></div>' +
|
||||
'<div class="form-group"><label>默认聚合</label><select id="f-faggr"><option value=""'+(!d.defaultAggregate?' selected':'')+'>无</option><option value="SUM"'+(d.defaultAggregate==='SUM'?' selected':'')+'>SUM</option><option value="COUNT"'+(d.defaultAggregate==='COUNT'?' selected':'')+'>COUNT</option><option value="AVG"'+(d.defaultAggregate==='AVG'?' selected':'')+'>AVG</option><option value="MAX"'+(d.defaultAggregate==='MAX'?' selected':'')+'>MAX</option><option value="MIN"'+(d.defaultAggregate==='MIN'?' selected':'')+'>MIN</option></select></div></div>' +
|
||||
'<div class="form-row-3"><div class="form-group"><label>分组名称</label><input id="f-fgroup" value="'+esc(d.groupName||'')+'"></div>' +
|
||||
'<div class="form-group"><label>排序号</label><input id="f-fsort" type="number" value="'+(d.sortOrder||0)+'"></div>' +
|
||||
'<div class="form-group"><label>单位</label><input id="f-funit" value="'+esc(d.unit||'')+'"></div></div>' +
|
||||
'<div class="form-group"><label>表达式(衍生字段如 {refund}/{order}*100)</label><input id="f-fexpr" value="'+esc(d.expression||'')+'"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>操作人</label><input id="f-fop" value="'+esc(d.creator||'admin')+'"></div>' +
|
||||
'<div class="form-group"><label>状态</label><select id="f-fs"><option value="ACTIVE"'+((d.status||'ACTIVE')==='ACTIVE'?' selected':'')+'>启用</option><option value="INACTIVE"'+(d.status==='INACTIVE'?' selected':'')+'>停用</option></select></div></div>' +
|
||||
'<div class="checkbox-group" style="margin-top:8px"><label><input type="checkbox" id="f-fagg" '+(d.isAggregatable===true?'checked':'')+'> 可聚合</label>' +
|
||||
'<label><input type="checkbox" id="f-ffilter" '+((d.isFilterable===undefined||d.isFilterable===true)?'checked':'')+'> 可筛选</label>' +
|
||||
'<label><input type="checkbox" id="f-fquery" '+((d.isQueryable===undefined||d.isQueryable===true)?'checked':'')+'> 可查询</label>' +
|
||||
'<label><input type="checkbox" id="f-fsortable" '+((d.isSortable===undefined||d.isSortable===true)?'checked':'')+'> 可排序</label></div>';
|
||||
}
|
||||
|
||||
async function openFieldModal(id) {
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
if (id) {
|
||||
try {
|
||||
const res = await api('GET', '/report/field?id=' + id);
|
||||
const d = res && res.data !== undefined ? res.data : (res && res.Data !== undefined ? res.Data : res);
|
||||
console.log('[FieldEdit] id=' + id + ' raw:', res, 'data:', d);
|
||||
if (!d || !d.id) { toast('未获取到字段数据(ID='+id+')', 'error'); return; }
|
||||
title.textContent = '编辑字段';
|
||||
body.innerHTML = fieldFormHTML(d);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveField('+id+')">保存</button>';
|
||||
} catch(e) { toast(e.message, 'error'); return; }
|
||||
} else {
|
||||
title.textContent = '新建字段';
|
||||
body.innerHTML = fieldFormHTML(null);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveField(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
}
|
||||
|
||||
function fieldVal(id) { const el = document.getElementById(id); return el ? (el.type==='checkbox'?el.checked:el.value) : ''; }
|
||||
|
||||
async function saveField(id) {
|
||||
const body = {
|
||||
businessCode: v('f-fbiz'), reportCode: v('f-frep'),
|
||||
fieldCode: v('f-fcode'), fieldName: v('f-fname'),
|
||||
fieldType: v('f-ftype'), dataType: v('f-fdtype'), fieldRole: v('f-frole'),
|
||||
defaultAggregate: v('f-faggr'), groupName: v('f-fgroup'),
|
||||
sortOrder: parseInt(v('f-fsort'))||0, unit: v('f-funit'),
|
||||
expression: v('f-fexpr'), operator: v('f-fop'), status: v('f-fs'),
|
||||
isAggregatable: fieldVal('f-fagg'),
|
||||
isFilterable: fieldVal('f-ffilter'),
|
||||
isQueryable: fieldVal('f-fquery'),
|
||||
isSortable: fieldVal('f-fsortable'),
|
||||
};
|
||||
if (id) body.id = id;
|
||||
try {
|
||||
await api('POST', '/report/field/save', body);
|
||||
toast(id ? '更新成功' : '创建成功', 'success');
|
||||
closeModal(); loadFieldList();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteField(id) {
|
||||
if (!confirm('确定删除此字段?')) return;
|
||||
try { await api('DELETE', '/report/field?id='+id); toast('删除成功', 'success'); loadFieldList(); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 抽取配置 CRUD ====================
|
||||
async function loadExtractList() {
|
||||
const bc = v('extract-biz-select');
|
||||
const rc = v('extract-report-select');
|
||||
if (!bc || !rc) { document.getElementById('extract-list').innerHTML = '<div class="empty">请先选择业务和报表</div>'; return; }
|
||||
document.getElementById('extract-loading').style.display = 'block';
|
||||
try {
|
||||
const data = await api('GET', '/report/extractConfigs?businessCode=' + bc + '&reportCode=' + rc);
|
||||
document.getElementById('extract-loading').style.display = 'none';
|
||||
const list = data.list || [];
|
||||
if (list.length === 0) {
|
||||
document.getElementById('extract-list').innerHTML = '<div class="empty">暂无抽取配置</div>';
|
||||
return;
|
||||
}
|
||||
let h = '<table><thead><tr><th>ID</th><th>编码</th><th>名称</th><th>源表</th><th>目标表</th><th>抽取模式</th><th>抽取类型</th><th>启用</th><th>操作</th></tr></thead><tbody>';
|
||||
for (const ec of list) {
|
||||
h += '<tr><td>'+ec.id+'</td><td><strong>'+esc(ec.extractCode)+'</strong></td><td>'+esc(ec.extractName)+'</td>' +
|
||||
'<td style="font-family:monospace;font-size:11px">'+esc(ec.sourceTableName||'-')+'</td>' +
|
||||
'<td style="font-family:monospace;font-size:11px">'+esc(ec.targetTableName||'-')+'</td>' +
|
||||
'<td><span class="badge '+(ec.extractMode==='AGGREGATE'?'info':'active')+'">'+(ec.extractMode||'DIRECT')+'</span></td>' +
|
||||
'<td><span class="badge active">'+(ec.extractType||'INCREMENTAL')+'</span></td>' +
|
||||
'<td>'+(ec.isEnabled ? '✓' : '✗')+'</td>' +
|
||||
'<td class="actions">' +
|
||||
'<button class="btn btn-sm btn-outline" onclick="openExtractModal('+ec.id+')">编辑</button>' +
|
||||
'<button class="btn btn-sm btn-danger" onclick="deleteExtract('+ec.id+')">删除</button></td></tr>';
|
||||
}
|
||||
h += '</tbody></table>';
|
||||
document.getElementById('extract-list').innerHTML = h;
|
||||
} catch(e) { document.getElementById('extract-loading').style.display = 'none'; toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function extractFormHTML(d) {
|
||||
d = d || {};
|
||||
return '<div class="form-row"><div class="form-group"><label>所属业务</label><input id="f-ebiz" value="'+esc(d.businessCode||v('extract-biz-select')||'')+'" readonly style="background:#f5f5f5"></div>' +
|
||||
'<div class="form-group"><label>所属报表</label><input id="f-erep" value="'+esc(d.reportCode||v('extract-report-select')||'')+'" readonly style="background:#f5f5f5"></div></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>抽取编码 *</label><input id="f-ecode" value="'+esc(d.extractCode||'')+'"></div>' +
|
||||
'<div class="form-group"><label>抽取名称 *</label><input id="f-ename" value="'+esc(d.extractName||'')+'"></div></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>源表名 *</label><input id="f-esrc" value="'+esc(d.sourceTableName||'')+'"></div>' +
|
||||
'<div class="form-group"><label>源表别名</label><input id="f-esrca" value="'+esc(d.sourceTableAlias||'')+'"></div></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>目标表名 *</label><input id="f-etgt" value="'+esc(d.targetTableName||'')+'"></div>' +
|
||||
'<div class="form-group"><label>增量依据字段</label><input id="f-ekey" value="'+esc(d.extractKeyField||'')+'"></div></div>' +
|
||||
'<div class="form-row-3"><div class="form-group"><label>抽取模式</label><select id="f-emode"><option value="DIRECT"'+(d.extractMode==='DIRECT'||!d.extractMode?' selected':'')+'>DIRECT</option><option value="AGGREGATE"'+(d.extractMode==='AGGREGATE'?' selected':'')+'>AGGREGATE</option></select></div>' +
|
||||
'<div class="form-group"><label>抽取类型</label><select id="f-etype"><option value="INCREMENTAL"'+(d.extractType==='INCREMENTAL'||!d.extractType?' selected':'')+'>增量(INCREMENTAL)</option><option value="FULL"'+(d.extractType==='FULL'?' selected':'')+'>全量(FULL)</option></select></div>' +
|
||||
'<div class="form-group"><label>批大小</label><input id="f-ebatch" type="number" value="'+(d.batchSize||1000)+'"></div></div>' +
|
||||
'<div class="form-group"><label>GROUP BY 字段(逗号分隔)</label><input id="f-egroup" value="'+esc((d.groupByFields||[]).join(','))+'"></div>' +
|
||||
'<div class="form-group"><label>过滤表达式</label><input id="f-efilter" value="'+esc(d.filterExpression||'')+'" placeholder="如 o.status != \'CANCELLED\'"></div>' +
|
||||
'<div class="form-group"><label>操作人</label><input id="f-eop" value="'+esc(d.creator||'admin')+'"></div>' +
|
||||
'<div class="form-row"><div class="form-group"><label>状态</label><select id="f-es"><option value="ACTIVE"'+((d.status||'ACTIVE')==='ACTIVE'?' selected':'')+'>启用</option><option value="INACTIVE"'+(d.status==='INACTIVE'?' selected':'')+'>停用</option></select></div>' +
|
||||
'<div class="form-group"><label><input type="checkbox" id="f-eenabled" '+((d.isEnabled===undefined||d.isEnabled===true)?'checked':'')+'> 启用抽取</label></div></div>';
|
||||
}
|
||||
|
||||
async function openExtractModal(id) {
|
||||
const title = document.getElementById('modalTitle');
|
||||
const body = document.getElementById('modalBody');
|
||||
const footer = document.getElementById('modalFooter');
|
||||
if (id) {
|
||||
try {
|
||||
const res = await api('GET', '/report/extractConfig?id=' + id);
|
||||
const d = res && res.data !== undefined ? res.data : (res && res.Data !== undefined ? res.Data : res);
|
||||
console.log('[ExtractEdit] id=' + id + ' raw:', res, 'data:', d);
|
||||
if (!d || !d.id) { toast('未获取到抽取配置数据(ID='+id+')', 'error'); return; }
|
||||
title.textContent = '编辑抽取配置';
|
||||
body.innerHTML = extractFormHTML(d);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveExtract('+id+')">保存</button>';
|
||||
} catch(e) { toast(e.message, 'error'); return; }
|
||||
} else {
|
||||
title.textContent = '新建抽取配置';
|
||||
body.innerHTML = extractFormHTML(null);
|
||||
footer.innerHTML = '<button class="btn btn-outline" onclick="closeModal()">取消</button><button class="btn btn-primary" onclick="saveExtract(0)">创建</button>';
|
||||
}
|
||||
document.getElementById('modalOverlay').classList.add('open');
|
||||
document.getElementById('modal').classList.add('open');
|
||||
}
|
||||
|
||||
async function saveExtract(id) {
|
||||
const body = {
|
||||
businessCode: v('f-ebiz'), reportCode: v('f-erep'),
|
||||
extractCode: v('f-ecode'), extractName: v('f-ename'),
|
||||
sourceTableName: v('f-esrc'), sourceTableAlias: v('f-esrca'),
|
||||
targetTableName: v('f-etgt'), extractKeyField: v('f-ekey'),
|
||||
extractMode: v('f-emode'), extractType: v('f-etype'),
|
||||
batchSize: parseInt(v('f-ebatch'))||1000,
|
||||
groupByFields: v('f-egroup').split(',').map(s => s.trim()).filter(Boolean),
|
||||
filterExpression: v('f-efilter'), operator: v('f-eop'),
|
||||
status: v('f-es'), isEnabled: document.getElementById('f-eenabled').checked,
|
||||
};
|
||||
if (id) body.id = id;
|
||||
try {
|
||||
await api('POST', '/report/extractConfig/save', body);
|
||||
toast(id ? '更新成功' : '创建成功', 'success');
|
||||
closeModal(); loadExtractList();
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function deleteExtract(id) {
|
||||
if (!confirm('确定删除此抽取配置?')) return;
|
||||
try { await api('DELETE', '/report/extractConfig?id='+id); toast('删除成功', 'success'); loadExtractList(); }
|
||||
catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function execExtract() {
|
||||
const bc = v('extract-biz-select');
|
||||
const rc = v('extract-report-select');
|
||||
if (!bc || !rc) { toast('请先选择业务和报表', 'error'); return; }
|
||||
const date = prompt('请输入统计日期 (yyyy-MM-dd):', new Date().toISOString().slice(0,10));
|
||||
if (!date) return;
|
||||
try {
|
||||
const res = await api('POST', '/report/extract', { businessCode: bc, reportCode: rc, statDate: date, executor: 'admin' });
|
||||
toast('抽取完成!成功: ' + res.successCount + ' 失败: ' + res.failCount + ' 耗时: ' + res.execTimeMs + 'ms', res.success ? 'success' : 'error');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
// ==================== 数据查询 ====================
|
||||
let queryCtx = { dimensions: [], indicators: [], filters: [], availableFields: null };
|
||||
|
||||
|
||||
async function execBackfill() {
|
||||
const bc = v('extract-biz-select');
|
||||
const rc = v('extract-report-select');
|
||||
if (!bc || !rc) { toast('请先选择业务和报表', 'error'); return; }
|
||||
const start = prompt('请输入开始日期 (yyyy-MM-dd):');
|
||||
if (!start) return;
|
||||
const end = prompt('请输入结束日期 (yyyy-MM-dd):');
|
||||
if (!end) return;
|
||||
if (!confirm('确定回填 ' + start + ' ~ ' + end + ' 的数据?')) return;
|
||||
try {
|
||||
const res = await api('POST', '/report/backfill', { businessCode: bc, reportCode: rc, startDate: start, endDate: end, executor: 'admin' });
|
||||
toast('回填完成!成功: ' + res.successDays + '/' + res.totalDays + ' 天, 耗时: ' + res.execTimeMs + 'ms' + (res.failDays > 0 ? ' 失败: ' + res.failDays + ' 天' : ''), res.success ? 'success' : 'error');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function onQueryBizChange() {
|
||||
const bc = v('query-biz-select');
|
||||
const rs = document.getElementById('query-report-select');
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>';
|
||||
queryCtx = { dimensions: [], indicators: [], filters: [], availableFields: null };
|
||||
if (!bc) return;
|
||||
try {
|
||||
const data = await api('GET', '/report/reports?businessCode=' + bc);
|
||||
rs.innerHTML = '<option value="">-- 选择报表 --</option>' + (data.list||[]).map(r => '<option value="'+r.reportCode+'">'+esc(r.reportName)+' ('+r.reportCode+')</option>').join('');
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function loadQueryFields() {
|
||||
const bc = v('query-biz-select');
|
||||
const rc = v('query-report-select');
|
||||
if (!bc || !rc) {
|
||||
document.getElementById('query-content').style.display = 'none';
|
||||
document.getElementById('query-empty').style.display = 'block';
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const data = await api('GET', '/report/fields?businessCode=' + bc + '&reportCode=' + rc);
|
||||
queryCtx.availableFields = {
|
||||
dimensions: data.dimensions || [],
|
||||
indicators: data.indicators || [],
|
||||
filters: data.filters || []
|
||||
};
|
||||
queryCtx.dimensions = [];
|
||||
queryCtx.indicators = [];
|
||||
queryCtx.filters = [];
|
||||
renderQueryUI();
|
||||
document.getElementById('query-content').style.display = 'block';
|
||||
document.getElementById('query-empty').style.display = 'none';
|
||||
document.getElementById('query-result').style.display = 'none';
|
||||
} catch(e) { toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
function renderQueryUI() {
|
||||
const f = queryCtx.availableFields;
|
||||
if (!f) return;
|
||||
|
||||
// 维度
|
||||
let dimHtml = '';
|
||||
for (const d of f.dimensions) {
|
||||
const sel = queryCtx.dimensions.includes(d.fieldCode);
|
||||
dimHtml += '<span class="chip'+(sel?' selected':'')+'" onclick="toggleDim(\''+jsAttr(d.fieldCode)+'\')">'+esc(d.fieldName||d.fieldCode)+'</span>';
|
||||
}
|
||||
document.getElementById('dimension-chips').innerHTML = dimHtml;
|
||||
|
||||
// 指标
|
||||
let indHtml = '';
|
||||
for (const d of f.indicators) {
|
||||
const sel = queryCtx.indicators.find(i => i.fieldCode === d.fieldCode);
|
||||
const fn = esc(d.fieldName||d.fieldCode);
|
||||
const unitSuffix = d.unit && fn.indexOf('('+d.unit+')') === -1 ? '('+d.unit+')' : '';
|
||||
indHtml += '<span class="chip'+(sel?' selected':'')+'" onclick="toggleInd(\''+jsAttr(d.fieldCode)+'\', \''+jsAttr(d.defaultAggregate||'SUM')+'\', \''+jsAttr(d.fieldName||d.fieldCode)+'\')">'+fn+unitSuffix+'</span>';
|
||||
}
|
||||
document.getElementById('indicator-area').innerHTML = indHtml;
|
||||
renderSelectedIndicators();
|
||||
|
||||
// 筛选
|
||||
let filHtml = '';
|
||||
for (const d of [...f.filters, ...f.indicators, ...f.dimensions]) {
|
||||
filHtml += '<span class="chip" onclick="addFilter(\''+jsAttr(d.fieldCode)+'\', \''+jsAttr(d.fieldName||d.fieldCode)+'\')">'+esc(d.fieldName||d.fieldCode)+'</span>';
|
||||
}
|
||||
document.getElementById('filter-area').innerHTML = filHtml;
|
||||
|
||||
// 默认时间范围
|
||||
if (!document.getElementById('query-start-date').value) {
|
||||
const today = new Date().toISOString().slice(0,10);
|
||||
const weekAgo = new Date(Date.now() - 7*86400000).toISOString().slice(0,10);
|
||||
document.getElementById('query-start-date').value = weekAgo;
|
||||
document.getElementById('query-end-date').value = today;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleDim(code) {
|
||||
const idx = queryCtx.dimensions.indexOf(code);
|
||||
if (idx >= 0) queryCtx.dimensions.splice(idx, 1);
|
||||
else queryCtx.dimensions.push(code);
|
||||
renderQueryUI();
|
||||
}
|
||||
|
||||
function toggleInd(code, defAggr, name) {
|
||||
const idx = queryCtx.indicators.findIndex(i => i.fieldCode === code);
|
||||
if (idx >= 0) { queryCtx.indicators.splice(idx, 1); renderSelectedIndicators(); renderQueryUI(); return; }
|
||||
const aggr = prompt('选择聚合方式 (SUM/COUNT/AVG/MAX/MIN):', defAggr||'SUM');
|
||||
if (!aggr) return;
|
||||
queryCtx.indicators.push({ fieldCode: code, aggregate: aggr.toUpperCase(), alias: code + '_' + aggr.toLowerCase() });
|
||||
renderSelectedIndicators();
|
||||
renderQueryUI();
|
||||
}
|
||||
|
||||
function renderSelectedIndicators() {
|
||||
if (queryCtx.indicators.length === 0) {
|
||||
document.getElementById('selected-indicators').innerHTML = '';
|
||||
return;
|
||||
}
|
||||
let h = '<div style="font-size:12px;color:#666;margin-bottom:4px">已选指标:</div>';
|
||||
for (const ind of queryCtx.indicators) {
|
||||
h += '<span class="field-tag">'+esc(ind.fieldCode)+' ('+ind.aggregate+') <span class="remove" onclick="removeInd(\''+jsAttr(ind.fieldCode)+'\')">×</span></span>';
|
||||
}
|
||||
document.getElementById('selected-indicators').innerHTML = h;
|
||||
}
|
||||
|
||||
function removeInd(code) {
|
||||
queryCtx.indicators = queryCtx.indicators.filter(i => i.fieldCode !== code);
|
||||
renderSelectedIndicators();
|
||||
renderQueryUI();
|
||||
}
|
||||
|
||||
let filterCounter = 0;
|
||||
|
||||
function addFilter(code, name) {
|
||||
filterCounter++;
|
||||
const area = document.getElementById('filter-area');
|
||||
const ops = ['=','!=','>','<','>=','<=','IN','LIKE','BETWEEN'];
|
||||
const isBetween = ops[ops.length-1];
|
||||
const row = document.createElement('div');
|
||||
row.className = 'add-filter-row';
|
||||
row.id = 'filter-row-'+filterCounter;
|
||||
row.innerHTML = '<select id="fcol-'+filterCounter+'" style="padding:6px 8px;border:1px solid #d9d9d9;border-radius:6px;font-size:11px"><option value="'+esc(code)+'">'+esc(name)+' ('+esc(code)+')</option></select>' +
|
||||
'<select id="fop-'+filterCounter+'" style="padding:6px 8px;border:1px solid #d9d9d9;border-radius:6px;font-size:11px" onchange="onFilterOpChange('+filterCounter+')">'+ops.map(o=>'<option value="'+o+'">'+o+'</option>').join('')+'</select>' +
|
||||
'<input id="fval-'+filterCounter+'" style="padding:6px 8px;border:1px solid #d9d9d9;border-radius:6px;font-size:11px" placeholder="值">' +
|
||||
'<button class="btn btn-sm btn-danger" style="padding:4px 8px" onclick="removeFilter('+filterCounter+')">×</button>';
|
||||
area.appendChild(row);
|
||||
}
|
||||
|
||||
function onFilterOpChange(idx) {
|
||||
const op = document.getElementById('fop-'+idx).value;
|
||||
const valEl = document.getElementById('fval-'+idx);
|
||||
if (op === 'BETWEEN') valEl.placeholder = '值1,值2';
|
||||
else if (op === 'IN') valEl.placeholder = '逗号分隔';
|
||||
else valEl.placeholder = '值';
|
||||
}
|
||||
|
||||
function removeFilter(idx) {
|
||||
const row = document.getElementById('filter-row-'+idx);
|
||||
if (row) row.remove();
|
||||
}
|
||||
|
||||
async function executeQuery() {
|
||||
const bc = v('query-biz-select');
|
||||
const rc = v('query-report-select');
|
||||
if (!bc || !rc) { toast('请先选择业务和报表', 'error'); return; }
|
||||
|
||||
const filters = [];
|
||||
document.querySelectorAll('[id^="filter-row-"]').forEach(row => {
|
||||
const m = row.id.match(/filter-row-(\d+)/);
|
||||
if (!m) return;
|
||||
const cid = m[1];
|
||||
const colEl = document.getElementById('fcol-'+cid);
|
||||
const opEl = document.getElementById('fop-'+cid);
|
||||
const valEl = document.getElementById('fval-'+cid);
|
||||
if (!colEl || !opEl || !valEl || !valEl.value.trim()) return;
|
||||
const op = opEl.value;
|
||||
let val = valEl.value.trim();
|
||||
let val2 = null;
|
||||
if (op === 'BETWEEN') {
|
||||
const parts = val.split(',').map(s => s.trim());
|
||||
if (parts.length >= 2) { val = parts[0]; val2 = parts[1]; }
|
||||
else return;
|
||||
} else if (op === 'IN') {
|
||||
val = val.split(',').map(s => s.trim());
|
||||
}
|
||||
const f = { fieldCode: colEl.value, operator: op, value: val };
|
||||
if (val2 !== null) f.value2 = val2;
|
||||
filters.push(f);
|
||||
});
|
||||
|
||||
const body = {
|
||||
businessCode: bc, reportCode: rc,
|
||||
dimensions: queryCtx.dimensions,
|
||||
indicators: queryCtx.indicators,
|
||||
filters: filters,
|
||||
timeRange: { startDate: v('query-start-date'), endDate: v('query-end-date') },
|
||||
orderBy: v('query-order-field') ? [{ fieldCode: v('query-order-field'), direction: v('query-order-dir') }] : [],
|
||||
page: 1, pageSize: parseInt(v('query-page-size'))||20,
|
||||
};
|
||||
|
||||
const resultBox = document.getElementById('query-result');
|
||||
resultBox.style.display = 'block';
|
||||
resultBox.textContent = '查询中...';
|
||||
try {
|
||||
const res = await api('POST', '/report/query', body);
|
||||
let output = '总记录数: ' + res.total + ' | 执行耗时: ' + res.execTimeMs + 'ms\n';
|
||||
if (res.sql) output += '生成SQL: ' + res.sql + '\n';
|
||||
output += '\n';
|
||||
if (res.list && res.list.length > 0) {
|
||||
// 表头
|
||||
const keys = Object.keys(res.list[0]);
|
||||
output += keys.join('\t') + '\n';
|
||||
output += keys.map(() => '---').join('\t') + '\n';
|
||||
for (const row of res.list) {
|
||||
output += keys.map(k => row[k] != null ? String(row[k]) : '').join('\t') + '\n';
|
||||
}
|
||||
} else {
|
||||
output += '(无数据)';
|
||||
}
|
||||
resultBox.textContent = output;
|
||||
} catch(e) { resultBox.textContent = '查询失败: ' + e.message; }
|
||||
}
|
||||
|
||||
// ==================== 通用 ====================
|
||||
function closeModal() {
|
||||
document.getElementById('modalOverlay').classList.remove('open');
|
||||
document.getElementById('modal').classList.remove('open');
|
||||
}
|
||||
|
||||
// 初始化
|
||||
loadBizList();
|
||||
loadBizSelects();
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
@@ -0,0 +1,499 @@
|
||||
package report
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
reportSvc "dataengine/common/report"
|
||||
"dataengine/common/report/model"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ============================================================
|
||||
// 批量操作(供 PPGo_Job 调度)
|
||||
// ============================================================
|
||||
|
||||
type extractAllReq struct {
|
||||
g.Meta `path:"/extractAll" method:"post" tags:"报表引擎" summary:"全量抽取" dc:"遍历所有启用业务+报表,抽取前一天数据"`
|
||||
}
|
||||
|
||||
type extractAllRes struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (c *report) ExtractAll(ctx context.Context, req *extractAllReq) (*extractAllRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
logrus.Info("[HTTP] 触发全量抽取")
|
||||
reportSvc.ExecDailyExtract(ctx)
|
||||
return &extractAllRes{Success: true, Message: "全量抽取已触发"}, nil
|
||||
}
|
||||
|
||||
type report struct{}
|
||||
|
||||
var ReportController = new(report)
|
||||
|
||||
func svc() *reportSvc.ReportService {
|
||||
return reportSvc.GetService()
|
||||
}
|
||||
|
||||
func ctxWithUser(ctx context.Context) context.Context {
|
||||
return context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 业务 CRUD
|
||||
// ============================================================
|
||||
|
||||
type listBusinessesReq struct {
|
||||
g.Meta `path:"/businesses" method:"get" tags:"报表引擎" summary:"业务列表"`
|
||||
model.PageReq
|
||||
}
|
||||
|
||||
type listBusinessesRes struct {
|
||||
List []model.BusinessConfig `json:"list"`
|
||||
model.PageRes
|
||||
}
|
||||
|
||||
func (c *report) ListBusinesses(ctx context.Context, req *listBusinessesReq) (*listBusinessesRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
list, total, err := svc().ListBusinesses(ctx, req.PageNum, req.PageSize)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取业务列表失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
totalPages := (total + req.PageSize - 1) / req.PageSize
|
||||
return &listBusinessesRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil
|
||||
}
|
||||
|
||||
type getBusinessReq struct {
|
||||
g.Meta `path:"/business" method:"get" tags:"报表引擎" summary:"获取业务"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type getBusinessRes struct {
|
||||
Data *model.BusinessConfig `json:"data"`
|
||||
}
|
||||
|
||||
func (c *report) GetBusiness(ctx context.Context, req *getBusinessReq) (*getBusinessRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
data, err := svc().GetBusiness(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取业务失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &getBusinessRes{Data: data}, nil
|
||||
}
|
||||
|
||||
type saveBusinessReq struct {
|
||||
g.Meta `path:"/business/save" method:"post" tags:"报表引擎" summary:"保存业务"`
|
||||
model.SaveBusinessReq
|
||||
}
|
||||
|
||||
type saveBusinessRes struct {
|
||||
*model.SaveResult
|
||||
}
|
||||
|
||||
func (c *report) SaveBusiness(ctx context.Context, req *saveBusinessReq) (*saveBusinessRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().SaveBusiness(ctx, &req.SaveBusinessReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 保存业务失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &saveBusinessRes{SaveResult: result}, nil
|
||||
}
|
||||
|
||||
type deleteBusinessReq struct {
|
||||
g.Meta `path:"/business" method:"delete" tags:"报表引擎" summary:"删除业务"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type deleteBusinessRes struct {
|
||||
*model.DeleteResult
|
||||
}
|
||||
|
||||
func (c *report) DeleteBusiness(ctx context.Context, req *deleteBusinessReq) (*deleteBusinessRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().DeleteBusiness(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 删除业务失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &deleteBusinessRes{DeleteResult: result}, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 报表 CRUD
|
||||
// ============================================================
|
||||
|
||||
type listReportsReq struct {
|
||||
g.Meta `path:"/reports" method:"get" tags:"报表引擎" summary:"报表列表"`
|
||||
BusinessCode string `json:"businessCode" v:"required"`
|
||||
ReportName string `json:"reportName" dc:"报表名称(模糊搜索)"`
|
||||
model.PageReq
|
||||
}
|
||||
|
||||
type listReportsRes struct {
|
||||
List []model.ReportConfig `json:"list"`
|
||||
model.PageRes
|
||||
}
|
||||
|
||||
func (c *report) ListReports(ctx context.Context, req *listReportsReq) (*listReportsRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
list, total, err := svc().ListReports(ctx, req.BusinessCode, req.ReportName, req.PageNum, req.PageSize)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取报表列表失败 %s: %v", req.BusinessCode, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 填充抽取配置ID(逗号分隔,不限 status)
|
||||
for i := range list {
|
||||
configs, err := svc().GetExtractConfigsAll(ctx, list[i].BusinessCode, list[i].ReportCode)
|
||||
if err == nil {
|
||||
ids := make([]string, 0, len(configs))
|
||||
for _, ec := range configs {
|
||||
ids = append(ids, strconv.FormatInt(ec.Id, 10))
|
||||
}
|
||||
list[i].ExtractConfigIDs = strings.Join(ids, ",")
|
||||
}
|
||||
}
|
||||
|
||||
totalPages := (total + req.PageSize - 1) / req.PageSize
|
||||
return &listReportsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil
|
||||
}
|
||||
|
||||
type getReportReq struct {
|
||||
g.Meta `path:"/report" method:"get" tags:"报表引擎" summary:"获取报表"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type getReportRes struct {
|
||||
Data *model.ReportConfig `json:"data"`
|
||||
}
|
||||
|
||||
func (c *report) GetReport(ctx context.Context, req *getReportReq) (*getReportRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
data, err := svc().GetReport(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取报表失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 填充抽取配置ID(逗号分隔,不限 status)
|
||||
configs, err := svc().GetExtractConfigsAll(ctx, data.BusinessCode, data.ReportCode)
|
||||
if err == nil {
|
||||
ids := make([]string, 0, len(configs))
|
||||
for _, ec := range configs {
|
||||
ids = append(ids, strconv.FormatInt(ec.Id, 10))
|
||||
}
|
||||
data.ExtractConfigIDs = strings.Join(ids, ",")
|
||||
}
|
||||
|
||||
return &getReportRes{Data: data}, nil
|
||||
}
|
||||
|
||||
type saveReportReq struct {
|
||||
g.Meta `path:"/report/save" method:"post" tags:"报表引擎" summary:"保存报表"`
|
||||
model.SaveReportReq
|
||||
}
|
||||
|
||||
type saveReportRes struct {
|
||||
*model.SaveResult
|
||||
}
|
||||
|
||||
func (c *report) SaveReport(ctx context.Context, req *saveReportReq) (*saveReportRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().SaveReport(ctx, &req.SaveReportReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 保存报表失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &saveReportRes{SaveResult: result}, nil
|
||||
}
|
||||
|
||||
type saveReportWithFieldsReq struct {
|
||||
g.Meta `path:"/report/saveWithFields" method:"post" tags:"报表引擎" summary:"保存报表及字段配置(全量替换)"`
|
||||
model.SaveReportWithFieldsReq
|
||||
}
|
||||
|
||||
type saveReportWithFieldsRes struct {
|
||||
*model.SaveResult
|
||||
}
|
||||
|
||||
func (c *report) SaveReportWithFields(ctx context.Context, req *saveReportWithFieldsReq) (*saveReportWithFieldsRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().SaveReportWithFields(ctx, &req.SaveReportWithFieldsReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 保存报表及字段失败 %s/%s: %v", req.BusinessCode, req.ReportCode, err)
|
||||
return nil, err
|
||||
}
|
||||
logrus.Infof("[报表引擎] 保存报表及字段成功 %s/%s id=%d", req.BusinessCode, req.ReportCode, result.ID)
|
||||
return &saveReportWithFieldsRes{SaveResult: result}, nil
|
||||
}
|
||||
|
||||
type deleteReportReq struct {
|
||||
g.Meta `path:"/report" method:"delete" tags:"报表引擎" summary:"删除报表"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type deleteReportRes struct {
|
||||
*model.DeleteResult
|
||||
}
|
||||
|
||||
func (c *report) DeleteReport(ctx context.Context, req *deleteReportReq) (*deleteReportRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().DeleteReport(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 删除报表失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &deleteReportRes{DeleteResult: result}, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 字段 CRUD
|
||||
// ============================================================
|
||||
|
||||
type getReportFieldsReq struct {
|
||||
g.Meta `path:"/fields" method:"get" tags:"报表引擎" summary:"报表字段列表(按角色分组)"`
|
||||
BusinessCode string `json:"businessCode" v:"required"`
|
||||
ReportCode string `json:"reportCode" v:"required"`
|
||||
model.PageReq
|
||||
}
|
||||
|
||||
func (c *report) GetReportFields(ctx context.Context, req *getReportFieldsReq) (*model.GetReportFieldsResp, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
resp, err := svc().GetReportFields(ctx, req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取报表字段失败 %s/%s: %v", req.BusinessCode, req.ReportCode, err)
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type getFieldReq struct {
|
||||
g.Meta `path:"/field" method:"get" tags:"报表引擎" summary:"获取字段"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type getFieldRes struct {
|
||||
Data *model.FieldConfig `json:"data"`
|
||||
}
|
||||
|
||||
func (c *report) GetField(ctx context.Context, req *getFieldReq) (*getFieldRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
data, err := svc().GetField(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取字段失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &getFieldRes{Data: data}, nil
|
||||
}
|
||||
|
||||
type saveFieldReq struct {
|
||||
g.Meta `path:"/field/save" method:"post" tags:"报表引擎" summary:"保存字段"`
|
||||
model.SaveFieldReq
|
||||
}
|
||||
|
||||
type saveFieldRes struct {
|
||||
*model.SaveResult
|
||||
}
|
||||
|
||||
func (c *report) SaveField(ctx context.Context, req *saveFieldReq) (*saveFieldRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().SaveField(ctx, &req.SaveFieldReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 保存字段失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &saveFieldRes{SaveResult: result}, nil
|
||||
}
|
||||
|
||||
type deleteFieldReq struct {
|
||||
g.Meta `path:"/field" method:"delete" tags:"报表引擎" summary:"删除字段"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type deleteFieldRes struct {
|
||||
*model.DeleteResult
|
||||
}
|
||||
|
||||
func (c *report) DeleteField(ctx context.Context, req *deleteFieldReq) (*deleteFieldRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().DeleteField(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 删除字段失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &deleteFieldRes{DeleteResult: result}, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 抽取配置 CRUD
|
||||
// ============================================================
|
||||
|
||||
type getExtractConfigsReq struct {
|
||||
g.Meta `path:"/extractConfigs" method:"get" tags:"报表引擎" summary:"抽取配置列表"`
|
||||
BusinessCode string `json:"businessCode" v:"required"`
|
||||
ReportCode string `json:"reportCode" v:"required"`
|
||||
model.PageReq
|
||||
}
|
||||
|
||||
type getExtractConfigsRes struct {
|
||||
List []model.ExtractConfig `json:"list"`
|
||||
model.PageRes
|
||||
}
|
||||
|
||||
func (c *report) GetExtractConfigs(ctx context.Context, req *getExtractConfigsReq) (*getExtractConfigsRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
list, total, err := svc().ListExtractConfigs(ctx, req.BusinessCode, req.ReportCode, req.PageNum, req.PageSize)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取抽取配置列表失败 %s/%s: %v", req.BusinessCode, req.ReportCode, err)
|
||||
return nil, err
|
||||
}
|
||||
totalPages := (total + req.PageSize - 1) / req.PageSize
|
||||
return &getExtractConfigsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil
|
||||
}
|
||||
|
||||
type getExtractConfigReq struct {
|
||||
g.Meta `path:"/extractConfig" method:"get" tags:"报表引擎" summary:"获取抽取配置"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type getExtractConfigRes struct {
|
||||
Data *model.ExtractConfig `json:"data"`
|
||||
}
|
||||
|
||||
func (c *report) GetExtractConfig(ctx context.Context, req *getExtractConfigReq) (*getExtractConfigRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
data, err := svc().GetExtractConfig(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 获取抽取配置失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &getExtractConfigRes{Data: data}, nil
|
||||
}
|
||||
|
||||
type saveExtractConfigReq struct {
|
||||
g.Meta `path:"/extractConfig/save" method:"post" tags:"报表引擎" summary:"保存抽取配置"`
|
||||
model.SaveExtractConfigReq
|
||||
}
|
||||
|
||||
type saveExtractConfigRes struct {
|
||||
*model.SaveResult
|
||||
}
|
||||
|
||||
func (c *report) SaveExtractConfig(ctx context.Context, req *saveExtractConfigReq) (*saveExtractConfigRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().SaveExtractConfig(ctx, &req.SaveExtractConfigReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 保存抽取配置失败: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return &saveExtractConfigRes{SaveResult: result}, nil
|
||||
}
|
||||
|
||||
type deleteExtractConfigReq struct {
|
||||
g.Meta `path:"/extractConfig" method:"delete" tags:"报表引擎" summary:"删除抽取配置"`
|
||||
ID int64 `json:"id" v:"required"`
|
||||
}
|
||||
|
||||
type deleteExtractConfigRes struct {
|
||||
*model.DeleteResult
|
||||
}
|
||||
|
||||
func (c *report) DeleteExtractConfig(ctx context.Context, req *deleteExtractConfigReq) (*deleteExtractConfigRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
result, err := svc().DeleteExtractConfig(ctx, req.ID)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 删除抽取配置失败 id=%d: %v", req.ID, err)
|
||||
return nil, err
|
||||
}
|
||||
return &deleteExtractConfigRes{DeleteResult: result}, nil
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 数据操作
|
||||
// ============================================================
|
||||
|
||||
type extractDataReq struct {
|
||||
g.Meta `path:"/extract" method:"post" tags:"报表引擎" summary:"执行按天数据抽取"`
|
||||
model.ExtractDailyDataReq
|
||||
}
|
||||
|
||||
func (c *report) ExtractData(ctx context.Context, req *extractDataReq) (*model.ExtractDailyDataResp, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
resp, err := svc().ExtractDailyData(ctx, req.BusinessCode, req.ReportCode, req.StatDate, req.Executor)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 按天抽取失败 %s/%s %s: %v", req.BusinessCode, req.ReportCode, req.StatDate, err)
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type backfillReq struct {
|
||||
g.Meta `path:"/backfill" method:"post" tags:"报表引擎" summary:"批量回填数据"`
|
||||
model.BackfillReq
|
||||
}
|
||||
|
||||
func (c *report) Backfill(ctx context.Context, req *backfillReq) (*model.BackfillResp, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
resp, err := svc().Backfill(ctx, &req.BackfillReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 批量回填失败 %s/%s %s~%s: %v", req.BusinessCode, req.ReportCode, req.StartDate, req.EndDate, err)
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type autoCreateTableReq struct {
|
||||
g.Meta `path:"/autoCreateTable" method:"post" tags:"报表引擎" summary:"自动创建统计宽表"`
|
||||
model.AutoCreateStatTableReq
|
||||
}
|
||||
|
||||
func (c *report) AutoCreateTable(ctx context.Context, req *autoCreateTableReq) (*model.AutoCreateStatTableResp, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
resp, err := svc().AutoCreateStatTable(ctx, req.BusinessCode, req.ReportCode)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 自动建表失败 %s/%s: %v", req.BusinessCode, req.ReportCode, err)
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type queryReportReq struct {
|
||||
g.Meta `path:"/query" method:"post" tags:"报表引擎" summary:"用户选择查询"`
|
||||
model.UserSelectQueryReq
|
||||
}
|
||||
|
||||
func (c *report) QueryReport(ctx context.Context, req *queryReportReq) (*model.UserSelectQueryResp, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
resp, err := svc().QueryReportByUserSelect(ctx, &req.UserSelectQueryReq)
|
||||
if err != nil {
|
||||
logrus.Errorf("[报表引擎] 查询报表失败 %s/%s: %v", req.BusinessCode, req.ReportCode, err)
|
||||
return nil, err
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
type initTablesReq struct {
|
||||
g.Meta `path:"/initTables" method:"post" tags:"报表引擎" summary:"初始化系统表"`
|
||||
}
|
||||
|
||||
type initTablesRes struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func (c *report) InitTables(ctx context.Context, req *initTablesReq) (*initTablesRes, error) {
|
||||
ctx = ctxWithUser(ctx)
|
||||
if err := svc().InitSystemTables(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &initTablesRes{Success: true, Message: "系统表初始化完成"}, nil
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
package sync
|
||||
|
||||
import (
|
||||
"context"
|
||||
svc "dataengine/service/sync"
|
||||
"fmt"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var PlatformSyncController = new(syncCtrl)
|
||||
|
||||
type syncCtrl struct{}
|
||||
|
||||
// RefreshTokenReq 刷新 Token 请求
|
||||
type RefreshTokenReq struct {
|
||||
g.Meta `path:"/refreshToken" method:"post" tags:"平台同步" summary:"刷新Token" dc:"刷新指定平台的认证Token(如腾讯OAuth2)"`
|
||||
PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"`
|
||||
}
|
||||
|
||||
// RefreshTokenRes 刷新 Token 响应
|
||||
type RefreshTokenRes struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// RefreshToken 手动触发 Token 刷新
|
||||
// 由 PPGo_Job 定时调度,定期刷新平台 Token(如腾讯 OAuth2 access_token)
|
||||
func (c *syncCtrl) RefreshToken(ctx context.Context, req *RefreshTokenReq) (*RefreshTokenRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
logrus.Infof("[HTTP] 触发 Token 刷新: platform=%s", req.PlatformCode)
|
||||
|
||||
pm := &svc.PlatformManager{}
|
||||
platform, _, err := pm.GetPlatformWithInterfaces(ctx, req.PlatformCode)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("读取平台配置失败: %w", err)
|
||||
}
|
||||
|
||||
if err := svc.RefreshTencentToken(ctx, platform); err != nil {
|
||||
return &RefreshTokenRes{Success: false, Message: err.Error()}, nil
|
||||
}
|
||||
|
||||
return &RefreshTokenRes{Success: true, Message: "Token 刷新成功"}, nil
|
||||
}
|
||||
|
||||
// TriggerSyncReq 触发同步请求
|
||||
type TriggerSyncReq struct {
|
||||
g.Meta `path:"/trigger" method:"post" tags:"平台同步" summary:"触发同步" dc:"根据平台编码和接口编码触发数据同步"`
|
||||
PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"`
|
||||
InterfaceCode string `json:"interfaceCode" v:"required" dc:"接口编码"`
|
||||
FullSync bool `json:"fullSync" dc:"是否全量同步,true=全量 false=增量"`
|
||||
}
|
||||
|
||||
// TriggerSyncRes 触发同步响应
|
||||
type TriggerSyncRes struct {
|
||||
Success bool `json:"success"`
|
||||
TableName string `json:"tableName"`
|
||||
TotalRows int `json:"totalRows"`
|
||||
InsertedRows int `json:"insertedRows"`
|
||||
Duration string `json:"duration"`
|
||||
}
|
||||
|
||||
// TriggerSync 触发同步
|
||||
func (c *syncCtrl) TriggerSync(ctx context.Context, req *TriggerSyncReq) (*TriggerSyncRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
logrus.Infof("触发同步: platform=%s, interface=%s, fullSync=%v", req.PlatformCode, req.InterfaceCode, req.FullSync)
|
||||
result, err := svc.SyncByConfig(ctx, req.PlatformCode, req.InterfaceCode, req.FullSync)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &TriggerSyncRes{
|
||||
Success: true, TableName: result.TableName,
|
||||
TotalRows: result.TotalRows, InsertedRows: result.InsertedRows,
|
||||
Duration: result.Duration,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// QueryPlatformConfigReq 查询平台配置请求
|
||||
type QueryPlatformConfigReq struct {
|
||||
g.Meta `path:"/config" method:"get" tags:"平台同步" summary:"查询平台配置" dc:"查看平台和接口配置"`
|
||||
PlatformCode string `json:"platformCode" v:"required" dc:"平台编码"`
|
||||
}
|
||||
|
||||
// QueryPlatformConfigRes 查询平台配置响应
|
||||
type QueryPlatformConfigRes struct {
|
||||
PlatformName string `json:"platformName"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
ApiBaseUrl string `json:"apiBaseUrl"`
|
||||
AuthType string `json:"authType"`
|
||||
HasToken bool `json:"hasToken"`
|
||||
InterfaceCount int `json:"interfaceCount"`
|
||||
Interfaces []struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
HasTableDef bool `json:"hasTableDef"`
|
||||
} `json:"interfaces"`
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 批量操作(供 PPGo_Job 调度)
|
||||
// ============================================================
|
||||
|
||||
// TriggerAllSyncReq 触发全平台同步请求
|
||||
type TriggerAllSyncReq struct {
|
||||
g.Meta `path:"/triggerAll" method:"post" tags:"平台同步" summary:"触发全平台同步" dc:"遍历所有ACTIVE平台/接口执行同步"`
|
||||
FullSync bool `json:"fullSync" dc:"true=强制全量同步,false=自动判断增量/全量"`
|
||||
}
|
||||
|
||||
// TriggerAllSyncRes 触发全平台同步响应
|
||||
type TriggerAllSyncRes struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Results []svc.SyncRunItemResult `json:"results,omitempty"`
|
||||
}
|
||||
|
||||
// TriggerAllSync 触发全平台同步(等价于内部自动同步的一次完整执行)
|
||||
func (c *syncCtrl) TriggerAllSync(ctx context.Context, req *TriggerAllSyncReq) (*TriggerAllSyncRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
logrus.Infof("[HTTP] 触发全平台同步, fullSync=%v", req.FullSync)
|
||||
results := svc.TriggerAllSync(ctx, req.FullSync)
|
||||
|
||||
hasError := false
|
||||
for _, r := range results {
|
||||
if !r.Success {
|
||||
hasError = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
res := &TriggerAllSyncRes{Results: results}
|
||||
if hasError {
|
||||
res.Success = false
|
||||
res.Message = "全平台同步完成,部分失败"
|
||||
} else {
|
||||
res.Success = true
|
||||
res.Message = "全平台同步完成"
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// CompensateReq 触发补偿扫描请求
|
||||
type CompensateReq struct {
|
||||
g.Meta `path:"/compensate" method:"post" tags:"平台同步" summary:"触发补偿扫描" dc:"扫描失败任务并重试"`
|
||||
}
|
||||
|
||||
// CompensateRes 触发补偿扫描响应
|
||||
type CompensateRes struct {
|
||||
Success bool `json:"success"`
|
||||
Message string `json:"message"`
|
||||
Result *svc.CompensateResult `json:"result,omitempty"`
|
||||
}
|
||||
|
||||
// Compensate 触发补偿扫描(等价于内部补偿调度的一次完整执行)
|
||||
func (c *syncCtrl) Compensate(ctx context.Context, req *CompensateReq) (*CompensateRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
logrus.Info("[HTTP] 触发补偿扫描")
|
||||
result := svc.TriggerCompensation(ctx)
|
||||
|
||||
res := &CompensateRes{Result: result}
|
||||
if result.TotalFailed == 0 {
|
||||
res.Success = true
|
||||
res.Message = "补偿扫描完成,没有待补偿任务"
|
||||
} else if result.Failed > 0 {
|
||||
res.Success = false
|
||||
res.Message = fmt.Sprintf("补偿扫描完成,共 %d 个失败任务,成功补偿 %d 个,失败 %d 个,已达最大重试 %d 个",
|
||||
result.TotalFailed, result.Succeeded, result.Failed, result.MaxRetryReached)
|
||||
} else {
|
||||
res.Success = true
|
||||
res.Message = fmt.Sprintf("补偿扫描完成,共 %d 个失败任务,成功补偿 %d 个",
|
||||
result.TotalFailed, result.Succeeded)
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
|
||||
// QueryConfig 查询配置
|
||||
func (c *syncCtrl) QueryConfig(ctx context.Context, req *QueryPlatformConfigReq) (*QueryPlatformConfigRes, error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin", TenantId: 1})
|
||||
pm := &svc.PlatformManager{}
|
||||
platform, interfaces, err := pm.GetPlatformWithInterfaces(ctx, req.PlatformCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res := &QueryPlatformConfigRes{
|
||||
PlatformName: platform.PlatformName, PlatformCode: platform.PlatformCode,
|
||||
ApiBaseUrl: platform.ApiBaseUrl, AuthType: platform.AuthType,
|
||||
HasToken: platform.AccessToken != "", InterfaceCount: len(interfaces),
|
||||
}
|
||||
for _, iface := range interfaces {
|
||||
res.Interfaces = append(res.Interfaces, struct {
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Url string `json:"url"`
|
||||
HasTableDef bool `json:"hasTableDef"`
|
||||
}{
|
||||
Code: iface.Code, Name: iface.Name, Url: iface.Url,
|
||||
HasTableDef: iface.TableDefinition != nil && len(iface.TableDefinition) > 0,
|
||||
})
|
||||
}
|
||||
return res, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var CidAccountReportDetail = new(cidAccountReportDetailDao)
|
||||
|
||||
type cidAccountReportDetailDao struct{}
|
||||
|
||||
// Insert 插入广告数据报表详情
|
||||
func (d *cidAccountReportDetailDao) Insert(ctx context.Context, req *dto.CidAccountReportDetailItem) (id int64, err error) {
|
||||
var entityData *entity.CidAccountReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.CidAccountReportDetailTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告数据报表详情
|
||||
func (d *cidAccountReportDetailDao) BatchInsert(ctx context.Context, reqs []*dto.CidAccountReportDetailItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批100条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.CidAccountReportDetail, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.CidAccountReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.CidAccountReportDetailTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var CidAccountReportSum = new(CidAccountReportSumDao)
|
||||
|
||||
type CidAccountReportSumDao struct{}
|
||||
|
||||
// Insert 插入广告数据报表详情
|
||||
func (d *CidAccountReportSumDao) Insert(ctx context.Context, req *dto.CidAccountReportSumItem) (id int64, err error) {
|
||||
var entityData *entity.CidAccountReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.CidAccountReportSumTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告数据报表详情
|
||||
func (d *CidAccountReportSumDao) BatchInsert(ctx context.Context, reqs []*dto.CidAccountReportSumItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批100条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.CidAccountReportSum, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.CidAccountReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.CidAccountReportSumTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var CreativeReportDetail = new(CreativeReportDetailDao)
|
||||
|
||||
type CreativeReportDetailDao struct{}
|
||||
|
||||
func (d *CreativeReportDetailDao) Insert(ctx context.Context, req *dto.CreativeReportDetailItem) (id int64, err error) {
|
||||
var entityData *entity.CreativeReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.CreativeReportDetailTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
func (d *CreativeReportDetailDao) BatchInsert(ctx context.Context, reqs []*dto.CreativeReportDetailItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.CreativeReportDetail, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.CreativeReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.CreativeReportDetailTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var CreativeReportSum = new(CreativeReportSumDao)
|
||||
|
||||
type CreativeReportSumDao struct{}
|
||||
|
||||
// Insert 插入广告效果指标表
|
||||
func (d *CreativeReportSumDao) Insert(ctx context.Context, req *dto.CreativeReportSumItem) (id int64, err error) {
|
||||
var entityData *entity.CreativeReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.CreativeReportSumTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告效果指标表
|
||||
func (d *CreativeReportSumDao) BatchInsert(ctx context.Context, reqs []*dto.CreativeReportSumItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批 100 条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.CreativeReportSum, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.CreativeReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.CreativeReportSumTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var MaterialReport = new(materialReportDao)
|
||||
|
||||
type materialReportDao struct{}
|
||||
|
||||
// Insert 插入素材报表数据
|
||||
func (d *materialReportDao) Insert(ctx context.Context, req *dto.MaterialReportItem) (id int64, err error) {
|
||||
var entityData *entity.MaterialReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.MaterialReportTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入素材报表数据
|
||||
func (d *materialReportDao) BatchInsert(ctx context.Context, reqs []*dto.MaterialReportItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.MaterialReport, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.MaterialReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.MaterialReportTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
|
||||
// List 查询素材报表数据列表
|
||||
func (d *materialReportDao) List(ctx context.Context, req *dto.ListMaterialReportReq) ([]*entity.MaterialReport, int, error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.MaterialReportTable).Model
|
||||
|
||||
if req.ReportDateStr != "" {
|
||||
model = model.Where("report_date_str", req.ReportDateStr)
|
||||
}
|
||||
if req.PhotoId != "" {
|
||||
model = model.Where("photo_id", req.PhotoId)
|
||||
}
|
||||
if req.CampaignId != nil {
|
||||
model = model.Where("campaign_id", req.CampaignId)
|
||||
}
|
||||
if req.UnitId != nil {
|
||||
model = model.Where("unit_id", req.UnitId)
|
||||
}
|
||||
if req.CreativeId != nil {
|
||||
model = model.Where("creative_id", req.CreativeId)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
model = model.Where("(photo_name LIKE ? OR campaign_name LIKE ? OR unit_name LIKE ? OR creative_name LIKE ?)",
|
||||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
model = model.OrderDesc("created_at")
|
||||
|
||||
total, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var list []*entity.MaterialReport
|
||||
if req.Page != nil {
|
||||
err = model.Page(int(req.Page.PageNum), int(req.Page.PageSize)).Scan(&list)
|
||||
} else {
|
||||
err = model.Scan(&list)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var PopulationReport = new(populationReportDao)
|
||||
|
||||
type populationReportDao struct{}
|
||||
|
||||
// Insert 插入人群报表数据
|
||||
func (d *populationReportDao) Insert(ctx context.Context, req *dto.PopulationReportItem) (id int64, err error) {
|
||||
var entityData *entity.PopulationReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.PopulationReportTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入人群报表数据
|
||||
func (d *populationReportDao) BatchInsert(ctx context.Context, reqs []*dto.PopulationReportItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批 100 条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.PopulationReport, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.PopulationReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.PopulationReportTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
|
||||
// List 查询人群报表数据列表
|
||||
func (d *populationReportDao) List(ctx context.Context, req *dto.ListPopulationReportReq) ([]*entity.PopulationReport, int, error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.PopulationReportTable).Model
|
||||
|
||||
// 构建查询条件
|
||||
if req.ReportDateStr != "" {
|
||||
model = model.Where("report_date_str", req.ReportDateStr)
|
||||
}
|
||||
if req.PhotoId != "" {
|
||||
model = model.Where("photo_id", req.PhotoId)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
model = model.Where("photo_name LIKE ?", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 设置排序
|
||||
model = model.OrderDesc("created_at")
|
||||
|
||||
// 分页查询并获取总数
|
||||
total, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var list []*entity.PopulationReport
|
||||
if req.Page != nil {
|
||||
err = model.Page(int(req.Page.PageNum), int(req.Page.PageSize)).Scan(&list)
|
||||
} else {
|
||||
err = model.Scan(&list)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var StorewideReportDetail = new(StorewideReportDetailDao)
|
||||
|
||||
type StorewideReportDetailDao struct{}
|
||||
|
||||
// Insert 插入广告效果指标表
|
||||
func (d *StorewideReportDetailDao) Insert(ctx context.Context, req *dto.StorewideReportDetailItem) (id int64, err error) {
|
||||
var entityData *entity.StorewideReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.StorewideReportDetailTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告效果指标表
|
||||
func (d *StorewideReportDetailDao) BatchInsert(ctx context.Context, reqs []*dto.StorewideReportDetailItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批 100 条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.StorewideReportDetail, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.StorewideReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.StorewideReportDetailTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var StorewideReportSum = new(StorewideReportSumDao)
|
||||
|
||||
type StorewideReportSumDao struct{}
|
||||
|
||||
// Insert 插入广告效果指标表
|
||||
func (d *StorewideReportSumDao) Insert(ctx context.Context, req *dto.StorewideReportSumItem) (id int64, err error) {
|
||||
var entityData *entity.StorewideReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.StorewideReportSumTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告效果指标表
|
||||
func (d *StorewideReportSumDao) BatchInsert(ctx context.Context, reqs []*dto.StorewideReportSumItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批 100 条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.StorewideReportSum, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.StorewideReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.StorewideReportSumTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -0,0 +1,172 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"context"
|
||||
consts "dataengine/consts/public"
|
||||
dto "dataengine/model/dto/copydata"
|
||||
entity "dataengine/model/entity/copydata"
|
||||
"dataengine/utils"
|
||||
"time"
|
||||
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
var SyncTaskLog = new(SyncTaskLogDao)
|
||||
|
||||
type SyncTaskLogDao struct{}
|
||||
|
||||
// Create 创建任务日志(如果task_id已存在则返回现有ID)
|
||||
func (d *SyncTaskLogDao) Create(ctx context.Context, req *dto.CreateSyncTaskLogReq) (int64, error) {
|
||||
existingTask, err := d.GetByTaskID(ctx, req.TaskID, req.TaskType)
|
||||
if err == nil && existingTask != nil {
|
||||
logrus.Debugf("任务日志已存在,task_id=%s, task_type=%s, id=%d", req.TaskID, req.TaskType, existingTask.Id)
|
||||
return existingTask.Id, nil
|
||||
}
|
||||
|
||||
data := map[string]interface{}{
|
||||
"task_id": req.TaskID,
|
||||
"task_type": req.TaskType,
|
||||
"platform_code": req.PlatformCode,
|
||||
"interface_code": req.InterfaceCode,
|
||||
"advertiser_id": req.AdvertiserID,
|
||||
"start_time": req.StartTime,
|
||||
"end_time": req.EndTime,
|
||||
"status": req.Status,
|
||||
"max_retry": req.MaxRetry,
|
||||
"page_info": req.PageInfo,
|
||||
"request_params": req.RequestParams,
|
||||
"retry_count": 0,
|
||||
"duration_ms": 0,
|
||||
"tenant_id": utils.GetCurrentTenantId(ctx),
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.SyncTaskLogTable).Data(data).Insert()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// Update 更新任务日志
|
||||
func (d *SyncTaskLogDao) Update(ctx context.Context, req *dto.UpdateSyncTaskLogReq) error {
|
||||
data := make(gdb.Map)
|
||||
|
||||
if req.Status != "" {
|
||||
data["status"] = req.Status
|
||||
}
|
||||
if req.RetryCount != nil {
|
||||
data["retry_count"] = *req.RetryCount
|
||||
}
|
||||
if req.ErrorMessage != "" {
|
||||
data["error_message"] = req.ErrorMessage
|
||||
}
|
||||
if req.ErrorCode != "" {
|
||||
data["error_code"] = req.ErrorCode
|
||||
}
|
||||
if req.ResultSummary != nil {
|
||||
data["result_summary"] = req.ResultSummary
|
||||
}
|
||||
if req.NextRetryTime != nil {
|
||||
data["next_retry_time"] = req.NextRetryTime
|
||||
}
|
||||
if req.CompletedAt != nil {
|
||||
data["completed_at"] = req.CompletedAt
|
||||
}
|
||||
if req.DurationMs != nil {
|
||||
data["duration_ms"] = *req.DurationMs
|
||||
}
|
||||
|
||||
data["updated_at"] = time.Now()
|
||||
|
||||
_, err := gfdb.DB(ctx).Model(ctx, consts.SyncTaskLogTable).
|
||||
Data(data).
|
||||
Where("id", req.ID).
|
||||
Update()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
// QueryFailedTasks 查询需要补偿的失败任务
|
||||
func (d *SyncTaskLogDao) QueryFailedTasks(ctx context.Context, req *dto.QueryFailedTasksReq) ([]*dto.SyncTaskLogItem, error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.SyncTaskLogTable).Model
|
||||
|
||||
// 状态过滤
|
||||
if len(req.Status) > 0 {
|
||||
model = model.WhereIn("status", req.Status)
|
||||
}
|
||||
|
||||
// 任务类型过滤
|
||||
if req.TaskType != "" {
|
||||
model = model.Where("task_type", req.TaskType)
|
||||
}
|
||||
|
||||
// 只查询到达重试时间的任务(或从未设置过重试时间)
|
||||
model = model.Where(
|
||||
"(next_retry_time <= ? OR next_retry_time IS NULL)",
|
||||
time.Now(),
|
||||
)
|
||||
|
||||
// 限制数量
|
||||
limit := req.Limit
|
||||
if limit <= 0 {
|
||||
limit = 100
|
||||
}
|
||||
model = model.Limit(limit)
|
||||
|
||||
var results []*entity.SyncTaskLog
|
||||
if err := model.Scan(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]*dto.SyncTaskLogItem, len(results))
|
||||
for i, r := range results {
|
||||
item := &dto.SyncTaskLogItem{}
|
||||
gconv.Struct(r, item)
|
||||
items[i] = item
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
|
||||
// GetByTaskID 根据任务ID获取日志
|
||||
func (d *SyncTaskLogDao) GetByTaskID(ctx context.Context, taskID, taskType string) (*entity.SyncTaskLog, error) {
|
||||
var result *entity.SyncTaskLog
|
||||
err := gfdb.DB(ctx).Model(ctx, consts.SyncTaskLogTable).
|
||||
Where("task_id", taskID).
|
||||
Where("task_type", taskType).
|
||||
Scan(&result)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// QueryAllPageTasksByParentID 根据主任务ID查询所有分页任务
|
||||
func (d *SyncTaskLogDao) QueryAllPageTasksByParentID(ctx context.Context, parentTaskID string, limit int) ([]*dto.SyncTaskLogItem, error) {
|
||||
if limit <= 0 {
|
||||
limit = 1000
|
||||
}
|
||||
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.SyncTaskLogTable).Model
|
||||
model = model.Where("task_type", "account_report_page")
|
||||
model = model.WhereLike("task_id", parentTaskID+"_page_%")
|
||||
model = model.Limit(limit)
|
||||
|
||||
var results []*entity.SyncTaskLog
|
||||
if err := model.Scan(&results); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
items := make([]*dto.SyncTaskLogItem, len(results))
|
||||
for i, r := range results {
|
||||
item := &dto.SyncTaskLogItem{}
|
||||
gconv.Struct(r, item)
|
||||
items[i] = item
|
||||
}
|
||||
|
||||
return items, nil
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var TaskReport = new(taskReportDao)
|
||||
|
||||
type taskReportDao struct{}
|
||||
|
||||
// Insert 插入调控任务数据
|
||||
func (d *taskReportDao) Insert(ctx context.Context, req *dto.TaskReportItem) (id int64, err error) {
|
||||
var entityData *entity.TaskReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.TaskReportTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入调控任务数据
|
||||
func (d *taskReportDao) BatchInsert(ctx context.Context, reqs []*dto.TaskReportItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
// 分批处理,每批 100 条
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.TaskReport, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.TaskReport
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// 执行批量插入
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.TaskReportTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
// 批量插入失败,尝试逐条插入
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
|
||||
// List 查询调控任务数据列表
|
||||
func (d *taskReportDao) List(ctx context.Context, req *dto.ListTaskReportReq) ([]*entity.TaskReport, int, error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.TaskReportTable).Model
|
||||
|
||||
// 构建查询条件
|
||||
if req.ReportDateStr != "" {
|
||||
model = model.Where("report_date_str", req.ReportDateStr)
|
||||
}
|
||||
if req.PhotoId != "" {
|
||||
model = model.Where("photo_id", req.PhotoId)
|
||||
}
|
||||
if req.Keyword != "" {
|
||||
model = model.Where("photo_name LIKE ?", "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
// 设置排序
|
||||
model = model.OrderDesc("created_at")
|
||||
|
||||
// 分页查询并获取总数
|
||||
total, err := model.Count()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
var list []*entity.TaskReport
|
||||
if req.Page != nil {
|
||||
err = model.Page(int(req.Page.PageNum), int(req.Page.PageSize)).Scan(&list)
|
||||
} else {
|
||||
err = model.Scan(&list)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
return list, total, nil
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var UnitReportDetail = new(UnitReportDetailDao)
|
||||
|
||||
type UnitReportDetailDao struct{}
|
||||
|
||||
// Insert 插入广告效果指标详情
|
||||
func (d *UnitReportDetailDao) Insert(ctx context.Context, req *dto.UnitReportDetailItem) (id int64, err error) {
|
||||
var entityData *entity.UnitReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.UnitReportDetailTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告效果指标详情
|
||||
func (d *UnitReportDetailDao) BatchInsert(ctx context.Context, reqs []*dto.UnitReportDetailItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.UnitReportDetail, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.UnitReportDetail
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.UnitReportDetailTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/copydata"
|
||||
entity "cid/model/entity/copydata"
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var UnitReportSum = new(UnitReportSumDao)
|
||||
|
||||
type UnitReportSumDao struct{}
|
||||
|
||||
// Insert 插入广告效果指标
|
||||
func (d *UnitReportSumDao) Insert(ctx context.Context, req *dto.UnitReportSumItem) (id int64, err error) {
|
||||
var entityData *entity.UnitReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.UnitReportSumTable).Data(&entityData).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// BatchInsert 批量插入广告效果指标
|
||||
func (d *UnitReportSumDao) BatchInsert(ctx context.Context, reqs []*dto.UnitReportSumItem) (successCount int64, failCount int64, failedIndexes []int64, err error) {
|
||||
if len(reqs) == 0 {
|
||||
return 0, 0, nil, errors.New("批量插入数据不能为空")
|
||||
}
|
||||
|
||||
batchSize := 100
|
||||
successCount = 0
|
||||
failCount = 0
|
||||
failedIndexes = make([]int64, 0)
|
||||
|
||||
for i := 0; i < len(reqs); i += batchSize {
|
||||
end := i + batchSize
|
||||
if end > len(reqs) {
|
||||
end = len(reqs)
|
||||
}
|
||||
|
||||
batch := reqs[i:end]
|
||||
entityList := make([]*entity.UnitReportSum, len(batch))
|
||||
|
||||
for j, req := range batch {
|
||||
var entityData entity.UnitReportSum
|
||||
if err = gconv.Struct(req, &entityData); err != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+j))
|
||||
continue
|
||||
}
|
||||
entityList[j] = &entityData
|
||||
}
|
||||
|
||||
if len(entityList) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
_, err = gfdb.DB(ctx).Model(ctx, consts.UnitReportSumTable).Data(entityList).Insert()
|
||||
if err != nil {
|
||||
for k := range batch {
|
||||
_, singleErr := d.Insert(ctx, batch[k])
|
||||
if singleErr != nil {
|
||||
failCount++
|
||||
failedIndexes = append(failedIndexes, int64(i+k))
|
||||
} else {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
} else {
|
||||
successCount += int64(len(entityList))
|
||||
}
|
||||
}
|
||||
|
||||
return successCount, failCount, failedIndexes, nil
|
||||
}
|
||||
@@ -1,17 +1,15 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
consts1 "cid/consts/api-feature"
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/dict"
|
||||
entity "cid/model/entity/dict"
|
||||
"context"
|
||||
"strconv"
|
||||
"time"
|
||||
consts "dataengine/consts/public"
|
||||
dto "dataengine/model/dto/dict"
|
||||
entity "dataengine/model/entity/dict"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
@@ -20,16 +18,18 @@ var DatasourcePlatform = new(datasourcePlatformDao)
|
||||
type datasourcePlatformDao struct{}
|
||||
|
||||
// Insert 插入数据源平台
|
||||
func (d *datasourcePlatformDao) Insert(ctx context.Context, req *dto.CreateDatasourcePlatformReq) (ID int64, err error) {
|
||||
func (d *datasourcePlatformDao) Insert(ctx context.Context, req *dto.CreateDatasourcePlatformReq, createdBy string, tenantId uint64) (ID int64, err error) {
|
||||
var res *entity.DatasourcePlatform
|
||||
if err = gconv.Struct(req, &res); err != nil {
|
||||
return
|
||||
}
|
||||
// 设置创建时间
|
||||
// 设置创建时间和更新时间
|
||||
now := time.Now()
|
||||
res.CreatedAt = &now
|
||||
res.UpdatedAt = &now
|
||||
// 设置审计字段(服务端生成,不依赖前端入参)
|
||||
now := gtime.Now()
|
||||
res.TenantId = tenantId
|
||||
res.Creator = createdBy
|
||||
res.CreatedAt = now
|
||||
res.Updater = createdBy
|
||||
res.UpdatedAt = now
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).Data(&res).Insert()
|
||||
if err != nil {
|
||||
@@ -39,15 +39,17 @@ func (d *datasourcePlatformDao) Insert(ctx context.Context, req *dto.CreateDatas
|
||||
}
|
||||
|
||||
// Update 更新数据源平台
|
||||
func (d *datasourcePlatformDao) Update(ctx context.Context, req *dto.UpdateDatasourcePlatformReq) (rows int64, err error) {
|
||||
// 设置更新时间
|
||||
func (d *datasourcePlatformDao) Update(ctx context.Context, req *dto.UpdateDatasourcePlatformReq, updatedBy string, tenantId uint64) (rows int64, err error) {
|
||||
// 设置更新人和更新时间(服务端生成,不依赖前端入参)
|
||||
data := gconv.Map(req)
|
||||
data[entity.DatasourcePlatformCols.UpdatedAt] = time.Now()
|
||||
data[entity.DatasourcePlatformCols.Updater] = updatedBy
|
||||
data[entity.DatasourcePlatformCols.UpdatedAt] = gtime.Now()
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Data(data).
|
||||
OmitEmpty().
|
||||
Where(entity.DatasourcePlatformCols.ID, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.Id, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -56,9 +58,10 @@ func (d *datasourcePlatformDao) Update(ctx context.Context, req *dto.UpdateDatas
|
||||
}
|
||||
|
||||
// Delete 删除数据源平台
|
||||
func (d *datasourcePlatformDao) Delete(ctx context.Context, req *dto.DeleteDatasourcePlatformReq) (rows int64, err error) {
|
||||
func (d *datasourcePlatformDao) Delete(ctx context.Context, req *dto.DeleteDatasourcePlatformReq, tenantId uint64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.ID, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.Id, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -67,9 +70,10 @@ func (d *datasourcePlatformDao) Delete(ctx context.Context, req *dto.DeleteDatas
|
||||
}
|
||||
|
||||
// GetOne 获取单个数据源平台
|
||||
func (d *datasourcePlatformDao) GetOne(ctx context.Context, req *dto.GetDatasourcePlatformReq) (res *entity.DatasourcePlatform, err error) {
|
||||
func (d *datasourcePlatformDao) GetOne(ctx context.Context, req *dto.GetDatasourcePlatformReq, tenantId uint64) (res *entity.DatasourcePlatform, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.ID, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.Id, req.Id).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
One()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -79,9 +83,10 @@ func (d *datasourcePlatformDao) GetOne(ctx context.Context, req *dto.GetDatasour
|
||||
}
|
||||
|
||||
// GetByPlatformCode 根据平台编码获取数据源平台
|
||||
func (d *datasourcePlatformDao) GetByPlatformCode(ctx context.Context, platformCode string) (res *entity.DatasourcePlatform, err error) {
|
||||
func (d *datasourcePlatformDao) GetByPlatformCode(ctx context.Context, platformCode string, tenantId uint64) (res *entity.DatasourcePlatform, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.PlatformCode, platformCode).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
One()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -91,13 +96,13 @@ func (d *datasourcePlatformDao) GetByPlatformCode(ctx context.Context, platformC
|
||||
}
|
||||
|
||||
// Count 获取数据源平台数量
|
||||
func (d *datasourcePlatformDao) Count(ctx context.Context, req *dto.ListDatasourcePlatformReq) (count int, err error) {
|
||||
return d.buildListFilter(ctx, req).Count()
|
||||
func (d *datasourcePlatformDao) Count(ctx context.Context, req *dto.ListDatasourcePlatformReq, tenantId uint64) (count int, err error) {
|
||||
return d.buildListFilter(ctx, req, tenantId).Count()
|
||||
}
|
||||
|
||||
// List 获取数据源平台列表
|
||||
func (d *datasourcePlatformDao) List(ctx context.Context, req *dto.ListDatasourcePlatformReq) (res []entity.DatasourcePlatform, total int, err error) {
|
||||
model := d.buildListFilter(ctx, req)
|
||||
func (d *datasourcePlatformDao) List(ctx context.Context, req *dto.ListDatasourcePlatformReq, tenantId uint64) (res []entity.DatasourcePlatform, total int, err error) {
|
||||
model := d.buildListFilter(ctx, req, tenantId)
|
||||
model.OrderDesc(entity.DatasourcePlatformCols.CreatedAt)
|
||||
if req.Page != nil {
|
||||
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
|
||||
@@ -111,9 +116,12 @@ func (d *datasourcePlatformDao) List(ctx context.Context, req *dto.ListDatasourc
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *datasourcePlatformDao) buildListFilter(ctx context.Context, req *dto.ListDatasourcePlatformReq) *gdb.Model {
|
||||
func (d *datasourcePlatformDao) buildListFilter(ctx context.Context, req *dto.ListDatasourcePlatformReq, tenantId uint64) *gdb.Model {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).Model
|
||||
|
||||
// 租户隔离
|
||||
model.Where(entity.DatasourcePlatformCols.TenantId, tenantId)
|
||||
|
||||
// 关键字搜索(平台名称或编码)
|
||||
if !g.IsEmpty(req.Keyword) {
|
||||
model.WhereLike(entity.DatasourcePlatformCols.PlatformName, "%"+req.Keyword+"%")
|
||||
@@ -140,14 +148,15 @@ func (d *datasourcePlatformDao) buildListFilter(ctx context.Context, req *dto.Li
|
||||
}
|
||||
|
||||
// UpdateStatus 更新数据源平台状态
|
||||
func (d *datasourcePlatformDao) UpdateStatus(ctx context.Context, ID int64, status string, updatedBy string) (rows int64, err error) {
|
||||
func (d *datasourcePlatformDao) UpdateStatus(ctx context.Context, ID int64, status string, updatedBy string, tenantId uint64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Data(map[string]interface{}{
|
||||
entity.DatasourcePlatformCols.Status: status,
|
||||
entity.DatasourcePlatformCols.UpdatedBy: updatedBy,
|
||||
entity.DatasourcePlatformCols.UpdatedAt: time.Now(),
|
||||
entity.DatasourcePlatformCols.Updater: updatedBy,
|
||||
entity.DatasourcePlatformCols.UpdatedAt: gtime.Now(),
|
||||
}).
|
||||
Where(entity.DatasourcePlatformCols.ID, ID).
|
||||
Where(entity.DatasourcePlatformCols.Id, ID).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -156,12 +165,13 @@ func (d *datasourcePlatformDao) UpdateStatus(ctx context.Context, ID int64, stat
|
||||
}
|
||||
|
||||
// ExistsByPlatformCode 检查平台编码是否存在
|
||||
func (d *datasourcePlatformDao) ExistsByPlatformCode(ctx context.Context, platformCode string, excludeId ...int64) (exists bool, err error) {
|
||||
func (d *datasourcePlatformDao) ExistsByPlatformCode(ctx context.Context, platformCode string, tenantId uint64, excludeId ...int64) (exists bool, err error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.PlatformCode, platformCode)
|
||||
Where(entity.DatasourcePlatformCols.PlatformCode, platformCode).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId)
|
||||
|
||||
if len(excludeId) > 0 && excludeId[0] > 0 {
|
||||
model.WhereNot(entity.DatasourcePlatformCols.ID, excludeId[0])
|
||||
model.WhereNot(entity.DatasourcePlatformCols.Id, excludeId[0])
|
||||
}
|
||||
|
||||
count, err := model.Count()
|
||||
@@ -172,9 +182,10 @@ func (d *datasourcePlatformDao) ExistsByPlatformCode(ctx context.Context, platfo
|
||||
}
|
||||
|
||||
// ListActivePlatforms 获取所有启用的平台
|
||||
func (d *datasourcePlatformDao) ListActivePlatforms(ctx context.Context) (res []entity.DatasourcePlatform, err error) {
|
||||
func (d *datasourcePlatformDao) ListActivePlatforms(ctx context.Context, tenantId uint64) (res []entity.DatasourcePlatform, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.Status, consts1.PlatformStatusActive).
|
||||
Where(entity.DatasourcePlatformCols.Status, "ACTIVE").
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
OrderAsc(entity.DatasourcePlatformCols.PlatformName).
|
||||
All()
|
||||
if err != nil {
|
||||
@@ -185,11 +196,13 @@ func (d *datasourcePlatformDao) ListActivePlatforms(ctx context.Context) (res []
|
||||
}
|
||||
|
||||
// GetPlatformStatistics 获取平台统计信息
|
||||
func (d *datasourcePlatformDao) GetPlatformStatistics(ctx context.Context) (stats map[string]int64, err error) {
|
||||
func (d *datasourcePlatformDao) GetPlatformStatistics(ctx context.Context, tenantId uint64) (stats map[string]int64, err error) {
|
||||
stats = make(map[string]int64)
|
||||
|
||||
// 总平台数
|
||||
total, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).Count()
|
||||
total, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -197,7 +210,8 @@ func (d *datasourcePlatformDao) GetPlatformStatistics(ctx context.Context) (stat
|
||||
|
||||
// 启用平台数
|
||||
active, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.Status, consts1.MappingStatusActive).
|
||||
Where(entity.DatasourcePlatformCols.Status, "ACTIVE").
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -212,6 +226,7 @@ func (d *datasourcePlatformDao) GetPlatformStatistics(ctx context.Context) (stat
|
||||
for _, authType := range authTypes {
|
||||
count, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Where(entity.DatasourcePlatformCols.AuthType, authType).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Count()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -223,14 +238,15 @@ func (d *datasourcePlatformDao) GetPlatformStatistics(ctx context.Context) (stat
|
||||
}
|
||||
|
||||
// BatchUpdateStatus 批量更新平台状态
|
||||
func (d *datasourcePlatformDao) BatchUpdateStatus(ctx context.Context, ids []int64, status string, updatedBy string) (rows int64, err error) {
|
||||
func (d *datasourcePlatformDao) BatchUpdateStatus(ctx context.Context, ids []int64, status string, updatedBy string, tenantId uint64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.DatasourcePlatformTable).
|
||||
Data(map[string]interface{}{
|
||||
entity.DatasourcePlatformCols.Status: status,
|
||||
entity.DatasourcePlatformCols.UpdatedBy: updatedBy,
|
||||
entity.DatasourcePlatformCols.UpdatedAt: strconv.FormatInt(time.Now().Unix(), 10),
|
||||
entity.DatasourcePlatformCols.Updater: updatedBy,
|
||||
entity.DatasourcePlatformCols.UpdatedAt: gtime.Now(),
|
||||
}).
|
||||
WhereIn(entity.DatasourcePlatformCols.ID, ids).
|
||||
WhereIn(entity.DatasourcePlatformCols.Id, ids).
|
||||
Where(entity.DatasourcePlatformCols.TenantId, tenantId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/dict"
|
||||
entity "cid/model/entity/dict"
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
var FieldMappingConfig = new(fieldMappingConfigDao)
|
||||
|
||||
type fieldMappingConfigDao struct{}
|
||||
|
||||
// Insert 插入字段映射配置
|
||||
func (d *fieldMappingConfigDao) Insert(ctx context.Context, req *dto.CreateFieldMappingConfigReq) (id int64, err error) {
|
||||
var config entity.FieldMappingConfig
|
||||
if err = gconv.Struct(req, &config); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now()
|
||||
config.CreatedTime = now
|
||||
config.UpdatedTime = now
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).Data(&config).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.LastInsertId()
|
||||
}
|
||||
|
||||
// Update 更新字段映射配置
|
||||
func (d *fieldMappingConfigDao) Update(ctx context.Context, req *dto.UpdateFieldMappingConfigReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).Data(&req).OmitEmpty().Where(entity.FieldMappingConfigCol.Id, req.Id).Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// Delete 删除字段映射配置
|
||||
func (d *fieldMappingConfigDao) Delete(ctx context.Context, req *dto.DeleteFieldMappingConfigReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.Id, req.Id).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// GetOne 获取单个字段映射配置
|
||||
func (d *fieldMappingConfigDao) GetOne(ctx context.Context, req *dto.GetFieldMappingConfigReq) (res *entity.FieldMappingConfig, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.Id, req.Id).
|
||||
One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Struct(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// Count 获取字段映射配置数量
|
||||
func (d *fieldMappingConfigDao) Count(ctx context.Context, req *dto.ListFieldMappingConfigReq) (count int, err error) {
|
||||
return d.buildListFilter(ctx, req).Count()
|
||||
}
|
||||
|
||||
// List 获取字段映射配置列表
|
||||
func (d *fieldMappingConfigDao) List(ctx context.Context, req *dto.ListFieldMappingConfigReq) (res []entity.FieldMappingConfig, total int, err error) {
|
||||
model := d.buildListFilter(ctx, req)
|
||||
model.OrderDesc(entity.FieldMappingConfigCol.CreatedTime)
|
||||
|
||||
if req.Page != nil {
|
||||
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
|
||||
}
|
||||
|
||||
r, total, err := model.AllAndCount(false)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *fieldMappingConfigDao) buildListFilter(ctx context.Context, req *dto.ListFieldMappingConfigReq) *gdb.Model {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).Model
|
||||
|
||||
if !g.IsEmpty(req.Keyword) {
|
||||
model.WhereLike(entity.FieldMappingConfigCol.ConfigName, "%"+req.Keyword+"%").
|
||||
WhereOrLike(entity.FieldMappingConfigCol.SourceFieldDesc, "%"+req.Keyword+"%").
|
||||
WhereOrLike(entity.FieldMappingConfigCol.TargetFieldDesc, "%"+req.Keyword+"%")
|
||||
}
|
||||
|
||||
if !g.IsEmpty(req.ConfigName) {
|
||||
model.Where(entity.FieldMappingConfigCol.ConfigName, req.ConfigName)
|
||||
}
|
||||
if !g.IsEmpty(req.VendorName) {
|
||||
model.Where(entity.FieldMappingConfigCol.VendorName, req.VendorName)
|
||||
}
|
||||
if !g.IsEmpty(req.ApiName) {
|
||||
model.Where(entity.FieldMappingConfigCol.ApiName, req.ApiName)
|
||||
}
|
||||
if !g.IsEmpty(req.ApiVersion) {
|
||||
model.Where(entity.FieldMappingConfigCol.ApiVersion, req.ApiVersion)
|
||||
}
|
||||
if !g.IsEmpty(req.SourceField) {
|
||||
model.Where(entity.FieldMappingConfigCol.SourceField, req.SourceField)
|
||||
}
|
||||
if !g.IsEmpty(req.TargetField) {
|
||||
model.Where(entity.FieldMappingConfigCol.TargetField, req.TargetField)
|
||||
}
|
||||
if !g.IsEmpty(req.TransformType) {
|
||||
model.Where(entity.FieldMappingConfigCol.TransformType, req.TransformType)
|
||||
}
|
||||
if !g.IsEmpty(req.BusinessDomain) {
|
||||
model.Where(entity.FieldMappingConfigCol.BusinessDomain, req.BusinessDomain)
|
||||
}
|
||||
if !g.IsEmpty(req.FieldGroup) {
|
||||
model.Where(entity.FieldMappingConfigCol.FieldGroup, req.FieldGroup)
|
||||
}
|
||||
if req.IsActive != nil {
|
||||
model.Where(entity.FieldMappingConfigCol.IsActive, *req.IsActive)
|
||||
}
|
||||
|
||||
model.OmitEmptyWhere()
|
||||
return model
|
||||
}
|
||||
|
||||
// UpdateStatus 更新配置状态
|
||||
func (d *fieldMappingConfigDao) UpdateStatus(ctx context.Context, id int64, isActive bool) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Data(g.Map{
|
||||
"is_active": isActive,
|
||||
"updated_time": time.Now(),
|
||||
}).
|
||||
Where(entity.FieldMappingConfigCol.Id, id).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// GetByIds 根据ID列表获取配置列表
|
||||
func (d *fieldMappingConfigDao) GetByIds(ctx context.Context, ids []int64) (res []entity.FieldMappingConfig, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
WhereIn(entity.FieldMappingConfigCol.Id, ids).
|
||||
All()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// GetByVendorAndApi 根据厂商和接口获取字段映射配置
|
||||
func (d *fieldMappingConfigDao) GetByVendorAndApi(ctx context.Context, vendorName, apiName, apiVersion string, isActive *bool) (res []*entity.FieldMappingConfig, err error) {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.VendorName, vendorName).
|
||||
Where(entity.FieldMappingConfigCol.ApiName, apiName)
|
||||
|
||||
if !g.IsEmpty(apiVersion) {
|
||||
model.Where(entity.FieldMappingConfigCol.ApiVersion, apiVersion)
|
||||
}
|
||||
|
||||
if isActive != nil {
|
||||
model.Where(entity.FieldMappingConfigCol.IsActive, *isActive)
|
||||
}
|
||||
|
||||
model.OrderDesc(entity.FieldMappingConfigCol.Priority).
|
||||
OrderAsc(entity.FieldMappingConfigCol.Id)
|
||||
|
||||
r, err := model.All()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// CheckDuplicate 检查重复配置
|
||||
func (d *fieldMappingConfigDao) CheckDuplicate(ctx context.Context, vendorName, apiName, sourceField, targetField string, excludeId int64) (exists bool, err error) {
|
||||
ctx = context.WithValue(ctx, "user", &beans.User{UserName: "admin"})
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.VendorName, vendorName).
|
||||
Where(entity.FieldMappingConfigCol.ApiName, apiName).
|
||||
Where(entity.FieldMappingConfigCol.SourceField, sourceField).
|
||||
Where(entity.FieldMappingConfigCol.TargetField, targetField)
|
||||
|
||||
if excludeId > 0 {
|
||||
model.WhereNot(entity.FieldMappingConfigCol.Id, excludeId)
|
||||
}
|
||||
|
||||
count, err := model.Count()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
exists = count > 0
|
||||
return
|
||||
}
|
||||
|
||||
// GetActiveConfigsByBusinessDomain 根据业务域获取启用的配置
|
||||
func (d *fieldMappingConfigDao) GetActiveConfigsByBusinessDomain(ctx context.Context, businessDomain string) (res []entity.FieldMappingConfig, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.BusinessDomain, businessDomain).
|
||||
Where(entity.FieldMappingConfigCol.IsActive, true).
|
||||
OrderDesc(entity.FieldMappingConfigCol.Priority).
|
||||
OrderAsc(entity.FieldMappingConfigCol.Id).
|
||||
All()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&res)
|
||||
return
|
||||
}
|
||||
|
||||
// GetFieldGroupsByVendorApi 获取指定厂商接口的字段分组
|
||||
func (d *fieldMappingConfigDao) GetFieldGroupsByVendorApi(ctx context.Context, vendorName, apiName string) (groups []string, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Fields(entity.FieldMappingConfigCol.FieldGroup).
|
||||
Where(entity.FieldMappingConfigCol.VendorName, vendorName).
|
||||
Where(entity.FieldMappingConfigCol.ApiName, apiName).
|
||||
Where(entity.FieldMappingConfigCol.IsActive, true).
|
||||
Group(entity.FieldMappingConfigCol.FieldGroup).
|
||||
All()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, record := range r {
|
||||
group := record.Map()[entity.FieldMappingConfigCol.FieldGroup]
|
||||
if groupStr, ok := group.(string); ok && groupStr != "" {
|
||||
groups = append(groups, groupStr)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeleteExpiredConfigs 删除已过期的配置
|
||||
func (d *fieldMappingConfigDao) DeleteExpiredConfigs(ctx context.Context) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where("expiry_date IS NOT NULL AND expiry_date < ?", time.Now()).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return r.RowsAffected()
|
||||
}
|
||||
|
||||
// GetConfigsEffectiveNow 获取当前生效的配置
|
||||
func (d *fieldMappingConfigDao) GetConfigsEffectiveNow(ctx context.Context) (res []entity.FieldMappingConfig, err error) {
|
||||
now := time.Now()
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.FieldMappingConfigTable).
|
||||
Where(entity.FieldMappingConfigCol.IsActive, true).
|
||||
Where("(effective_date IS NULL OR effective_date <= ?)", now).
|
||||
Where("(expiry_date IS NULL OR expiry_date > ?)", now).
|
||||
OrderDesc(entity.FieldMappingConfigCol.Priority).
|
||||
OrderAsc(entity.FieldMappingConfigCol.Id).
|
||||
All()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = r.Structs(&res)
|
||||
return
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
package dict
|
||||
|
||||
import (
|
||||
consts "cid/consts/public"
|
||||
dto "cid/model/dto/dict"
|
||||
entity "cid/model/entity/dict"
|
||||
"context"
|
||||
consts "dataengine/consts/public"
|
||||
dto "dataengine/model/dto/dict"
|
||||
entity "dataengine/model/entity/dict"
|
||||
|
||||
"gitea.com/red-future/common/db/gfdb"
|
||||
"gitea.redpowerfuture.com/red-future/common/db/gfdb"
|
||||
"github.com/gogf/gf/v2/database/gdb"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
@@ -17,11 +18,19 @@ var ApiInterface = new(apiInterfaceDao)
|
||||
type apiInterfaceDao struct{}
|
||||
|
||||
// Insert 插入接口
|
||||
func (d *apiInterfaceDao) Insert(ctx context.Context, req *dto.CreateApiInterfaceReq) (id int64, err error) {
|
||||
func (d *apiInterfaceDao) Insert(ctx context.Context, req *dto.CreateApiInterfaceReq, creator string, tenantId uint64) (id int64, err error) {
|
||||
var res *entity.ApiInterface
|
||||
if err = gconv.Struct(req, &res); err != nil {
|
||||
return
|
||||
}
|
||||
// 设置审计字段(服务端生成,不依赖前端入参)
|
||||
now := gtime.Now()
|
||||
res.TenantId = tenantId
|
||||
res.Creator = creator
|
||||
res.CreatedAt = now
|
||||
res.Updater = creator
|
||||
res.UpdatedAt = now
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).Data(&res).Insert()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -30,8 +39,18 @@ func (d *apiInterfaceDao) Insert(ctx context.Context, req *dto.CreateApiInterfac
|
||||
}
|
||||
|
||||
// Update 更新接口
|
||||
func (d *apiInterfaceDao) Update(ctx context.Context, req *dto.UpdateApiInterfaceReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).Data(&req).OmitEmpty().Where(entity.ApiInterfaceCols.Id, req.Id).Update()
|
||||
func (d *apiInterfaceDao) Update(ctx context.Context, req *dto.UpdateApiInterfaceReq, updater string, tenantId uint64) (rows int64, err error) {
|
||||
// 设置更新人和更新时间(服务端生成,不依赖前端入参)
|
||||
data := gconv.Map(req)
|
||||
data[entity.ApiInterfaceCols.Updater] = updater
|
||||
data[entity.ApiInterfaceCols.UpdatedAt] = gtime.Now()
|
||||
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).
|
||||
Data(data).
|
||||
OmitEmpty().
|
||||
Where(entity.ApiInterfaceCols.Id, req.Id).
|
||||
Where(entity.ApiInterfaceCols.TenantId, tenantId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -39,8 +58,11 @@ func (d *apiInterfaceDao) Update(ctx context.Context, req *dto.UpdateApiInterfac
|
||||
}
|
||||
|
||||
// Delete 删除接口
|
||||
func (d *apiInterfaceDao) Delete(ctx context.Context, req *dto.DeleteApiInterfaceReq) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).Where(entity.ApiInterfaceCols.Id, req.Id).Delete()
|
||||
func (d *apiInterfaceDao) Delete(ctx context.Context, req *dto.DeleteApiInterfaceReq, tenantId uint64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).
|
||||
Where(entity.ApiInterfaceCols.Id, req.Id).
|
||||
Where(entity.ApiInterfaceCols.TenantId, tenantId).
|
||||
Delete()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -48,8 +70,11 @@ func (d *apiInterfaceDao) Delete(ctx context.Context, req *dto.DeleteApiInterfac
|
||||
}
|
||||
|
||||
// GetOne 获取单个接口
|
||||
func (d *apiInterfaceDao) GetOne(ctx context.Context, req *dto.GetApiInterfaceReq) (res *entity.ApiInterface, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).Where(entity.ApiInterfaceCols.Id, req.Id).One()
|
||||
func (d *apiInterfaceDao) GetOne(ctx context.Context, req *dto.GetApiInterfaceReq, tenantId uint64) (res *entity.ApiInterface, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).
|
||||
Where(entity.ApiInterfaceCols.Id, req.Id).
|
||||
Where(entity.ApiInterfaceCols.TenantId, tenantId).
|
||||
One()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -58,13 +83,13 @@ func (d *apiInterfaceDao) GetOne(ctx context.Context, req *dto.GetApiInterfaceRe
|
||||
}
|
||||
|
||||
// Count 获取接口数量
|
||||
func (d *apiInterfaceDao) Count(ctx context.Context, req *dto.ListApiInterfaceReq) (count int, err error) {
|
||||
return d.buildListFilter(ctx, req).Count()
|
||||
func (d *apiInterfaceDao) Count(ctx context.Context, req *dto.ListApiInterfaceReq, tenantId uint64) (count int, err error) {
|
||||
return d.buildListFilter(ctx, req, tenantId).Count()
|
||||
}
|
||||
|
||||
// List 获取接口列表
|
||||
func (d *apiInterfaceDao) List(ctx context.Context, req *dto.ListApiInterfaceReq) (res []entity.ApiInterface, total int, err error) {
|
||||
model := d.buildListFilter(ctx, req)
|
||||
func (d *apiInterfaceDao) List(ctx context.Context, req *dto.ListApiInterfaceReq, tenantId uint64) (res []entity.ApiInterface, total int, err error) {
|
||||
model := d.buildListFilter(ctx, req, tenantId)
|
||||
model.OrderDesc(entity.ApiInterfaceCols.CreatedAt)
|
||||
if req.Page != nil {
|
||||
model.Page(int(req.Page.PageNum), int(req.Page.PageSize))
|
||||
@@ -78,26 +103,45 @@ func (d *apiInterfaceDao) List(ctx context.Context, req *dto.ListApiInterfaceReq
|
||||
}
|
||||
|
||||
// buildListFilter 构建列表查询的过滤条件
|
||||
func (d *apiInterfaceDao) buildListFilter(ctx context.Context, req *dto.ListApiInterfaceReq) *gdb.Model {
|
||||
func (d *apiInterfaceDao) buildListFilter(ctx context.Context, req *dto.ListApiInterfaceReq, tenantId uint64) *gdb.Model {
|
||||
model := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).Model
|
||||
|
||||
// 租户隔离
|
||||
model.Where(entity.ApiInterfaceCols.TenantId, tenantId)
|
||||
|
||||
if !g.IsEmpty(req.Keyword) {
|
||||
model.WhereLike(entity.ApiInterfaceCols.Name, "%"+req.Keyword+"%")
|
||||
model.WhereOrLike(entity.ApiInterfaceCols.Code, "%"+req.Keyword+"%")
|
||||
}
|
||||
model.Where(entity.ApiInterfaceCols.PlatformId, req.PlatformId)
|
||||
model.Where(entity.ApiInterfaceCols.Name, req.Name)
|
||||
model.Where(entity.ApiInterfaceCols.Code, req.Code)
|
||||
model.Where(entity.ApiInterfaceCols.Method, req.Method)
|
||||
model.Where(entity.ApiInterfaceCols.Status, req.Status)
|
||||
if req.PlatformId > 0 {
|
||||
model.Where(entity.ApiInterfaceCols.PlatformId, req.PlatformId)
|
||||
}
|
||||
if !g.IsEmpty(req.Name) {
|
||||
model.Where(entity.ApiInterfaceCols.Name, req.Name)
|
||||
}
|
||||
if !g.IsEmpty(req.Code) {
|
||||
model.Where(entity.ApiInterfaceCols.Code, req.Code)
|
||||
}
|
||||
if !g.IsEmpty(req.Method) {
|
||||
model.Where(entity.ApiInterfaceCols.Method, req.Method)
|
||||
}
|
||||
if !g.IsEmpty(req.Status) {
|
||||
model.Where(entity.ApiInterfaceCols.Status, req.Status)
|
||||
}
|
||||
model.OmitEmptyWhere()
|
||||
return model
|
||||
}
|
||||
|
||||
// UpdateStatus 更新接口状态
|
||||
func (d *apiInterfaceDao) UpdateStatus(ctx context.Context, id int64, status string) (rows int64, err error) {
|
||||
func (d *apiInterfaceDao) UpdateStatus(ctx context.Context, id int64, status string, updater string, tenantId uint64) (rows int64, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).
|
||||
Data(map[string]interface{}{"status": status}).
|
||||
Data(map[string]interface{}{
|
||||
entity.ApiInterfaceCols.Status: status,
|
||||
entity.ApiInterfaceCols.Updater: updater,
|
||||
entity.ApiInterfaceCols.UpdatedAt: gtime.Now(),
|
||||
}).
|
||||
Where(entity.ApiInterfaceCols.Id, id).
|
||||
Where(entity.ApiInterfaceCols.TenantId, tenantId).
|
||||
Update()
|
||||
if err != nil {
|
||||
return
|
||||
@@ -106,9 +150,10 @@ func (d *apiInterfaceDao) UpdateStatus(ctx context.Context, id int64, status str
|
||||
}
|
||||
|
||||
// GetByIds 根据ID列表获取接口列表
|
||||
func (d *apiInterfaceDao) GetByIds(ctx context.Context, ids []int64) (res []entity.ApiInterface, err error) {
|
||||
func (d *apiInterfaceDao) GetByIds(ctx context.Context, ids []int64, tenantId uint64) (res []entity.ApiInterface, err error) {
|
||||
r, err := gfdb.DB(ctx).Model(ctx, consts.ApiInterfaceTable).
|
||||
WhereIn(entity.ApiInterfaceCols.Id, ids).
|
||||
Where(entity.ApiInterfaceCols.TenantId, tenantId).
|
||||
All()
|
||||
if err != nil {
|
||||
return
|
||||
|
||||
@@ -0,0 +1,359 @@
|
||||
-- ============================================================
|
||||
-- 报表引擎 — 快手电商演示数据初始化脚本
|
||||
-- ============================================================
|
||||
-- 使用方式:
|
||||
-- PGPASSWORD='Bjang09@686^*^' psql -h 116.204.74.41 \
|
||||
-- -p 15432 -U postgres -d engine -f demo_kuaishou.sql
|
||||
-- ============================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- ============================================================
|
||||
-- 第〇步:确保系统表存在(CREATE TABLE IF NOT EXISTS)
|
||||
-- ============================================================
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_business_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
business_name VARCHAR(128) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
config JSONB DEFAULT '{}',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_report_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
report_name VARCHAR(128) NOT NULL,
|
||||
description TEXT DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
stat_table_name VARCHAR(128) NOT NULL,
|
||||
stat_table_comment VARCHAR(256) DEFAULT '',
|
||||
date_field VARCHAR(64) DEFAULT 'stat_date',
|
||||
primary_keys JSONB DEFAULT '["id"]'::jsonb,
|
||||
conflict_keys JSONB DEFAULT '["stat_date"]'::jsonb,
|
||||
config JSONB DEFAULT '{}',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_code UNIQUE (tenant_id, business_code, report_code)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_field_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
field_code VARCHAR(64) NOT NULL,
|
||||
field_name VARCHAR(128) NOT NULL,
|
||||
field_type VARCHAR(32) NOT NULL,
|
||||
data_type VARCHAR(32) NOT NULL DEFAULT 'STRING',
|
||||
field_role VARCHAR(32) NOT NULL,
|
||||
is_aggregatable BOOLEAN DEFAULT FALSE,
|
||||
is_filterable BOOLEAN DEFAULT TRUE,
|
||||
is_queryable BOOLEAN DEFAULT TRUE,
|
||||
is_sortable BOOLEAN DEFAULT TRUE,
|
||||
default_aggregate VARCHAR(32) DEFAULT '',
|
||||
valid_aggregates JSONB DEFAULT '[]'::jsonb,
|
||||
filter_operators JSONB DEFAULT '["=","!=",">","<",">=","<=","IN","LIKE","BETWEEN"]'::jsonb,
|
||||
expression VARCHAR(512) DEFAULT '',
|
||||
expression_type VARCHAR(32) DEFAULT '',
|
||||
format_pattern VARCHAR(64) DEFAULT '',
|
||||
unit VARCHAR(32) DEFAULT '',
|
||||
dict_code VARCHAR(64) DEFAULT '',
|
||||
sort_order INT DEFAULT 0,
|
||||
group_name VARCHAR(64) DEFAULT '',
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_field_code UNIQUE (tenant_id, business_code, report_code, field_code)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_extract_config (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
extract_code VARCHAR(64) NOT NULL,
|
||||
extract_name VARCHAR(128) NOT NULL,
|
||||
source_table_name VARCHAR(128) NOT NULL,
|
||||
source_table_alias VARCHAR(64) DEFAULT '',
|
||||
target_table_name VARCHAR(128) NOT NULL,
|
||||
is_enabled BOOLEAN DEFAULT TRUE,
|
||||
extract_type VARCHAR(32) NOT NULL DEFAULT 'FULL',
|
||||
extract_mode VARCHAR(32) NOT NULL DEFAULT 'DIRECT',
|
||||
extract_key_field VARCHAR(64) DEFAULT '',
|
||||
extract_key_format VARCHAR(64) DEFAULT '',
|
||||
group_by_fields JSONB DEFAULT '[]'::jsonb,
|
||||
filter_expression TEXT DEFAULT '',
|
||||
join_configs JSONB DEFAULT '[]'::jsonb,
|
||||
field_mappings JSONB DEFAULT '[]'::jsonb,
|
||||
transform_rules JSONB DEFAULT '[]'::jsonb,
|
||||
batch_size INT DEFAULT 1000,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
|
||||
creator VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updater VARCHAR(64) DEFAULT '',
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
deleted_at TIMESTAMP WITH TIME ZONE,
|
||||
CONSTRAINT uk_business_report_extract_code UNIQUE (tenant_id, business_code, report_code, extract_code)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS report_extract_log (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
tenant_id BIGINT NOT NULL DEFAULT 0,
|
||||
business_code VARCHAR(64) NOT NULL,
|
||||
report_code VARCHAR(64) NOT NULL,
|
||||
extract_code VARCHAR(64) NOT NULL,
|
||||
stat_date VARCHAR(16) NOT NULL,
|
||||
extract_type VARCHAR(32) NOT NULL,
|
||||
status VARCHAR(16) NOT NULL DEFAULT 'RUNNING',
|
||||
total_count INT DEFAULT 0,
|
||||
success_count INT DEFAULT 0,
|
||||
fail_count INT DEFAULT 0,
|
||||
start_time TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
end_time TIMESTAMP WITH TIME ZONE,
|
||||
error_message TEXT DEFAULT '',
|
||||
executor VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
CONSTRAINT uk_extract_keys UNIQUE (tenant_id, business_code, report_code, extract_code, stat_date)
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第一步:创建源数据表 + 模拟数据
|
||||
-- ============================================================
|
||||
|
||||
-- 店铺表
|
||||
DROP TABLE IF EXISTS demo_kuaishou_shop CASCADE;
|
||||
CREATE TABLE demo_kuaishou_shop (
|
||||
shop_id VARCHAR(64) PRIMARY KEY,
|
||||
shop_name VARCHAR(128) NOT NULL,
|
||||
shop_level VARCHAR(16) DEFAULT '普通',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
INSERT INTO demo_kuaishou_shop VALUES
|
||||
('KS001', '快手官方旗舰店', '旗舰'),
|
||||
('KS002', '数码3C专营店', '高级'),
|
||||
('KS003', '美妆个护优选', '普通'),
|
||||
('KS004', '食品生鲜超市', '高级'),
|
||||
('KS005', '服装潮流馆', '普通');
|
||||
|
||||
-- 订单明细表
|
||||
DROP TABLE IF EXISTS demo_kuaishou_orders CASCADE;
|
||||
CREATE TABLE demo_kuaishou_orders (
|
||||
id BIGSERIAL PRIMARY KEY,
|
||||
order_no VARCHAR(64) NOT NULL,
|
||||
shop_id VARCHAR(64) NOT NULL,
|
||||
order_status VARCHAR(16) NOT NULL DEFAULT '已完成',
|
||||
product_name VARCHAR(256),
|
||||
product_category VARCHAR(64),
|
||||
quantity INT DEFAULT 1,
|
||||
unit_price NUMERIC(12,2) DEFAULT 0,
|
||||
order_amount NUMERIC(12,2) DEFAULT 0,
|
||||
refund_amount NUMERIC(12,2) DEFAULT 0,
|
||||
shipping_cost NUMERIC(10,2) DEFAULT 0,
|
||||
payment_method VARCHAR(32) DEFAULT '微信',
|
||||
buyer_id VARCHAR(64) DEFAULT '',
|
||||
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- 生成30天模拟订单
|
||||
DO $$
|
||||
DECLARE
|
||||
v_date DATE;
|
||||
v_order_date DATE := CURRENT_DATE - INTERVAL '30 days';
|
||||
v_order_count INT;
|
||||
v_shop_id VARCHAR(64);
|
||||
v_shop_ids VARCHAR[] := ARRAY['KS001','KS002','KS003','KS004','KS005'];
|
||||
v_categories VARCHAR[] := ARRAY['手机','电脑','美妆','食品','服装'];
|
||||
v_payments VARCHAR[] := ARRAY['微信','支付宝','快手支付'];
|
||||
v_statuses VARCHAR[] := ARRAY['已完成','已完成','已完成','已完成','已完成','已完成','已完成','已取消','已完成'];
|
||||
v_price NUMERIC(12,2);
|
||||
v_qty INT;
|
||||
BEGIN
|
||||
FOR day_offset IN 0..29 LOOP
|
||||
v_date := v_order_date + day_offset;
|
||||
v_order_count := 20 + floor(random() * 31)::int;
|
||||
FOR i IN 1..v_order_count LOOP
|
||||
v_shop_id := v_shop_ids[1 + floor(random() * 5)::int];
|
||||
v_qty := 1 + floor(random() * 5)::int;
|
||||
v_price := round((50 + random() * 2000)::numeric, 2);
|
||||
INSERT INTO demo_kuaishou_orders (order_no, shop_id, order_status, product_name,
|
||||
product_category, quantity, unit_price, order_amount, refund_amount,
|
||||
shipping_cost, payment_method, buyer_id, created_at, updated_at)
|
||||
VALUES (
|
||||
'KS' || to_char(v_date, 'YYYYMMDD') || '-' || lpad(i::text, 4, '0'),
|
||||
v_shop_id, v_statuses[1 + floor(random() * 9)::int],
|
||||
'商品' || (1000 + floor(random() * 9000)::int),
|
||||
v_categories[1 + floor(random() * 5)::int],
|
||||
v_qty, v_price, round((v_price * v_qty)::numeric, 2),
|
||||
CASE WHEN random() < 0.1 THEN round((v_price * v_qty * random() * 0.5)::numeric, 2) ELSE 0 END,
|
||||
round((5 + random() * 15)::numeric, 2),
|
||||
v_payments[1 + floor(random() * 3)::int],
|
||||
'buyer_' || (10000 + floor(random() * 90000)::int),
|
||||
v_date + time '08:00:00' + (random() * interval '12 hours'),
|
||||
v_date + time '08:00:00' + (random() * interval '12 hours')
|
||||
);
|
||||
END LOOP;
|
||||
END LOOP;
|
||||
END $$;
|
||||
|
||||
ANALYZE demo_kuaishou_orders;
|
||||
|
||||
-- ============================================================
|
||||
-- 第二步:插入系统配置(先清空避免重复)
|
||||
-- ============================================================
|
||||
|
||||
DELETE FROM report_business_config WHERE business_code = 'KUAISHOU';
|
||||
DELETE FROM report_report_config WHERE business_code = 'KUAISHOU';
|
||||
DELETE FROM report_field_config WHERE business_code = 'KUAISHOU';
|
||||
DELETE FROM report_extract_config WHERE business_code = 'KUAISHOU';
|
||||
|
||||
-- 1. 业务配置
|
||||
INSERT INTO report_business_config (tenant_id, business_code, business_name, description, status, config, creator)
|
||||
VALUES (1, 'KUAISHOU', '快手电商', '快手电商平台订单数据', 'ACTIVE', '{}', 'system');
|
||||
|
||||
-- 2. 报表配置
|
||||
INSERT INTO report_report_config (tenant_id, business_code, report_code, report_name, description,
|
||||
status, stat_table_name, stat_table_comment, date_field, primary_keys, conflict_keys, config, creator)
|
||||
VALUES (1, 'KUAISHOU', 'shop_daily', '店铺日报',
|
||||
'按店铺+日期统计订单数据', 'ACTIVE',
|
||||
'stat_kuaishou_shop_daily', '快手电商店铺日报统计宽表',
|
||||
'stat_date', '["id"]'::jsonb, '["stat_date", "shop_id"]'::jsonb, '{}', 'system');
|
||||
|
||||
-- 3. 字段配置
|
||||
INSERT INTO report_field_config (tenant_id, business_code, report_code, field_code, field_name,
|
||||
field_type, data_type, field_role, is_aggregatable, is_filterable, is_queryable, is_sortable,
|
||||
default_aggregate, valid_aggregates, filter_operators, sort_order, group_name, status, creator)
|
||||
VALUES
|
||||
-- 维度
|
||||
(1, 'KUAISHOU', 'shop_daily', 'stat_date', '统计日期',
|
||||
'DATE', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=",">=","<=","BETWEEN"]'::jsonb, 1, '时间维度', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'order_no', '订单号',
|
||||
'STRING', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=","IN"]'::jsonb, 0, '订单维度', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'shop_id', '店铺ID',
|
||||
'STRING', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=","IN"]'::jsonb, 2, '店铺维度', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'shop_name', '店铺名称',
|
||||
'STRING', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=","LIKE"]'::jsonb, 3, '店铺维度', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'product_category', '商品类目',
|
||||
'STRING', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=","IN"]'::jsonb, 4, '商品维度', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'order_status', '订单状态',
|
||||
'STRING', 'STRING', 'DIMENSION', false, true, true, true,
|
||||
'', '[]'::jsonb, '["=","IN"]'::jsonb, 5, '订单维度', 'ACTIVE', 'system'),
|
||||
-- 指标
|
||||
(1, 'KUAISHOU', 'shop_daily', 'order_count', '订单数',
|
||||
'INT', 'INT', 'INDICATOR', true, false, true, true,
|
||||
'SUM', '["SUM","COUNT"]'::jsonb, '[]'::jsonb, 10, '交易指标', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'order_amount', '订单金额',
|
||||
'FLOAT', 'FLOAT', 'INDICATOR', true, false, true, true,
|
||||
'SUM', '["SUM","AVG","MAX","MIN"]'::jsonb, '[]'::jsonb, 11, '交易指标', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'refund_amount', '退款金额',
|
||||
'FLOAT', 'FLOAT', 'INDICATOR', true, false, true, true,
|
||||
'SUM', '["SUM","AVG"]'::jsonb, '[]'::jsonb, 12, '交易指标', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'shipping_cost', '运费',
|
||||
'FLOAT', 'FLOAT', 'INDICATOR', true, false, true, true,
|
||||
'SUM', '["SUM","AVG"]'::jsonb, '[]'::jsonb, 13, '交易指标', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'total_quantity', '商品数量',
|
||||
'INT', 'INT', 'INDICATOR', true, false, true, true,
|
||||
'SUM', '["SUM","AVG"]'::jsonb, '[]'::jsonb, 14, '交易指标', 'ACTIVE', 'system'),
|
||||
(1, 'KUAISHOU', 'shop_daily', 'avg_price', '平均单价',
|
||||
'FLOAT', 'FLOAT', 'INDICATOR', true, false, true, true,
|
||||
'AVG', '["AVG","MAX","MIN"]'::jsonb, '[]'::jsonb, 15, '交易指标', 'ACTIVE', 'system');
|
||||
|
||||
-- 4. 抽取配置(DIRECT 模式:逐行抽取,查询时动态聚合)
|
||||
INSERT INTO report_extract_config (tenant_id, business_code, report_code, extract_code, extract_name,
|
||||
source_table_name, source_table_alias, target_table_name, is_enabled,
|
||||
extract_type, extract_mode, extract_key_field, group_by_fields, filter_expression,
|
||||
field_mappings, join_configs, batch_size, status, creator)
|
||||
VALUES (1, 'KUAISHOU', 'shop_daily', 'shop_daily_direct', '店铺日订单(逐行,可自由组合查询)',
|
||||
'demo_kuaishou_orders', 'o', 'stat_kuaishou_shop_daily',
|
||||
true, 'INCREMENTAL', 'DIRECT', 'created_at',
|
||||
'[]'::jsonb,
|
||||
'o.order_status = ''已完成''',
|
||||
'[
|
||||
{"sourceField":"order_no", "targetField":"order_no", "fieldType":"STRING", "aggregateFunction":"", "defaultValue":""},
|
||||
{"sourceField":"shop_id", "targetField":"shop_id", "fieldType":"STRING", "aggregateFunction":"", "defaultValue":""},
|
||||
{"sourceField":"product_category", "targetField":"product_category", "fieldType":"STRING", "aggregateFunction":"", "defaultValue":""},
|
||||
{"sourceField":"order_status", "targetField":"order_status", "fieldType":"STRING", "aggregateFunction":"", "defaultValue":""},
|
||||
{"sourceField":"order_amount", "targetField":"order_amount", "fieldType":"FLOAT", "aggregateFunction":"", "defaultValue":0},
|
||||
{"sourceField":"refund_amount", "targetField":"refund_amount", "fieldType":"FLOAT", "aggregateFunction":"", "defaultValue":0},
|
||||
{"sourceField":"shipping_cost", "targetField":"shipping_cost", "fieldType":"FLOAT", "aggregateFunction":"", "defaultValue":0},
|
||||
{"sourceField":"quantity", "targetField":"total_quantity", "fieldType":"INT", "aggregateFunction":"", "defaultValue":0},
|
||||
{"sourceField":"unit_price", "targetField":"unit_price", "fieldType":"FLOAT", "aggregateFunction":"", "defaultValue":0}
|
||||
]'::jsonb,
|
||||
'[
|
||||
{
|
||||
"joinTable":"demo_kuaishou_shop",
|
||||
"joinAlias":"s",
|
||||
"joinType":"LEFT",
|
||||
"joinCondition":"o.shop_id = s.shop_id",
|
||||
"fieldMappings":[
|
||||
{"sourceField":"shop_name", "targetField":"shop_name", "fieldType":"STRING", "aggregateFunction":""},
|
||||
{"sourceField":"shop_level", "targetField":"shop_level", "fieldType":"STRING", "aggregateFunction":""}
|
||||
]
|
||||
}
|
||||
]'::jsonb,
|
||||
1000, 'ACTIVE', 'system'
|
||||
);
|
||||
|
||||
-- ============================================================
|
||||
-- 第三步:验证
|
||||
-- ============================================================
|
||||
|
||||
SELECT '✅ 源表数据' AS info, COUNT(*) AS 行数 FROM demo_kuaishou_orders
|
||||
UNION ALL
|
||||
SELECT '✅ 店铺数', COUNT(*) FROM demo_kuaishou_shop
|
||||
UNION ALL
|
||||
SELECT '✅ 业务配置', COUNT(*) FROM report_business_config WHERE business_code = 'KUAISHOU'
|
||||
UNION ALL
|
||||
SELECT '✅ 报表配置', COUNT(*) FROM report_report_config WHERE business_code = 'KUAISHOU'
|
||||
UNION ALL
|
||||
SELECT '✅ 字段配置', COUNT(*) FROM report_field_config WHERE business_code = 'KUAISHOU'
|
||||
UNION ALL
|
||||
SELECT '✅ 抽取配置', COUNT(*) FROM report_extract_config WHERE business_code = 'KUAISHOU';
|
||||
|
||||
SELECT '📅 数据日期范围:' AS info, MIN(created_at::date)::text || ' ~ ' || MAX(created_at::date)::text AS 范围 FROM demo_kuaishou_orders;
|
||||
|
||||
COMMIT;
|
||||
|
||||
-- ============================================================
|
||||
-- 使用说明
|
||||
-- ============================================================
|
||||
-- 1. 启动项目(会自动创建系统表)
|
||||
-- 2. 执行本脚本
|
||||
-- 3. 打开 http://localhost:3013/admin/report
|
||||
-- 4. 抽取配置 → 选"快手电商/shop_daily" → 点"执行抽取"
|
||||
-- 5. 数据查询 → 选"快手电商/shop_daily" → 自由选维度/指标查询
|
||||
-- ============================================================
|
||||
--
|
||||
-- 上线前清理 Demo 数据:
|
||||
-- DELETE FROM report_business_config WHERE business_code = 'KUAISHOU';
|
||||
-- DELETE FROM report_report_config WHERE business_code = 'KUAISHOU';
|
||||
-- DELETE FROM report_field_config WHERE business_code = 'KUAISHOU';
|
||||
-- DELETE FROM report_extract_config WHERE business_code = 'KUAISHOU';
|
||||
-- DELETE FROM report_extract_log WHERE business_code = 'KUAISHOU';
|
||||
-- DROP TABLE IF EXISTS stat_kuaishou_shop_daily CASCADE;
|
||||
-- DROP TABLE IF EXISTS demo_kuaishou_orders CASCADE;
|
||||
-- DROP TABLE IF EXISTS demo_kuaishou_shop CASCADE;
|
||||
-- ============================================================
|
||||
@@ -0,0 +1,568 @@
|
||||
# 数据引擎 — 平台与接口配置指南
|
||||
|
||||
> 适用版本:HDWL data-engine
|
||||
> 阅读对象:实施工程师、运维人员
|
||||
> 前置知识:了解 JSON 基本格式即可
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
1. [概念说明](#1-概念说明)
|
||||
2. [平台管理 (api_datasource_platform)](#2-平台管理)
|
||||
3. [接口管理 (api_interface)](#3-接口管理)
|
||||
4. [实战:新增一个平台](#4-实战新增一个平台)
|
||||
5. [常见问题](#5-常见问题)
|
||||
|
||||
---
|
||||
|
||||
## 1. 概念说明
|
||||
|
||||
```
|
||||
平台 (Platform) → 对接的第三方系统(如:快手电商、腾讯广告、钉钉)
|
||||
└── 接口 (Interface) → 平台提供的具体 API(如:订单列表、商品详情)
|
||||
└── 表 (Table) → 数据最终存储的数据库表
|
||||
```
|
||||
|
||||
**举例**:
|
||||
- **平台** = 快手电商
|
||||
- **接口1** = 订单列表 → 数据存入 `kuaishou_order_list` 表
|
||||
- **接口2** = 商品列表 → 数据存入 `kuaishou_item_list` 表
|
||||
- **接口3** = 售后单列表 → 数据存入 `kuaishou_refund_list` 表
|
||||
|
||||
系统会按照配置的**调度周期**,自动拉取每个接口的数据并存入对应的数据库表。
|
||||
|
||||
---
|
||||
|
||||
## 2. 平台管理
|
||||
|
||||
平台配置存储在 `api_datasource_platform` 表中,定义了一个第三方平台的基本信息和认证方式。
|
||||
|
||||
### 2.1 基础字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 | 示例 |
|
||||
|------|------|------|------|------|
|
||||
| `platform_code` | VARCHAR | ✅ | 平台唯一编码,不能重复 | `kuaishou`、`tencent` |
|
||||
| `platform_name` | VARCHAR | ✅ | 平台显示名称 | `快手电商`、`腾讯广告` |
|
||||
| `description` | VARCHAR | ❌ | 平台描述 | `快手电商开放平台数据同步` |
|
||||
| `status` | VARCHAR | ✅ | 状态,`ACTIVE`=启用,`INACTIVE`=停用 | `ACTIVE` |
|
||||
| `api_base_url` | VARCHAR | ✅ | API 域名,接口地址会拼接在此 URL 后面 | `https://openapi.kwaixiaodian.com` |
|
||||
|
||||
### 2.2 认证类型 (auth_type)
|
||||
|
||||
| 类型 | 说明 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| `TOKEN` | 简单 Token 认证,放在 Header 中 | 内部系统 |
|
||||
| `API_KEY` | API Key 认证,Token 放在请求参数中 | **快手电商**、钉钉 |
|
||||
| `OAUTH2` | OAuth 2.0 认证 | 腾讯广告 |
|
||||
| `SIGN` | 签名认证,使用 appKey + appSecret 签名 | 部分开放平台 |
|
||||
| `APP_SIGNATURE` | 应用签名认证,基于请求体 MD5 | 钉钉智能薪酬 |
|
||||
|
||||
### 2.3 认证配置 (auth_config)
|
||||
|
||||
`auth_config` 是一个 **JSON 对象**,根据不同的 `auth_type` 配置不同内容。
|
||||
|
||||
#### API_KEY 类型配置(快手电商示例)
|
||||
|
||||
```json
|
||||
{
|
||||
"sign_algorithm": "md5",
|
||||
"app_key": "ks651333099611149957",
|
||||
"app_secret": "JPUXG2CS3I7tqRWbKaLrYQ",
|
||||
"sign_secret": "7bc51baab818cf86e121a48d99ff3fe4",
|
||||
"token_in_query": true,
|
||||
"query_key": "access_token",
|
||||
"extra_query_params": {
|
||||
"timestamp": "{timestamp_ms}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `sign_algorithm` | 签名算法,可选 `md5`(小写输出)或 `md5_upper`(大写输出)或 `HMAC_SHA256` |
|
||||
| `app_key` | 平台分配的 AppKey(也叫 AppId、client_id),**快手签名必填** |
|
||||
| `app_secret` | 平台分配的 AppSecret,用于 OAuth 获取 Token |
|
||||
| `sign_secret` | 签名专用密钥(**快手特有**),平台分配,在"应用详情"中查看 |
|
||||
| `token_in_query` | `true`=Token 放在 URL 参数中;`false`=Token 放在 Header 中 |
|
||||
| `query_key` | Token 的参数名,默认 `access_token` |
|
||||
| `extra_query_params` | 额外需要拼接的参数,支持 `{timestamp}`(秒)、`{timestamp_ms}`(毫秒)、`{nonce}` 占位符 |
|
||||
|
||||
> **快手签名算法**(仅供参考,代码已实现):
|
||||
> 1. 所有参数按字母排序,拼接成 `key1=value1&key2=value2&...`
|
||||
> 2. 末尾追加 `&signSecret=你的signSecret`
|
||||
> 3. 对整个字符串取 MD5
|
||||
|
||||
#### SIGN 类型配置
|
||||
|
||||
```json
|
||||
{
|
||||
"sign_algorithm": "md5",
|
||||
"app_key": "your_app_key",
|
||||
"app_secret": "your_app_secret"
|
||||
}
|
||||
```
|
||||
|
||||
### 2.4 限流与重试字段
|
||||
|
||||
| 字段 | 类型 | 默认值 | 说明 |
|
||||
|------|------|--------|------|
|
||||
| `rate_limit_per_minute` | INT | 100 | 每分钟最大请求次数,防止触发平台限流 |
|
||||
| `rate_limit_per_hour` | INT | 3600 | 每小时最大请求次数 |
|
||||
| `concurrency_limit` | INT | 5 | 并发请求数 |
|
||||
| `request_timeout_ms` | INT | 30000 | 单次请求超时时间(毫秒) |
|
||||
| `max_retries` | INT | 3 | 请求失败后的最大重试次数 |
|
||||
| `retry_delay_ms` | INT | 1000 | 重试间隔(毫秒),每次翻倍 |
|
||||
|
||||
### 2.5 Token 与 API Key 字段
|
||||
|
||||
| 字段 | 说明 | 快手示例 |
|
||||
|------|------|----------|
|
||||
| `token` | 认证 Token / Refresh Token | 快手 OAuth 返回的 `refresh_token` |
|
||||
| `api_key` | API Key / Access Token | 快手 OAuth 返回的 `access_token` |
|
||||
| `client_id` | OAuth2 Client ID | 腾讯广告的 client_id |
|
||||
| `client_secret` | OAuth2 Client Secret | 腾讯广告的 client_secret |
|
||||
|
||||
---
|
||||
|
||||
## 3. 接口管理
|
||||
|
||||
接口配置存储在 `api_interface` 表中,定义了一个平台下每个 API 的请求方式、参数、响应解析规则和存储表结构。
|
||||
|
||||
### 3.1 基础字段
|
||||
|
||||
| 字段 | 类型 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| `platform_id` | INT | ✅ | 所属平台的 ID(关联 `api_datasource_platform.id`) |
|
||||
| `name` | VARCHAR | ✅ | 接口名称,如"订单列表" |
|
||||
| `code` | VARCHAR | ✅ | 接口编码,唯一标识,如 `order_list` |
|
||||
| `url` | VARCHAR | ✅ | API 路径,会拼接在平台 `api_base_url` 后面 |
|
||||
| `method` | VARCHAR | ✅ | HTTP 方法,`GET` 或 `POST` |
|
||||
| `status` | VARCHAR | ✅ | 状态,`active`=启用,`inactive`=停用 |
|
||||
| `auth_type` | VARCHAR | ✅ | 认证方式,`inherit`=继承平台配置 |
|
||||
|
||||
### 3.2 请求配置 (request_config)
|
||||
|
||||
`request_config` 是一个 **JSON 对象**,定义如何构造 API 请求参数。
|
||||
|
||||
#### 3.2.1 完整字段说明
|
||||
|
||||
```json
|
||||
{
|
||||
"page_param": "cursor",
|
||||
"page_size_param": "pageSize",
|
||||
"cursor_pagination": true,
|
||||
"pagination_mode": "offset",
|
||||
"page_size": 50,
|
||||
"initial_cursor": "",
|
||||
"method": "open.order.cursor.list",
|
||||
"version": 1,
|
||||
"signMethod": "MD5",
|
||||
"time_field": "updateTime",
|
||||
"time_field_mode": "range",
|
||||
"full_sync_start_time": 1700000000000,
|
||||
"queryType": 2,
|
||||
"parameters_location": "query",
|
||||
"body_wrapper_field": "param",
|
||||
"exclude_from_wrapper": ["method", "version", "signMethod"],
|
||||
"prefetch": { ... },
|
||||
"recursive": { ... },
|
||||
"row_inject": ["statisticsMonth"],
|
||||
"max_recursive_depth": 20
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.2 分页配置
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `page_param` | 页码/游标的参数名 | `cursor`、`page`、`offset` |
|
||||
| `page_size_param` | 每页条数的参数名 | `pageSize`、`page_size` |
|
||||
| `page_size` | 每页请求条数,覆盖全局配置 | `50` |
|
||||
| `cursor_pagination` | `true`=游标分页;不配置=普通页码分页 | `true` |
|
||||
| `pagination_mode` | `offset`=偏移量分页(`offset=(page-1)*size`);不配置=页码分页 | `offset` |
|
||||
| `initial_cursor` | 游标分页时的初始游标值,不配则为空字符串 | `""` |
|
||||
|
||||
**三种分页模式对比**:
|
||||
|
||||
| 模式 | 配置方式 | 适用平台 | 翻页方式 |
|
||||
|------|----------|----------|----------|
|
||||
| 游标分页 | `cursor_pagination: true` | 快手、钉钉 | 响应返回 cursor,下次请求带上 |
|
||||
| 普通分页 | 默认 | 腾讯广告 | 响应返回 totalPages,按页码翻 |
|
||||
| hasMore 分页 | `has_more_field` 在 response_config | 钉钉 | 响应有 hasMore 字段判断是否有下一页 |
|
||||
|
||||
#### 3.2.3 时间过滤配置
|
||||
|
||||
用于增量同步——只拉取上次同步之后新增/更新的数据。
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `time_field` | 时间字段名,如 `updateTime`、`createTime` |
|
||||
| `time_field_mode` | 时间模式:`range`=快手模式(beginTime/endTime),`filtering`=腾讯模式(filtering 数组) |
|
||||
| `full_sync_start_time` | 首次全量同步的起始时间戳(**Unix 毫秒**),不配则用全局 `default_lookback_days` |
|
||||
| `queryType` | 快手专用:`1`=按创建时间查(90天内),`2`=按更新时间查(240天内) |
|
||||
|
||||
> **全局配置** `config.yml` 中 `sync.default_lookback_days` 控制默认回溯天数(默认 89 天)。
|
||||
|
||||
#### 3.2.4 业务参数
|
||||
|
||||
除了上述系统级字段外,`request_config` 中其他字段都会作为**业务参数**发送给 API。
|
||||
|
||||
**快手示例**(`orderViewStatus`、`cpsType`、`sort` 等是业务参数):
|
||||
```json
|
||||
{
|
||||
"orderViewStatus": 1,
|
||||
"cpsType": 1,
|
||||
"sort": 1,
|
||||
"pageSize": 50
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.2.5 body_wrapper_field(快手专用)
|
||||
|
||||
快手 API 要求所有业务参数打包成一个 JSON 字符串,放在 `param` 字段中发送。
|
||||
|
||||
```json
|
||||
{
|
||||
"body_wrapper_field": "param",
|
||||
"exclude_from_wrapper": ["method", "version", "signMethod"]
|
||||
}
|
||||
```
|
||||
|
||||
**效果**:
|
||||
- 业务参数 `orderViewStatus=1, pageSize=50, sort=1` → 打包为 `param={"orderViewStatus":1,"pageSize":50,"sort":1}`
|
||||
- `method`、`version`、`signMethod` 保持在顶层,不被打包
|
||||
|
||||
#### 3.2.6 预取配置 (prefetch)
|
||||
|
||||
用于需要**先获取实体列表,再逐个查详情**的场景。
|
||||
|
||||
```json
|
||||
{
|
||||
"prefetch": {
|
||||
"url": "/open/order/cursor/list",
|
||||
"method": "GET",
|
||||
"response_path": "data.orderList",
|
||||
"target_param": "oid",
|
||||
"value_field": "oid"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `url` | 预取数据的 API 路径 |
|
||||
| `method` | HTTP 方法 |
|
||||
| `response_path` | 从响应中提取实体列表的 JSON 路径,如 `data.orderList` |
|
||||
| `target_param` | 将提取的值传给目标接口时使用的参数名 |
|
||||
| `value_field` | 从实体中取哪个字段的值,不配则传整个实体对象 |
|
||||
|
||||
**工作流程示例**(订单详情):
|
||||
1. 先调用 `/open/order/cursor/list` 获取所有订单 ID 列表
|
||||
2. 对每个订单 ID,调用 `/open/order/detail?oid=xxx` 获取详情
|
||||
3. 所有详情数据合并存入 `kuaishou_order_detail` 表
|
||||
|
||||
#### 3.2.7 递归遍历配置 (recursive)
|
||||
|
||||
用于树形结构数据(如钉钉部门树)。
|
||||
|
||||
```json
|
||||
{
|
||||
"recursive": {
|
||||
"key_field": "dept_id",
|
||||
"target_param": "parent_id"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `key_field` | 当前节点的 ID 字段名 |
|
||||
| `target_param` | 传给下级查询的参数名 |
|
||||
| `max_recursive_depth` | 最大递归深度,默认 20 |
|
||||
|
||||
#### 3.2.8 参数位置控制
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `parameters_location` | `query`=参数放 URL 查询字符串;不配=GET 放 URL,POST 放 Body |
|
||||
|
||||
#### 3.2.9 字段注入 (row_inject)
|
||||
|
||||
如果响应中缺少请求参数中的某些字段,可以用 `row_inject` 注入到每行数据中。
|
||||
|
||||
```json
|
||||
{
|
||||
"row_inject": ["statisticsMonth"]
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 响应配置 (response_config)
|
||||
|
||||
`response_config` 是一个 **JSON 对象**,定义如何解析 API 响应,判断成功/失败,提取数据。
|
||||
|
||||
```json
|
||||
{
|
||||
"success_field": "result",
|
||||
"success_value": 1,
|
||||
"message_field": "error_msg",
|
||||
"list_path": "data.orderList",
|
||||
"cursor_field": "data.cursor",
|
||||
"cursor_end_marker": "nomore",
|
||||
"single_record": false,
|
||||
"has_more_field": "data.hasMore"
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 | 默认值 |
|
||||
|------|------|--------|
|
||||
| `success_field` | 判断成功的字段名 | `code` |
|
||||
| `success_value` | 成功的值(数字) | `0` |
|
||||
| `message_field` | 错误消息的字段名 | `message` |
|
||||
| `list_path` | 数据列表的 JSON 路径,如 `data.orderList` | `data` |
|
||||
| `cursor_field` | 游标值的 JSON 路径,如 `data.cursor` | 无 |
|
||||
| `cursor_end_marker` | 游标结束标记,收到此值表示到底 | `nomore` |
|
||||
| `single_record` | `true`=响应是单条记录(详情接口),会自动包装成数组 | `false` |
|
||||
| `has_more_field` | hasMore 分页模式的判断字段路径 | 无 |
|
||||
|
||||
#### 成功判断逻辑
|
||||
|
||||
```
|
||||
读取响应的 success_field 字段 → 转成数字 → 和 success_value 比较
|
||||
相等 = 成功
|
||||
不相等 = 失败,取 message_field 作为错误消息
|
||||
```
|
||||
|
||||
**快手示例**:`result` 字段值 = `1` 表示成功
|
||||
**腾讯示例**:`code` 字段值 = `0` 表示成功
|
||||
|
||||
#### 数据提取逻辑
|
||||
|
||||
```
|
||||
1. 按 list_path 逐层进入 JSON
|
||||
2. 最后一段如果是数组 → 直接作为列表
|
||||
3. 最后一段是对象 → 找对象里的 list/orderList 字段
|
||||
4. 如果 single_record=true → 将对象包装为单元素数组
|
||||
5. 每行数据自动展平(子对象字段合并到顶层)
|
||||
6. 每行附加 raw_data 字段(原始 JSON)
|
||||
```
|
||||
|
||||
### 3.4 表结构定义 (table_definition)
|
||||
|
||||
`table_definition` 是 **JSON 对象**,定义目标表的表名、列结构和冲突处理。
|
||||
|
||||
```json
|
||||
{
|
||||
"table_name": "kuaishou_order_list",
|
||||
"columns": [
|
||||
{ "name": "oid", "type": "BIGINT", "comment": "订单ID" },
|
||||
{ "name": "status", "type": "INT", "comment": "订单状态码" },
|
||||
{ "name": "createTime", "type": "BIGINT", "comment": "创建时间" },
|
||||
{ "name": "updateTime", "type": "BIGINT", "comment": "更新时间" },
|
||||
{ "name": "totalFee", "type": "BIGINT", "comment": "总金额(分)" },
|
||||
{ "name": "buyerNick", "type": "VARCHAR(100)", "comment": "买家昵称" },
|
||||
{ "name": "itemTitle", "type": "VARCHAR(300)", "comment": "商品标题" }
|
||||
],
|
||||
"conflict_keys": ["oid"]
|
||||
}
|
||||
```
|
||||
|
||||
| 字段 | 说明 |
|
||||
|------|------|
|
||||
| `table_name` | 数据库表名,系统会自动创建 |
|
||||
| `columns` | 列定义数组 |
|
||||
| `columns[].name` | 列名,**必须与 API 响应中的字段名一致** |
|
||||
| `columns[].type` | PostgreSQL 数据类型:`BIGINT`、`INT`、`VARCHAR(n)`、`TEXT`、`BOOLEAN`、`JSONB` |
|
||||
| `columns[].comment` | 列的注释说明 |
|
||||
| `conflict_keys` | 唯一键,插入时冲突则更新(UPSERT),如 `["oid"]` |
|
||||
|
||||
> **自动建表**:系统首次同步时会自动创建表。
|
||||
> **自动过滤**:只写入 `columns` 中定义的字段,多余的忽略。
|
||||
> **raw_data**:每行会自动附带一个 `raw_data` 字段,保存完整的原始 JSON 响应。
|
||||
|
||||
---
|
||||
|
||||
## 4. 实战:新增一个平台
|
||||
|
||||
以"快手电商"为例,完整步骤:
|
||||
|
||||
### 第 1 步:创建平台配置
|
||||
|
||||
在 `api_datasource_platform` 表插入一条记录:
|
||||
|
||||
```sql
|
||||
INSERT INTO api_datasource_platform (
|
||||
tenant_id, platform_code, platform_name, description, status,
|
||||
api_base_url, auth_type, token, api_key, auth_config,
|
||||
rate_limit_per_minute, request_timeout_ms, max_retries, retry_delay_ms
|
||||
) VALUES (
|
||||
1, -- 租户 ID
|
||||
'kuaishou', -- 平台编码(唯一)
|
||||
'快手电商', -- 名��
|
||||
'快手电商开放平台数据同步',
|
||||
'ACTIVE', -- 状态
|
||||
'https://openapi.kwaixiaodian.com', -- API 域名
|
||||
'API_KEY', -- 认证类型
|
||||
'你的refresh_token', -- Token(OAuth 返回)
|
||||
'你的access_token', -- API Key(OAuth 返回)
|
||||
'{...}', -- auth_config(见 2.3 节)
|
||||
100, 30000, 3, 1000 -- 限流/超时/重试
|
||||
);
|
||||
```
|
||||
|
||||
### 第 2 步:创建接口配置
|
||||
|
||||
对每个需要同步的 API,在 `api_interface` 表插入一条记录:
|
||||
|
||||
```sql
|
||||
INSERT INTO api_interface (
|
||||
tenant_id, platform_id, name, code, url, method, status, auth_type,
|
||||
request_config, response_config, table_definition
|
||||
) VALUES (
|
||||
1,
|
||||
(SELECT id FROM api_datasource_platform WHERE platform_code='kuaishou'),
|
||||
'订单列表', -- 名称
|
||||
'order_list', -- 编码(唯一)
|
||||
'/open/order/cursor/list', -- API 路径
|
||||
'GET', -- HTTP 方法
|
||||
'active', -- 状态
|
||||
'inherit', -- 认证(继承平台)
|
||||
'{...}', -- request_config(见 3.2 节)
|
||||
'{...}', -- response_config(见 3.3 节)
|
||||
'{...}' -- table_definition(见 3.4 节)
|
||||
);
|
||||
```
|
||||
|
||||
### 第 3 步:验证
|
||||
|
||||
系统启动后会自动:
|
||||
1. 根据 `table_definition` 创建数据库表
|
||||
2. 按调度周期拉取数据
|
||||
3. 写入表中
|
||||
|
||||
---
|
||||
|
||||
## 5. 常见问题
|
||||
|
||||
### Q1:怎么判断数据有没有同步成功?
|
||||
|
||||
查看 `sync_tracker` 表,每个接口有一条记录:
|
||||
- `sync_status` = `success` 表示成功
|
||||
- `last_sync_time` = 上次同步的时间戳
|
||||
- 查看 `sync_task_log` 表可以看到每次同步的详细日志
|
||||
|
||||
### Q2:怎么修改同步频率?
|
||||
|
||||
编辑 `config.yml`:
|
||||
```yaml
|
||||
sync:
|
||||
sync_interval_minutes: 60 # 改这个,单位分钟
|
||||
```
|
||||
|
||||
### Q3:接口报"签名校验失败"怎么排查?
|
||||
|
||||
1. 确认 `app_key`、`app_secret`、`sign_secret` 和快手后台一致
|
||||
2. 确认 `access_token` 没有过期
|
||||
3. 确认 `sign_algorithm` 配置正确(快手用 `md5`)
|
||||
4. 查看日志中的 "签名原文" 和 "签名值",对比快手官方签名工具的结果
|
||||
|
||||
### Q4:想抽取历史数据怎么办?
|
||||
|
||||
在接口的 `request_config` 中设置 `full_sync_start_time`(Unix 毫秒时间戳),或修改 `config.yml` 的 `sync.default_lookback_days`。
|
||||
|
||||
### Q5:数据重复怎么办?
|
||||
|
||||
配置 `table_definition` 中的 `conflict_keys`,系统会用 **UPSERT** 模式(冲突时自动更新,不产生重复数据)。
|
||||
|
||||
### Q6:如何停用某个接口?
|
||||
|
||||
```sql
|
||||
UPDATE api_interface SET status = 'inactive' WHERE code = '接口编码';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> 📅 最后更新:2026-06-16
|
||||
> 📝 如有疑问,请联系开发团队
|
||||
|
||||
---
|
||||
|
||||
## 6. 同步机制说明
|
||||
|
||||
### 6.1 同步生命周期
|
||||
|
||||
```
|
||||
第 1 次同步(全量)
|
||||
lastSyncTime = 0 → 从 default_lookback_days 天前开始拉取
|
||||
时间分片:每 7 天一个分片(如 90 天 ≈ 13 个分片循环)
|
||||
完成后:记录 lastSyncTime = 数据中最大的更新时间
|
||||
↓
|
||||
第 N 次同步(增量)
|
||||
读取 lastSyncTime → beginTime = lastSyncTime
|
||||
只拉取上次同步之后新增/更新的数据
|
||||
完成后:更新 lastSyncTime
|
||||
↓
|
||||
(每隔 sync_interval_minutes 分钟循环一次)
|
||||
```
|
||||
|
||||
### 6.2 配置项
|
||||
|
||||
编辑 `config.yml`:
|
||||
|
||||
```yaml
|
||||
sync:
|
||||
page_size: 100 # 每次分页请求条数
|
||||
concurrency: 5 # 同步并发数
|
||||
retry_count: 3 # 最大重试次数
|
||||
sync_interval_minutes: 60 # 自动同步间隔(分钟),增量频率
|
||||
compensation_interval_seconds: 300 # 失败重试扫描间隔(秒)
|
||||
auto_sync_enabled: true # 是否启用自动同步
|
||||
sync_timeout_minutes: 120 # 单次同步超时(分钟)
|
||||
default_lookback_days: 89 # 首次全量回溯天数(可按接口覆盖)
|
||||
default_tenant_id: 1 # 租户 ID
|
||||
```
|
||||
|
||||
| 配置项 | 说明 | 建议值 |
|
||||
|--------|------|--------|
|
||||
| `sync_interval_minutes` | 增量同步频率 | 60(1小时) |
|
||||
| `default_lookback_days` | 首次全量拉多少天 | 快手限制 90 天内,建议 89 |
|
||||
| `compensation_interval_seconds` | 失败后多久重试 | 300(5分钟) |
|
||||
| `auto_sync_enabled` | 是否自动跑 | 生产环境 `true` |
|
||||
|
||||
### 6.3 覆盖单个接口的回溯天数
|
||||
|
||||
在接口 `request_config` 中加入 `full_sync_start_time`(Unix **毫秒**)
|
||||
|
||||
```json
|
||||
{ "full_sync_start_time": 1769200000000 }
|
||||
```
|
||||
|
||||
### 6.4 查看同步状态
|
||||
|
||||
```sql
|
||||
-- 所有接口的同步状态
|
||||
SELECT platform_code, interface_code, sync_status,
|
||||
to_timestamp(last_sync_time) AS last_sync_at
|
||||
FROM sync_tracker ORDER BY platform_code, interface_code;
|
||||
|
||||
-- 最近失败记录
|
||||
SELECT * FROM sync_task_log WHERE status = 'failed'
|
||||
ORDER BY start_time DESC LIMIT 20;
|
||||
```
|
||||
|
||||
- `sync_status = 'success'`:上次成功
|
||||
- `sync_status = 'running'`:正在跑
|
||||
- `last_sync_time = 0`:还没完成过同步
|
||||
|
||||
### 6.5 手动重新全量
|
||||
|
||||
把 `last_sync_time` 置 0,下次自动变全量:
|
||||
|
||||
```sql
|
||||
UPDATE sync_tracker SET last_sync_time = 0
|
||||
WHERE platform_code = 'kuaishou' AND interface_code = 'order_list';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> 📅 最后更新:2026-06-16
|
||||
+657
@@ -0,0 +1,657 @@
|
||||
# Data Engine 通用数据同步引擎 - 使用文档
|
||||
|
||||
## 一、概述
|
||||
|
||||
本系统是一个**配置驱动的通用数据同步引擎**,核心思想是:**平台管理 + 接口管理**。
|
||||
|
||||
您只需要通过 API 维护好平台和接口的配置信息(认证方式、请求参数、响应解析、目标表结构),系统就会自动:
|
||||
|
||||
1. **创建目标表**(根据 `table_definition` 自动建表)
|
||||
2. **拉取数据**(分页请求、多步骤请求、并发处理)
|
||||
3. **写入数据库**(批量 upsert)
|
||||
4. **增量同步**(通过 `filtering` 按最后修改时间过滤)
|
||||
5. **自动调度** → **推荐由 PPGo_Job 外部调度**(支持内部定时和外部 HTTP 触发两种模式)
|
||||
6. **补偿重试**(失败的同步任务自动重试,退避递增)
|
||||
7. **Token 自动刷新**(OAuth2 token 过期自动续期,不占用重试次数)
|
||||
|
||||
**不需要为每个平台写一行业务代码。**
|
||||
|
||||
---
|
||||
|
||||
## 二、数据库初始化
|
||||
|
||||
### 2.1 核心表
|
||||
|
||||
首次使用需执行 `sql/init_core_tables.sql`,创建以下 4 张表:
|
||||
|
||||
| 表名 | 说明 |
|
||||
|------|------|
|
||||
| `api_datasource_platform` | 数据源平台配置(认证信息、限流等) |
|
||||
| `api_interface` | 接口配置(URL、请求参数、表结构等) |
|
||||
| `sync_task_log` | 同步任务日志(记录状态,用于补偿) |
|
||||
| `sync_tracker` | 同步跟踪(记录每个接口的最后同步时间) |
|
||||
|
||||
### 2.2 初始化数据
|
||||
|
||||
执行 `sql/seed_data.sql` 创建腾讯广告平台 + 4 个接口配置。
|
||||
|
||||
如需清空重来:
|
||||
|
||||
```sql
|
||||
ALTER SEQUENCE api_datasource_platform_id_seq RESTART WITH 1;
|
||||
ALTER SEQUENCE api_interface_id_seq RESTART WITH 1;
|
||||
DELETE FROM api_interface;
|
||||
DELETE FROM api_datasource_platform;
|
||||
\i sql/seed_data.sql
|
||||
```
|
||||
|
||||
其他平台的 seed 数据:
|
||||
- `sql/seed_data_dingtalk.sql` — 钉钉平台(部门、用户、考勤)
|
||||
- `sql/seed_data_dingtalk_hrm.sql` — 钉钉 HRM
|
||||
- `sql/seed_data_dingtalk_salary.sql` — 钉钉智能薪酬
|
||||
- `sql/seed_data_kuaishou.sql` — 快手平台
|
||||
- `sql/seed_data_report_kuaishou.sql` — 快手报表引擎演示数据
|
||||
|
||||
---
|
||||
|
||||
## 三、配置说明
|
||||
|
||||
### 3.1 平台管理 (`api_datasource_platform`)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `platform_code` | 平台编码(唯一) | `tencent` |
|
||||
| `platform_name` | 平台名称 | `腾讯广告` |
|
||||
| `api_base_url` | API 基础地址 | `https://api.e.qq.com/v3.0` |
|
||||
| `auth_type` | 认证类型 | `OAUTH2` / `TOKEN` / `API_KEY` / `SIGN` / `APP_SIGNATURE` |
|
||||
| `token` | access_token 明文 | `xxxxx` |
|
||||
| `client_id` / `client_secret` | OAuth2 凭证 | `xxxxx` |
|
||||
| `auth_config` | 自定义认证配置 (JSONB) | 详见 3.1.1 |
|
||||
| `rate_limit_per_minute` | 每分钟请求限制 | `60` |
|
||||
| `request_timeout_ms` | 请求超时(毫秒) | `30000` |
|
||||
|
||||
> **注意:** 认证凭据现在统一存储在数据库中(`api_datasource_platform` 表),不再放在 `config.yml` 中。`config.yml` 中 `tencent.oauth.*` 仅作参考。
|
||||
|
||||
#### 3.1.1 `auth_config` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"token_in_query": true, // token 放在 URL 查询参数(如腾讯广告)
|
||||
"query_key": "access_token", // 查询参数名,默认 "access_token"
|
||||
"header_name": "Authorization", // token 放请求头时的头名
|
||||
"header_format": "Bearer {token}",
|
||||
"refresh_token": "xxxxx", // OAuth2 refresh_token
|
||||
"extra_query_params": { // 额外查询参数
|
||||
"timestamp": "{timestamp}", // {timestamp} → 当前 Unix 时间戳
|
||||
"nonce": "{nonce}" // {nonce} → 随机字符串
|
||||
},
|
||||
"app_key": "xxx", // SIGN 认证的应用 Key
|
||||
"app_secret": "xxx", // SIGN 认证的应用 Secret
|
||||
"sign_algorithm": "md5", // 签名算法: md5 / md5_upper
|
||||
"sign_secret": "xxx" // 签名专用密钥(可选,默认用 app_secret)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3.1.2 认证类型说明
|
||||
|
||||
| 类型 | 说明 | token 传递方式 |
|
||||
|------|------|------|
|
||||
| `OAUTH2` | OAuth2 授权 | URL query 或 Header,支持自动刷新 |
|
||||
| `TOKEN` | 静态 Token | `Authorization: Bearer <token>` |
|
||||
| `API_KEY` | API Key | `X-API-Key <api_key>` |
|
||||
| `SIGN` | 参数签名 | URL query + `sign` 参数 |
|
||||
| `APP_SIGNATURE` | App-ID + 签名 | Header: `app-id` + `signature` |
|
||||
|
||||
### 3.2 接口管理 (`api_interface`)
|
||||
|
||||
| 字段 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `platform_id` | 所属平台 ID | `1` |
|
||||
| `name` / `code` | 接口名称 / 唯一编码 | `图片素材` / `image` |
|
||||
| `url` | 接口地址(相对路径) | `/images/get` |
|
||||
| `method` | 请求方法 | `GET` / `POST` |
|
||||
| `request_config` | 请求配置 (JSONB) | 详见 3.2.1 |
|
||||
| `response_config` | 响应配置 (JSONB) | 详见 3.2.2 |
|
||||
| `table_definition` | 表结构定义 (JSONB) | 详见 3.3 |
|
||||
|
||||
#### 3.2.1 `request_config` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"parameters_location": "query", // 参数位置: "query"(URL) / "body"(默认POST)
|
||||
"page": 1,
|
||||
"page_size": 100,
|
||||
"page_param": "page", // 分页参数名(可自定义)
|
||||
"page_size_param": "page_size",
|
||||
"pagination_mode": "offset", // 分页模式: 默认页码 / "offset"(偏移量)
|
||||
"time_field": "last_modified_time", // 增量时间字段名
|
||||
"time_field_mode": "filtering", // 时间模式: "filtering"(腾讯) / "range"(快手)
|
||||
"cursor_pagination": true, // 游标分页
|
||||
"initial_cursor": "", // 初始游标值
|
||||
"fields": ["field1", "field2"], // 请求字段列表
|
||||
"body_wrapper_field": "param", // Body 包装字段(快手 API 的 param JSON)
|
||||
"exclude_from_wrapper": ["method"], // 不包装的顶层字段
|
||||
"row_inject": ["statisticsMonth"], // 将请求参数注入到响应行中
|
||||
"prefetch": { ... }, // 预取配置(见下文)
|
||||
"recursive": { ... }, // 递归配置(见下文)
|
||||
"max_recursive_depth": 20, // 递归最大深度
|
||||
"full_sync_start_time": 1700000000000 // 全量同步起始时间(毫秒时间戳)
|
||||
}
|
||||
```
|
||||
|
||||
**`parameters_location` 说明**:
|
||||
- 未设置或 `"body"` → 参数放在请求体中(POST 请求)
|
||||
- `"query"` → 参数放在 URL 查询字符串中(GET 请求)
|
||||
- 当 `method=GET` 时,即使不设置也默认走 query
|
||||
|
||||
**支持 4 种分页模式:**
|
||||
|
||||
| 模式 | 配置 | 说明 |
|
||||
|------|------|------|
|
||||
| 普通分页 | 默认 | `page` 递增,支持 `total_page` 判断结束 |
|
||||
| 偏移量分页 | `pagination_mode: "offset"` | `offset = (page-1) * pageSize` |
|
||||
| 游标分页 | `cursor_pagination: true` | 从响应 `cursor_field` 取游标继续下一页 |
|
||||
| hasMore 分页 | `response_config.has_more_field` | 从响应 `has_more` 字段判断是否还有下一页 |
|
||||
|
||||
**预取(prefetch)**:某些接口需要"先拿列表 → 遍历每个元素查数据"(如先拉账户列表,再遍历拉图片)。并发处理,并发数由 `sync.concurrency` 控制。
|
||||
|
||||
```json
|
||||
"prefetch": {
|
||||
"url": "/advertiser/get", // 预取接口地址
|
||||
"method": "GET",
|
||||
"response_path": "data.list", // 从响应中取值路径
|
||||
"target_param": "account_id", // 注入主请求的参数名
|
||||
"value_field": "account_id" // 从预取结果取哪个字段值
|
||||
}
|
||||
```
|
||||
|
||||
**递归(recursive)**:用于树形结构接口(如钉钉部门树),先查根级 → 对每个子节点递归查下级。
|
||||
|
||||
```json
|
||||
"recursive": {
|
||||
"key_field": "dept_id", // 递归键字段
|
||||
"target_param": "dept_id" // 注入到请求的参数名
|
||||
}
|
||||
```
|
||||
|
||||
**时间分片(range mode)**:快手等平台使用 `beginTime/endTime` 做时间范围查询,全量同步时自动按 3 天分片循环拉取。
|
||||
|
||||
#### 3.2.2 `response_config` 字段详解
|
||||
|
||||
系统默认解析的响应格式:
|
||||
|
||||
```json
|
||||
{ "code": 0, "message": "success", "data": { "list": [...], "page_info": { "total_page": N } } }
|
||||
```
|
||||
|
||||
可通过 `response_config` 自定义:
|
||||
|
||||
```json
|
||||
{
|
||||
"list_path": "data.list", // 数据列表路径
|
||||
"success_field": "code", // 成功标识字段,默认 "code"
|
||||
"success_value": 0, // 成功值,默认 0
|
||||
"message_field": "message", // 错误消息字段
|
||||
"cursor_field": "data.cursor", // 游标字段路径(游标分页用)
|
||||
"has_more_field": "data.has_more", // hasMore 字段路径
|
||||
"single_record": false // 是否为单条记录(自动包装为数组)
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 `table_definition` 字段详解
|
||||
|
||||
```json
|
||||
{
|
||||
"table_name": "tencent_image",
|
||||
"columns": [
|
||||
{ "name": "image_id", "type": "VARCHAR(100)", "comment": "图片ID" },
|
||||
{ "name": "account_id", "type": "BIGINT", "comment": "账户ID" }
|
||||
],
|
||||
"conflict_keys": ["image_id", "account_id"]
|
||||
}
|
||||
```
|
||||
|
||||
**自动添加的列**(无需声明):
|
||||
|
||||
| 列名 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `id` | BIGSERIAL PRIMARY KEY | 自增主键 |
|
||||
| `tenant_id` | BIGINT | 租户 ID |
|
||||
| `creator` | VARCHAR(64) | 创建人 |
|
||||
| `created_at` | TIMESTAMPTZ | 创建时间 |
|
||||
| `updater` | VARCHAR(64) | 更新人 |
|
||||
| `updated_at` | TIMESTAMPTZ | 更新时间 |
|
||||
| `deleted_at` | TIMESTAMPTZ | 软删除 |
|
||||
| `raw_data` | JSONB | 原始响应数据(含展平的嵌套字段) |
|
||||
|
||||
---
|
||||
|
||||
## 四、API 接口
|
||||
|
||||
服务端口:`3013`(由 `config.yml` 中 `server.address` 配置)
|
||||
|
||||
路由规则:系统使用 `RouteRegister` 自动注册,URL 路径由 **struct type name** 转换为 kebab-case 生成。
|
||||
|
||||
### 4.1 平台管理 CRUD
|
||||
|
||||
基础路径:`/datasource/platform/controller`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/datasource/platform/controller/createDatasourcePlatform` | 创建平台 |
|
||||
| GET | `/datasource/platform/controller/listDatasourcePlatforms` | 平台列表 |
|
||||
| GET | `/datasource/platform/controller/getDatasourcePlatform` | 平台详情 |
|
||||
| GET | `/datasource/platform/controller/getPlatformByCode` | 按平台编码查询 |
|
||||
| PUT | `/datasource/platform/controller/updateDatasourcePlatform` | 更新平台 |
|
||||
| PUT | `/datasource/platform/controller/updateDatasourcePlatformStatus` | 更新平台状态 |
|
||||
| DELETE | `/datasource/platform/controller/deleteDatasourcePlatform` | 删除平台 |
|
||||
| GET | `/datasource/platform/controller/getPlatformStatistics` | 平台统计 |
|
||||
| POST | `/datasource/platform/controller/testPlatformConnection` | 测试平台连接 |
|
||||
|
||||
### 4.2 接口管理 CRUD
|
||||
|
||||
基础路径:`/api/interface/controller`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| POST | `/api/interface/controller/createApiInterface` | 创建接口 |
|
||||
| GET | `/api/interface/controller/listApiInterfaces` | 接口列表 |
|
||||
| GET | `/api/interface/controller/getApiInterface` | 接口详情 |
|
||||
| PUT | `/api/interface/controller/updateApiInterface` | 更新接口 |
|
||||
| PUT | `/api/interface/controller/updateApiInterfaceStatus` | 更新接口状态 |
|
||||
| DELETE | `/api/interface/controller/deleteApiInterface` | 删除接口 |
|
||||
|
||||
### 4.3 同步控制
|
||||
|
||||
基础路径:`/sync/ctrl`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| **POST** | **`/sync/ctrl/triggerAll`** | **全平台同步(PPGo_Job 主任务)** |
|
||||
| **POST** | **`/sync/ctrl/compensate`** | **补偿扫描(PPGo_Job 辅助任务)** |
|
||||
| POST | `/sync/ctrl/trigger` | 触发单个接口同步 |
|
||||
| GET | `/sync/ctrl/config` | 查询平台配置 |
|
||||
|
||||
**触发单个接口同步示例:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3013/sync/ctrl/trigger \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"platformCode":"tencent","interfaceCode":"image","fullSync":true}'
|
||||
```
|
||||
|
||||
参数说明:
|
||||
- `platformCode`:平台编码(必填)
|
||||
- `interfaceCode`:接口编码(必填)
|
||||
- `fullSync`:`true`=全量拉取,`false`=增量(默认自动判断)
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"tableName": "tencent_image",
|
||||
"totalRows": 1500,
|
||||
"insertedRows": 1450,
|
||||
"duration": "12.3s"
|
||||
}
|
||||
```
|
||||
|
||||
**全平台同步示例:**
|
||||
|
||||
```bash
|
||||
curl -X POST http://localhost:3013/sync/ctrl/triggerAll -v
|
||||
```
|
||||
|
||||
响应:
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"message": "全平台同步完成",
|
||||
"results": [
|
||||
{ "platformCode": "tencent", "interfaceCode": "account_relation", "success": true },
|
||||
{ "platformCode": "tencent", "interfaceCode": "image", "success": true }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### 4.4 报表引擎 API
|
||||
|
||||
基础路径:`/report`
|
||||
|
||||
| 方法 | 路径 | 说明 |
|
||||
|------|------|------|
|
||||
| **POST** | **`/report/extractAll`** | **全量抽取(PPGo_Job 报表任务)** |
|
||||
| POST | `/report/extract` | 按天数据抽取 |
|
||||
| POST | `/report/backfill` | 批量回填数据 |
|
||||
| POST | `/report/query` | 用户选择查询 |
|
||||
| POST | `/report/autoCreateTable` | 自动创建统计宽表 |
|
||||
| POST | `/report/initTables` | 初始化系统表 |
|
||||
| GET | `/report/businesses` | 业务列表 |
|
||||
| GET/POST/DELETE | `/report/business[/save]` | 业务 CRUD |
|
||||
| GET | `/report/reports` | 报表列表 |
|
||||
| GET/POST/DELETE | `/report/report[/save][/saveWithFields]` | 报表 CRUD |
|
||||
| GET | `/report/fields` | 报表字段列表 |
|
||||
| GET/POST/DELETE | `/report/field[/save]` | 字段 CRUD |
|
||||
| GET | `/report/extractConfigs` | 抽取配置列表 |
|
||||
| GET/POST/DELETE | `/report/extractConfig[/save]` | 抽取配置 CRUD |
|
||||
|
||||
### 4.5 管理页面
|
||||
|
||||
| 路径 | 说明 |
|
||||
|------|------|
|
||||
| `GET /admin` | 数据引擎管理后台(平台/接口管理) |
|
||||
| `GET /admin/report` | 报表引擎管理页面 |
|
||||
|
||||
---
|
||||
|
||||
## 五、配置文件 (`config.yml`)
|
||||
|
||||
```yaml
|
||||
server:
|
||||
address: ":3013" # 服务端口
|
||||
|
||||
# 报表引擎配置
|
||||
report:
|
||||
extract_enabled: false # 是否启用内部定时抽取(推荐 false,由 PPGo_Job 触发)
|
||||
|
||||
# 数据同步配置
|
||||
sync:
|
||||
page_size: 100 # 每次分页请求条数
|
||||
concurrency: 5 # 并发处理数(prefetch 遍历实体时)
|
||||
retry_count: 3 # 最大重试次数
|
||||
sync_interval_minutes: 60 # 自动同步间隔(内部调度模式用)
|
||||
compensation_interval_seconds: 300 # 补偿扫描间隔(内部调度模式用)
|
||||
auto_sync_enabled: false # 是否启用内部自动同步(推荐 false,由 PPGo_Job 触发)
|
||||
sync_timeout_minutes: 120 # 单次同步超时(分钟)
|
||||
default_lookback_days: 89 # 全量同步默认回溯天数
|
||||
default_tenant_id: 1 # 自动同步使用的租户 ID
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、调度策略(核心变更)
|
||||
|
||||
系统支持 **两种调度模式**,通过配置开关切换:
|
||||
|
||||
### 6.1 推荐模式:PPGo_Job 外部调度
|
||||
|
||||
```
|
||||
config.yml:
|
||||
report.extract_enabled: false
|
||||
sync.auto_sync_enabled: false ← 默认值
|
||||
```
|
||||
|
||||
内部调度器不启动,由 PPGo_Job 定时调用 HTTP 端点触发。详情见第八章。
|
||||
|
||||
### 6.2 备选模式:内部自动调度
|
||||
|
||||
```
|
||||
config.yml:
|
||||
report.extract_enabled: true
|
||||
sync.auto_sync_enabled: true
|
||||
```
|
||||
|
||||
- 同步引擎:进程启动后自动循环执行全平台同步,每次完成后等待 `sync_interval_minutes` 再继续
|
||||
- 报表引擎:每天凌晨 2:00 执行全量抽取(gcron `0 0 2 * * *`)
|
||||
- 补偿调度器:独立于 `auto_sync_enabled`,始终随服务启动(内部模式)
|
||||
|
||||
### 6.3 异常中断恢复
|
||||
|
||||
- 同步开始前写 `sync_tracker.sync_status='running'`
|
||||
- 同步完成后写 `'success'`
|
||||
- 重启检测到 `'running'` → 日志告警 → 重新全量同步
|
||||
|
||||
---
|
||||
|
||||
## 七、Token 自动刷新机制
|
||||
|
||||
### 7.1 工作原理
|
||||
|
||||
系统支持 OAuth2 token 过期自动刷新,**不需要人工介入**:
|
||||
|
||||
1. `ApiClient` 发送请求 → 收到 401 或 token 过期错误
|
||||
2. 自动调用平台的 `RefreshToken` 回调函数(如 `RefreshTencentToken`)
|
||||
3. 刷新成功后**立即重试**原请求(不消耗退避重试次数)
|
||||
4. 每个 `doRequest` 最多刷新一次,避免死循环
|
||||
5. 刷新后的 token 同时更新内存和数据库
|
||||
|
||||
```
|
||||
doRequest
|
||||
├── execute() → HTTP 401 / token_expired
|
||||
├── isTokenExpiredError() → true
|
||||
├── RefreshTencentToken()
|
||||
│ ├── POST https://api.e.qq.com/oauth/refresh_token
|
||||
│ ├── 更新 platform.Token / platform.AccessToken
|
||||
│ └── 更新数据库 api_datasource_platform.token + auth_config
|
||||
└── continue(用新 token 重试,不消耗重试次数)
|
||||
```
|
||||
|
||||
### 7.2 token 过期错误检测
|
||||
|
||||
系统通过关键字匹配识别 token 过期错误:
|
||||
|
||||
```go
|
||||
tokenExpiredKeywords := []string{
|
||||
"TOKEN过期", "token过期", "token_expired", "Token过期",
|
||||
"result=28", // 快手
|
||||
"access_token", "token已失效", "token无效",
|
||||
}
|
||||
```
|
||||
|
||||
### 7.3 token 过期时的处理策略
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 自动同步中 | 跳过该平台剩余接口,继续处理下一个平台 |
|
||||
| 补偿扫描中 | 标记为 `manual_review`,等待人工处理,不再重试 |
|
||||
| 手动触发 | 自动刷新后重试 |
|
||||
|
||||
### 7.4 腾讯广告 OAuth2 刷新(当前已实现)
|
||||
|
||||
已支持腾讯广告 OAuth2 token 自动刷新,使用 `refresh_token` 换取新的 `access_token`:
|
||||
|
||||
- 刷新端点:`POST https://api.e.qq.com/oauth/refresh_token`
|
||||
- 超时:30 秒
|
||||
- 互斥锁:防止并发刷新(`refreshTokenMu sync.Mutex`)
|
||||
|
||||
---
|
||||
|
||||
## 八、PPGo_Job 定时任务配置
|
||||
|
||||
系统推荐由 **PPGo_Job** 外部调度,需在 PPGo_Job 中配置以下任务。
|
||||
|
||||
### 任务 1:全平台数据同步
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 任务名称 | `数据引擎 - 全平台同步` |
|
||||
| 请求 URL | `http://<data-engine-host>:3013/sync/ctrl/triggerAll` |
|
||||
| 请求方法 | `POST` |
|
||||
| Header | `Content-Type: application/json` |
|
||||
| Body | 空(不需要请求体) |
|
||||
| Cron | `0 0 * * * ?`(每小时整点) |
|
||||
| 超时 | `7200` 秒(2小时) |
|
||||
| 说明 | 遍历所有 ACTIVE 平台下有 table_definition 的接口,自动判断全量/增量。所有平台的同步都在同一任务中完成 |
|
||||
|
||||
执行流程:
|
||||
|
||||
```
|
||||
triggerAll
|
||||
└── runAutoSync()
|
||||
├── 查所有 ACTIVE 平台
|
||||
├── 对每个平台 → 查所有有 table_definition 的接口
|
||||
│ ├── 查 sync_tracker → 有记录=增量,无记录=全量
|
||||
│ ├── SyncByConfig(platformCode, interfaceCode)
|
||||
│ └── token 过期 → 跳过该平台剩余接口
|
||||
└── 返回每个接口的执行结果
|
||||
```
|
||||
|
||||
### 任务 2:失败任务补偿
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 任务名称 | `数据引擎 - 补偿扫描` |
|
||||
| 请求 URL | `http://<data-engine-host>:3013/sync/ctrl/compensate` |
|
||||
| 请求方法 | `POST` |
|
||||
| Header | `Content-Type: application/json` |
|
||||
| Body | 空 |
|
||||
| Cron | `0 0/5 * * * ?`(每5分钟) |
|
||||
| 超时 | `600` 秒(10分钟) |
|
||||
| 说明 | 扫描 sync_task_log 中 status=failed 的任务,按退避策略重试 |
|
||||
|
||||
补偿退避策略:
|
||||
```
|
||||
第1次重试 → 等待 5分钟
|
||||
第2次重试 → 等待 15分钟
|
||||
第3次重试 → 等待 30分钟
|
||||
第4次重试 → 等待 60分钟
|
||||
第5次重试 → 等待 120分钟(封顶)
|
||||
达最大次数 → 标记 manual_review,等待人工处理
|
||||
```
|
||||
|
||||
### 任务 3:报表每日抽取(可选)
|
||||
|
||||
| 字段 | 值 |
|
||||
|------|-----|
|
||||
| 任务名称 | `数据引擎 - 每日抽取` |
|
||||
| 请求 URL | `http://<data-engine-host>:3013/report/extractAll` |
|
||||
| 请求方法 | `POST` |
|
||||
| Header | `Content-Type: application/json` |
|
||||
| Body | 空 |
|
||||
| Cron | `0 0 2 * * ?`(每天凌晨2点) |
|
||||
| 超时 | `3600` 秒(1小时) |
|
||||
| 说明 | 遍历所有启用的业务+报表,抽取前一天数据到统计宽表 |
|
||||
|
||||
---
|
||||
|
||||
## 九、已配置平台
|
||||
|
||||
### 9.1 腾讯广告
|
||||
|
||||
| 项目 | 值 |
|
||||
|------|-----|
|
||||
| platform_code | `tencent` |
|
||||
| API Base | `https://api.e.qq.com/v3.0` |
|
||||
| 认证 | OAUTH2(access_token 在 URL query 中,支持自动刷新) |
|
||||
|
||||
**4 个接口:**
|
||||
|
||||
| 接口编码 | 名称 | 方法+路径 | 同步模式 | 增量 |
|
||||
|---------|------|----------|---------|------|
|
||||
| `account_relation` | 账户列表 | `GET /advertiser/get` | 普通分页 | 不支持(全量+去重) |
|
||||
| `image` | 图片素材 | `GET /images/get` | **预取**(遍历账户) | ✅ `last_modified_time` |
|
||||
| `video` | 视频素材 | `GET /videos/get` | **预取**(遍历账户) | ✅ `last_modified_time` |
|
||||
| `audio` | 音频素材 | `POST /muse_audios/get` | 普通分页 POST | 不支持(全量+去重) |
|
||||
|
||||
**图片/视频同步流程:**
|
||||
|
||||
```
|
||||
SyncByConfig("tencent", "image")
|
||||
├── ① 预取: 分页拉取 /advertiser/get → 所有 account_id
|
||||
│ 数据同时存入 tencent_account_relation 表
|
||||
├── ② 并发遍历账户(config.yml concurrency=5)
|
||||
│ 每个 account_id → GET /images/get?account_id=xxx&page=1&page_size=100
|
||||
│ 自动补充 filtering(增量时)
|
||||
│ 结果 upsert 到 tencent_image
|
||||
└── ③ 更新 sync_tracker 记录同步时间
|
||||
```
|
||||
|
||||
### 9.2 其他平台
|
||||
|
||||
| 平台 | seed 文件 | 接口数 |
|
||||
|------|----------|--------|
|
||||
| 钉钉 | `seed_data_dingtalk.sql` | ~10个 |
|
||||
| 钉钉 HRM | `seed_data_dingtalk_hrm.sql` | ~5个 |
|
||||
| 钉钉智能薪酬 | `seed_data_dingtalk_salary.sql` | ~3个 |
|
||||
| 快手 | `seed_data_kuaishou.sql` | ~5个 |
|
||||
|
||||
---
|
||||
|
||||
## 十、补偿机制
|
||||
|
||||
### 10.1 工作原理
|
||||
|
||||
1. 同步失败 → 自动写入 `sync_task_log`(status=failed)
|
||||
2. 补偿扫描(由 PPGo_Job 调用 `/sync/ctrl/compensate`)扫描 failed 记录
|
||||
3. 对未达最大重试次数的任务,调用 `SyncByConfig` 重试
|
||||
4. 重试间隔按退避策略递增:5min → 15min → 30min → 60min → 120min
|
||||
5. 达最大次数 → 标记 `manual_review`,等待人工介入
|
||||
6. Token 过期错误 → 直接标记 `manual_review`,不再重试
|
||||
|
||||
### 10.2 配置
|
||||
|
||||
```yaml
|
||||
sync:
|
||||
compensation_interval_seconds: 300 # 扫描间隔(内部调度用)
|
||||
retry_count: 3 # 最大重试次数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十一、快速开始
|
||||
|
||||
```bash
|
||||
# 1. 建核心表
|
||||
PGPASSWORD='xxx' psql -h <host> -p 15432 -U postgres -d engine -f sql/init_core_tables.sql
|
||||
|
||||
# 2. 初始化腾讯广告数据
|
||||
PGPASSWORD='xxx' psql -h <host> -p 15432 -U postgres -d engine -f sql/seed_data.sql
|
||||
|
||||
# 3. 配置 PPGo_Job(推荐)或启用内部调度
|
||||
# 见第六章调度策略 和 第八章 PPGo_Job 配置
|
||||
|
||||
# 4. 启动服务
|
||||
cd D:/WorkSpace/HDWL/data-engine
|
||||
go run main.go
|
||||
|
||||
# 5. 手动测试同步
|
||||
curl -X POST http://localhost:3013/sync/ctrl/trigger \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d '{"platformCode":"tencent","interfaceCode":"image","fullSync":true}'
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 十二、常见问题
|
||||
|
||||
**Q: 响应格式不符合 `{code: 0, data: {list: [...]}}` 怎么办?**
|
||||
|
||||
通过接口管理的 `response_config` 自定义 `list_path`、`success_field`、`success_value`。如需新的解析逻辑,修改 `dynamic_sync.go` 的 `parseRespExt` 函数。
|
||||
|
||||
**Q: 如何添加新平台?**
|
||||
|
||||
1. 调用平台管理 API 创建平台配置
|
||||
2. 调用接口管理 API 创建接口(含 `table_definition`)
|
||||
3. 平台状态设为 `ACTIVE`,接口状态设为 `active`
|
||||
4. 系统下次同步时自动建表并拉取数据
|
||||
|
||||
**Q: prefetch 的响应格式要求?**
|
||||
|
||||
必须是 JSON,`response_path` 指向一个数组。如 `response_path: "data.list"` 从 `{"data":{"list":[...]}}` 取值。
|
||||
|
||||
**Q: 如何排查同步失败?**
|
||||
|
||||
1. 查询 `sync_task_log` 表看失败记录和错误信息
|
||||
2. `GET /sync/ctrl/config?platformCode=tencent` 查看平台配置
|
||||
3. 检查服务日志中 `equivalent curl` 输出,手动复现请求
|
||||
4. PPGo_Job 补偿任务会自动重试,日志会打印重试过程
|
||||
|
||||
**Q: Token 过期了怎么办?**
|
||||
|
||||
如果配置了 OAuth2 且 `refresh_token` 有效,系统会自动刷新。手动强制刷新可用 `_update_token.go`:
|
||||
|
||||
```bash
|
||||
# 先修改 _update_token.go 中 token 和 refresh_token 的值
|
||||
cd D:/WorkSpace/HDWL/data-engine
|
||||
go run _update_token.go
|
||||
```
|
||||
|
||||
**Q: 内部调度和 PPGo_Job 调度有什么区别?**
|
||||
|
||||
| 对比项 | 内部调度 | PPGo_Job 调度(推荐) |
|
||||
|-------|---------|-------------------|
|
||||
| 依赖 | 无外部依赖 | 需要 PPGo_Job 服务 |
|
||||
| 配置 | `auto_sync_enabled: true` | `auto_sync_enabled: false` |
|
||||
| 控制 | 进程内循环,重启失效 | 统一调度中心,支持暂停/手动执行 |
|
||||
| 多服务协调 | 不支持 | 支持 |
|
||||
| 运维 | 需要登录服务器 | 通过 Web UI 管理所有任务 |
|
||||
@@ -0,0 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Add pagination to all list endpoints + change status filter to deleted_at."""
|
||||
|
||||
# ========== 1. Controller: request/response structs + handlers ==========
|
||||
ctrl_path = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/controller/report/report_controller.go'
|
||||
with open(ctrl_path) as f:
|
||||
ctrl = f.read()
|
||||
|
||||
# 1a. listBusinessesReq - add PageReq embed
|
||||
ctrl = ctrl.replace(
|
||||
'type listBusinessesReq struct {\n\t\tg.Meta `path:"/businesses" method:"get" tags:"报表引擎" summary:"业务列表"`\n\t}',
|
||||
'type listBusinessesReq struct {\n\t\tg.Meta `path:"/businesses" method:"get" tags:"报表引擎" summary:"业务列表"`\n\t\tmodel.PageReq\n\t}'
|
||||
)
|
||||
|
||||
# 1b. listBusinessesRes - add PageRes embed + rename List
|
||||
ctrl = ctrl.replace(
|
||||
'type listBusinessesRes struct {\n\t\tList []model.BusinessConfig `json:"list"`\n\t}',
|
||||
'type listBusinessesRes struct {\n\t\tList []model.BusinessConfig `json:"list"`\n\t\tmodel.PageRes\n\t}'
|
||||
)
|
||||
|
||||
# 1c. ListBusinesses handler - pass page params + calc TotalPages
|
||||
ctrl = ctrl.replace(
|
||||
'\t\tlist, err := svc().GetAllBusinesses(ctx)',
|
||||
'\t\tlist, total, err := svc().GetAllBusinesses(ctx, req.PageNum, req.PageSize)'
|
||||
)
|
||||
|
||||
ctrl = ctrl.replace(
|
||||
'\t\treturn &listBusinessesRes{List: list}, nil',
|
||||
'\t\ttotalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &listBusinessesRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
# 1d. listReportsReq - add PageReq embed
|
||||
ctrl = ctrl.replace(
|
||||
'type listReportsReq struct {\n\t\tg.Meta `path:"/reports" method:"get" tags:"报表引擎" summary:"报表列表"`\n\t\tBusinessCode string `json:"businessCode" v:"required"`\n\t}',
|
||||
'type listReportsReq struct {\n\t\tg.Meta `path:"/reports" method:"get" tags:"报表引擎" summary:"报表列表"`\n\t\tBusinessCode string `json:"businessCode" v:"required"`\n\t\tmodel.PageReq\n\t}'
|
||||
)
|
||||
|
||||
# 1e. listReportsRes - add PageRes embed
|
||||
ctrl = ctrl.replace(
|
||||
'type listReportsRes struct {\n\t\tList []model.ReportConfig `json:"list"`\n\t}',
|
||||
'type listReportsRes struct {\n\t\tList []model.ReportConfig `json:"list"`\n\t\tmodel.PageRes\n\t}'
|
||||
)
|
||||
|
||||
# 1f. ListReports handler
|
||||
ctrl = ctrl.replace(
|
||||
'\t\tlist, err := svc().GetAllReports(ctx, req.BusinessCode)',
|
||||
'\t\tlist, total, err := svc().GetAllReports(ctx, req.BusinessCode, req.PageNum, req.PageSize)'
|
||||
)
|
||||
|
||||
ctrl = ctrl.replace(
|
||||
'\t\treturn &listReportsRes{List: list}, nil',
|
||||
'\t\ttotalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &listReportsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
# 1g. getExtractConfigsReq - add PageReq embed
|
||||
ctrl = ctrl.replace(
|
||||
'type getExtractConfigsReq struct {\n\t\tg.Meta `path:"/extractConfigs" method:"get" tags:"报表引擎" summary:"抽取配置列表"`\n\t\tBusinessCode string `json:"businessCode" v:"required"`\n\t\tReportCode string `json:"reportCode" v:"required"`\n\t}',
|
||||
'type getExtractConfigsReq struct {\n\t\tg.Meta `path:"/extractConfigs" method:"get" tags:"报表引擎" summary:"抽取配置列表"`\n\t\tBusinessCode string `json:"businessCode" v:"required"`\n\t\tReportCode string `json:"reportCode" v:"required"`\n\t\tmodel.PageReq\n\t}'
|
||||
)
|
||||
|
||||
# 1h. getExtractConfigsRes - add PageRes embed
|
||||
ctrl = ctrl.replace(
|
||||
'type getExtractConfigsRes struct {\n\t\tList []model.ExtractConfig `json:"list"`\n\t}',
|
||||
'type getExtractConfigsRes struct {\n\t\tList []model.ExtractConfig `json:"list"`\n\t\tmodel.PageRes\n\t}'
|
||||
)
|
||||
|
||||
# 1i. GetExtractConfigs handler
|
||||
ctrl = ctrl.replace(
|
||||
'\t\tlist, err := svc().GetExtractConfigs(ctx, req.BusinessCode, req.ReportCode)',
|
||||
'\t\tlist, total, err := svc().GetExtractConfigs(ctx, req.BusinessCode, req.ReportCode, req.PageNum, req.PageSize)'
|
||||
)
|
||||
|
||||
ctrl = ctrl.replace(
|
||||
'\t\treturn &getExtractConfigsRes{List: list}, nil',
|
||||
'\t\ttotalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &getExtractConfigsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
with open(ctrl_path, 'w') as f:
|
||||
f.write(ctrl)
|
||||
print('✅ Controller done')
|
||||
|
||||
# ========== 2. API service layer ==========
|
||||
api_path = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/common/report/api.go'
|
||||
with open(api_path) as f:
|
||||
api = f.read()
|
||||
|
||||
# 2a. GetAllBusinesses
|
||||
api = api.replace(
|
||||
'func (s *ReportService) GetAllBusinesses(ctx context.Context) ([]model.BusinessConfig, error) {\n\t\treturn s.configLoader.GetAllBusinesses(ctx)',
|
||||
'func (s *ReportService) GetAllBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {\n\t\treturn s.configLoader.GetAllBusinesses(ctx, pageNum, pageSize)'
|
||||
)
|
||||
|
||||
# 2b. GetAllReports
|
||||
api = api.replace(
|
||||
'func (s *ReportService) GetAllReports(ctx context.Context, businessCode string) ([]model.ReportConfig, error) {\n\t\treturn s.configLoader.GetAllReports(ctx, businessCode)',
|
||||
'func (s *ReportService) GetAllReports(ctx context.Context, businessCode string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {\n\t\treturn s.configLoader.GetAllReports(ctx, businessCode, pageNum, pageSize)'
|
||||
)
|
||||
|
||||
# 2c. GetExtractConfigs
|
||||
api = api.replace(
|
||||
'func (s *ReportService) GetExtractConfigs(ctx context.Context, businessCode, reportCode string) ([]model.ExtractConfig, error) {\n\t\treturn s.configLoader.GetExtractConfigs(ctx, businessCode, reportCode)',
|
||||
'func (s *ReportService) GetExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {\n\t\treturn s.configLoader.GetExtractConfigs(ctx, businessCode, reportCode, pageNum, pageSize)'
|
||||
)
|
||||
|
||||
with open(api_path, 'w') as f:
|
||||
f.write(api)
|
||||
print('✅ Service done')
|
||||
|
||||
# ========== 3. Loader layer ==========
|
||||
loader_path = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/common/report/config/loader.go'
|
||||
with open(loader_path) as f:
|
||||
loader = f.read()
|
||||
|
||||
# 3a. GetAllBusinesses - add deleted_at + pagination
|
||||
old_biz = '''func (l *ConfigLoader) GetAllBusinesses(ctx context.Context) ([]model.BusinessConfig, error) {
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_business_config WHERE status = $1 ORDER BY id ASC",
|
||||
\t\tmodel.StatusActive)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, err
|
||||
\t}
|
||||
\tvar businesses []model.BusinessConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar biz model.BusinessConfig
|
||||
\t\tif err := record.Struct(&biz); err != nil {
|
||||
\t\t\treturn nil, err
|
||||
\t\t}
|
||||
\t\tbusinesses = append(businesses, biz)
|
||||
\t}
|
||||
\treturn businesses, nil
|
||||
}'''
|
||||
|
||||
new_biz = '''func (l *ConfigLoader) GetAllBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {
|
||||
\t// 查询总数
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_business_config WHERE deleted_at IS NULL")
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_business_config WHERE deleted_at IS NULL ORDER BY id ASC LIMIT $1 OFFSET $2",
|
||||
\t\tpageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar businesses []model.BusinessConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar biz model.BusinessConfig
|
||||
\t\tif err := record.Struct(&biz); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\tbusinesses = append(businesses, biz)
|
||||
\t}
|
||||
\treturn businesses, count, nil
|
||||
}'''
|
||||
|
||||
loader = loader.replace(old_biz, new_biz, 1)
|
||||
|
||||
# 3b. GetAllReports - add deleted_at + pagination
|
||||
old_rpt = '''func (l *ConfigLoader) GetAllReports(ctx context.Context, businessCode string) ([]model.ReportConfig, error) {
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_report_config WHERE business_code = $1 ORDER BY id ASC",
|
||||
\t\tbusinessCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, err
|
||||
\t}
|
||||
\tvar reports []model.ReportConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar rpt model.ReportConfig
|
||||
\t\tif err := record.Struct(&rpt); err != nil {
|
||||
\t\t\treturn nil, err
|
||||
\t\t}
|
||||
\t\treports = append(reports, rpt)
|
||||
\t}
|
||||
\treturn reports, nil
|
||||
}'''
|
||||
|
||||
new_rpt = '''func (l *ConfigLoader) GetAllReports(ctx context.Context, businessCode string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {
|
||||
\t// 查询总数
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL",
|
||||
\t\tbusinessCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL ORDER BY id ASC LIMIT $2 OFFSET $3",
|
||||
\t\tbusinessCode, pageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar reports []model.ReportConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar rpt model.ReportConfig
|
||||
\t\tif err := record.Struct(&rpt); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\treports = append(reports, rpt)
|
||||
\t}
|
||||
\treturn reports, count, nil
|
||||
}'''
|
||||
|
||||
loader = loader.replace(old_rpt, new_rpt, 1)
|
||||
|
||||
# 3c. GetExtractConfigs - add deleted_at + pagination
|
||||
old_ec = '''func (l *ConfigLoader) GetExtractConfigs(ctx context.Context, businessCode, reportCode string) ([]model.ExtractConfig, error) {
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND status = $3 AND is_enabled = $4",
|
||||
\t\tbusinessCode, reportCode, model.StatusActive, true)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, err
|
||||
\t}
|
||||
\tvar configs []model.ExtractConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar ec model.ExtractConfig
|
||||
\t\tif err := record.Struct(&ec); err != nil {
|
||||
\t\t\treturn nil, err
|
||||
\t\t}
|
||||
\t\tconfigs = append(configs, ec)
|
||||
\t}
|
||||
\tl.mu.Lock()
|
||||
\tl.extractCache[businessCode+":"+reportCode] = configs
|
||||
\tl.mu.Unlock()
|
||||
\treturn configs, nil
|
||||
}'''
|
||||
|
||||
new_ec = '''func (l *ConfigLoader) GetExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {
|
||||
\t// 查询总数
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL",
|
||||
\t\tbusinessCode, reportCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY id ASC LIMIT $3 OFFSET $4",
|
||||
\t\tbusinessCode, reportCode, pageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar configs []model.ExtractConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar ec model.ExtractConfig
|
||||
\t\tif err := record.Struct(&ec); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\tconfigs = append(configs, ec)
|
||||
\t}
|
||||
\treturn configs, count, nil
|
||||
}'''
|
||||
|
||||
loader = loader.replace(old_ec, new_ec, 1)
|
||||
|
||||
with open(loader_path, 'w') as f:
|
||||
f.write(loader)
|
||||
print('✅ Loader done')
|
||||
@@ -0,0 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
import re
|
||||
|
||||
path = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/common/report/config/loader.go'
|
||||
with open(path) as f:
|
||||
content = f.read()
|
||||
|
||||
# Fix `\$1` → `$1` (the \ came from Python escaping in previous script)
|
||||
content = content.replace('\\$1', '$1')
|
||||
content = content.replace('\\$2', '$2')
|
||||
|
||||
with open(path, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
print('Fixed')
|
||||
+255
@@ -0,0 +1,255 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Final pass: apply all list-pagination + deleted_at filter changes."""
|
||||
import re
|
||||
|
||||
# ============= LOADER =============
|
||||
lp = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/common/report/config/loader.go'
|
||||
with open(lp) as f:
|
||||
l = f.read()
|
||||
|
||||
# 1. GetAllBusinesses: status=ACTIVE -> deleted_at IS NULL
|
||||
l = l.replace(
|
||||
'"SELECT * FROM report_business_config WHERE status = $1 ORDER BY id ASC"',
|
||||
'"SELECT * FROM report_business_config WHERE deleted_at IS NULL ORDER BY id ASC"'
|
||||
)
|
||||
l = l.replace('\t\tmodel.StatusActive)', ')\n\t\t// (removed status filter)')
|
||||
|
||||
# 2. GetAllReports: add deleted_at IS NULL (keep existing)
|
||||
# Already has no status filter, just verify
|
||||
# (The user already removed status filter from GetAllReports earlier)
|
||||
|
||||
# 3. GetFields: status=ACTIVE -> deleted_at IS NULL
|
||||
l = l.replace(
|
||||
'"SELECT * FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND status = $3 ORDER BY sort_order ASC"',
|
||||
'"SELECT * FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY sort_order ASC"'
|
||||
)
|
||||
# Remove StatusActive arg from GetFields call
|
||||
l = l.replace(
|
||||
'businessCode, reportCode, model.StatusActive)',
|
||||
'businessCode, reportCode)'
|
||||
)
|
||||
|
||||
# 4. GetExtractConfigs: status=ACTIVE AND is_enabled=true -> deleted_at IS NULL
|
||||
l = l.replace(
|
||||
'"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND status = $3 AND is_enabled = $4"',
|
||||
'"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY id ASC"'
|
||||
)
|
||||
l = l.replace(
|
||||
'businessCode, reportCode, model.StatusActive, true)',
|
||||
'businessCode, reportCode)'
|
||||
)
|
||||
|
||||
# 5. Add GetAllFields (for edit query, returns all non-deleted regardless of status)
|
||||
if 'GetAllFields' not in l:
|
||||
# Add after GetFields function ends
|
||||
marker = 'return fields, nil\n}\n\n// GetFieldMap 获取字段配置Map'
|
||||
add = '''return fields, nil
|
||||
}
|
||||
|
||||
// GetAllFields 获取报表全部字段(含 INACTIVE,不含已删除,用于编辑回显)
|
||||
func (l *ConfigLoader) GetAllFields(ctx context.Context, businessCode, reportCode string) ([]model.FieldConfig, error) {
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_field_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY sort_order ASC",
|
||||
\t\tbusinessCode, reportCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, err
|
||||
\t}
|
||||
\tvar fields []model.FieldConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar f model.FieldConfig
|
||||
\t\tif err := record.Struct(&f); err != nil {
|
||||
\t\t\treturn nil, err
|
||||
\t\t}
|
||||
\t\tif f.ValidAggregates == nil {
|
||||
\t\t\tf.ValidAggregates = []string{}
|
||||
\t\t}
|
||||
\t\tif f.FilterOperators == nil {
|
||||
\t\t\tf.FilterOperators = []string{"=", "!=", ">", "<", ">=", "<=", "IN", "LIKE", "BETWEEN"}
|
||||
\t\t}
|
||||
\t\tfields = append(fields, f)
|
||||
\t}
|
||||
\treturn fields, nil
|
||||
}
|
||||
|
||||
// GetFieldMap 获取字段配置Map'''
|
||||
l = l.replace(marker, add, 1)
|
||||
|
||||
# 6. Add paginated list methods
|
||||
# ListBusinesses
|
||||
if 'ListBusinesses' not in l:
|
||||
add = '''// ListBusinesses 分页获取业务列表
|
||||
func (l *ConfigLoader) ListBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_business_config WHERE deleted_at IS NULL")
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_business_config WHERE deleted_at IS NULL ORDER BY id ASC LIMIT $1 OFFSET $2",
|
||||
\t\tpageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar businesses []model.BusinessConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar biz model.BusinessConfig
|
||||
\t\tif err := record.Struct(&biz); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\tbusinesses = append(businesses, biz)
|
||||
\t}
|
||||
\treturn businesses, count, nil
|
||||
}
|
||||
|
||||
// ListReports 分页获取报表列表
|
||||
func (l *ConfigLoader) ListReports(ctx context.Context, businessCode string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL",
|
||||
\t\tbusinessCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_report_config WHERE business_code = $1 AND deleted_at IS NULL ORDER BY id ASC LIMIT $2 OFFSET $3",
|
||||
\t\tbusinessCode, pageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar reports []model.ReportConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar rpt model.ReportConfig
|
||||
\t\tif err := record.Struct(&rpt); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\treports = append(reports, rpt)
|
||||
\t}
|
||||
\treturn reports, count, nil
|
||||
}
|
||||
|
||||
// ListExtractConfigs 分页获取抽取配置列表
|
||||
func (l *ConfigLoader) ListExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {
|
||||
\ttotal, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT COUNT(*) AS cnt FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL",
|
||||
\t\tbusinessCode, reportCode)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tcount := 0
|
||||
\tif !total.IsEmpty() {
|
||||
\t\tcount = total[0]["cnt"].Int()
|
||||
\t}
|
||||
\toffset := (pageNum - 1) * pageSize
|
||||
\tr, err := gfdb.DB(ctx).GetAll(ctx,
|
||||
\t\t"SELECT * FROM report_extract_config WHERE business_code = $1 AND report_code = $2 AND deleted_at IS NULL ORDER BY id ASC LIMIT $3 OFFSET $4",
|
||||
\t\tbusinessCode, reportCode, pageSize, offset)
|
||||
\tif err != nil {
|
||||
\t\treturn nil, 0, err
|
||||
\t}
|
||||
\tvar configs []model.ExtractConfig
|
||||
\tfor _, record := range r {
|
||||
\t\tvar ec model.ExtractConfig
|
||||
\t\tif err := record.Struct(&ec); err != nil {
|
||||
\t\t\treturn nil, 0, err
|
||||
\t\t}
|
||||
\t\tconfigs = append(configs, ec)
|
||||
\t}
|
||||
\tl.mu.Lock()
|
||||
\tl.extractCache[businessCode+":"+reportCode] = configs
|
||||
\tl.mu.Unlock()
|
||||
\treturn configs, count, nil
|
||||
}
|
||||
'''
|
||||
# Insert before the "//" comment separator near the end
|
||||
marker2 = '\n// GetReportFields 获取报表可用字段(按角色分类)'
|
||||
if marker2 in l:
|
||||
l = l.replace(marker2, add + marker2, 1)
|
||||
else:
|
||||
l += add
|
||||
|
||||
with open(lp, 'w') as f:
|
||||
f.write(l)
|
||||
print('✅ Loader')
|
||||
|
||||
# ============= SERVICE (api.go) =============
|
||||
ap = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/common/report/api.go'
|
||||
with open(ap) as f:
|
||||
a = f.read()
|
||||
|
||||
# Add ListBusinesses/ListReports/ListExtractConfigs service methods
|
||||
if 'func (s *ReportService) ListBusinesses' not in a:
|
||||
add_svc = '''
|
||||
// ListBusinesses 分页获取业务列表
|
||||
func (s *ReportService) ListBusinesses(ctx context.Context, pageNum, pageSize int) ([]model.BusinessConfig, int, error) {
|
||||
\treturn s.configLoader.ListBusinesses(ctx, pageNum, pageSize)
|
||||
}
|
||||
|
||||
// ListReports 分页获取报表列表
|
||||
func (s *ReportService) ListReports(ctx context.Context, businessCode string, pageNum, pageSize int) ([]model.ReportConfig, int, error) {
|
||||
\treturn s.configLoader.ListReports(ctx, businessCode, pageNum, pageSize)
|
||||
}
|
||||
|
||||
// ListExtractConfigs 分页获取抽取配置列表
|
||||
func (s *ReportService) ListExtractConfigs(ctx context.Context, businessCode, reportCode string, pageNum, pageSize int) ([]model.ExtractConfig, int, error) {
|
||||
\treturn s.configLoader.ListExtractConfigs(ctx, businessCode, reportCode, pageNum, pageSize)
|
||||
}
|
||||
'''
|
||||
# Insert after the service type definition
|
||||
a = a.replace(
|
||||
's.configLoader.GetAllBusinesses(ctx)\n}',
|
||||
's.configLoader.GetAllBusinesses(ctx)\n}' + add_svc
|
||||
)
|
||||
|
||||
with open(ap, 'w') as f:
|
||||
f.write(a)
|
||||
print('✅ Service')
|
||||
|
||||
# ============= CONTROLLER =============
|
||||
cp = '/Users/xujiaqian/GolandProjects/HDWL/data-engine/controller/report/report_controller.go'
|
||||
with open(cp) as f:
|
||||
c = f.read()
|
||||
|
||||
# Fix ListBusinesses handler
|
||||
c = c.replace(
|
||||
'list, err := svc().GetAllBusinesses(ctx)',
|
||||
'list, total, err := svc().ListBusinesses(ctx, req.PageNum, req.PageSize)'
|
||||
)
|
||||
c = c.replace(
|
||||
'return &listBusinessesRes{List: list}, nil',
|
||||
'totalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &listBusinessesRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
# Fix ListReports handler
|
||||
c = c.replace(
|
||||
'list, err := svc().GetAllReports(ctx, req.BusinessCode)',
|
||||
'list, total, err := svc().ListReports(ctx, req.BusinessCode, req.PageNum, req.PageSize)'
|
||||
)
|
||||
c = c.replace(
|
||||
'return &listReportsRes{List: list}, nil',
|
||||
'totalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &listReportsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
# Fix GetExtractConfigs handler
|
||||
c = c.replace(
|
||||
'list, err := svc().GetExtractConfigs(ctx, req.BusinessCode, req.ReportCode)',
|
||||
'list, total, err := svc().ListExtractConfigs(ctx, req.BusinessCode, req.ReportCode, req.PageNum, req.PageSize)'
|
||||
)
|
||||
c = c.replace(
|
||||
'return &getExtractConfigsRes{List: list}, nil',
|
||||
'totalPages := (total + req.PageSize - 1) / req.PageSize\n\t\treturn &getExtractConfigsRes{List: list, PageRes: model.PageRes{Total: total, Page: req.PageNum, PageSize: req.PageSize, TotalPages: totalPages}}, nil'
|
||||
)
|
||||
|
||||
with open(cp, 'w') as f:
|
||||
f.write(c)
|
||||
print('✅ Controller')
|
||||
|
||||
print('\nAll done!')
|
||||
@@ -1,34 +1,44 @@
|
||||
module cid
|
||||
module dataengine
|
||||
|
||||
go 1.25.5
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
gitea.com/red-future/common v0.0.4
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.0
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.9.5
|
||||
github.com/gogf/gf/v2 v2.10.0
|
||||
github.com/olekukonko/errors v1.1.0
|
||||
golang.org/x/net v0.47.0
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2
|
||||
github.com/gogf/gf/v2 v2.10.2
|
||||
github.com/olekukonko/errors v1.3.0
|
||||
github.com/sirupsen/logrus v1.9.4
|
||||
)
|
||||
|
||||
//replace gitea.com/red-future/common => ../common
|
||||
//replace gitea.redpowerfuture.com/red-future/common => ../common
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/armon/go-metrics v0.4.1 // indirect
|
||||
github.com/bwmarrin/snowflake v0.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/clbanning/mxj/v2 v2.7.0 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 // indirect
|
||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.9.0 // indirect
|
||||
github.com/fatih/color v1.19.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.10.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-ego/gse v1.0.2 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 // indirect
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
@@ -37,12 +47,13 @@ require (
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/flatbuffers v1.12.1 // indirect
|
||||
github.com/google/flatbuffers v25.12.19+incompatible // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 // indirect
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/hashicorp/consul/api v1.26.1 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/consul/api v1.33.5 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.5.0 // indirect
|
||||
@@ -51,42 +62,66 @@ require (
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
|
||||
github.com/hashicorp/golang-lru v1.0.2 // indirect
|
||||
github.com/hashicorp/serf v0.10.1 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.18.6 // indirect
|
||||
github.com/lib/pq v1.12.3 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mailru/easyjson v0.9.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.15 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.24 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/ll v0.1.8 // indirect
|
||||
github.com/olekukonko/tablewriter v1.1.4 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.12.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/r3labs/diff/v2 v2.15.1 // indirect
|
||||
github.com/redis/go-redis/v9 v9.21.0 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/tiger1103/gfast-token v1.0.10 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.4.0 // indirect
|
||||
go.opencensus.io v0.23.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
github.com/vcaesar/cedar v0.30.0 // indirect
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/otel v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.44.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||
golang.org/x/crypto v0.44.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250128144449-3edf0e91c1ae // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||
google.golang.org/grpc v1.75.0 // indirect
|
||||
google.golang.org/protobuf v1.36.8 // indirect
|
||||
go.uber.org/atomic v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/oauth2 v0.35.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.35.3 // indirect
|
||||
k8s.io/apimachinery v0.35.3 // indirect
|
||||
k8s.io/client-go v0.35.3 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -0,0 +1,552 @@
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29 h1:5McaN5pSewvrLUHQzWMX6EaUvD+B5I5bMYoU+clHJk4=
|
||||
gitea.redpowerfuture.com/red-future/common v0.0.29/go.mod h1:50U1Xi+Ie56z09S5LQbZvaken0Mxv3OeS9LgR7U/ZRY=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
|
||||
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
|
||||
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||
github.com/bwmarrin/snowflake v0.3.0 h1:xm67bEhkKh6ij1790JB83OujPR5CzNe8QuQqAgISZN0=
|
||||
github.com/bwmarrin/snowflake v0.3.0/go.mod h1:NdZxfVWX+oR6y2K0o6qAYv6gIOP9rjG0/E9WsDpxqwE=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0 h1:kJrlajbXXL9DFTNuhhu9yCx7JJa4qpYWxtE8BzuWsEs=
|
||||
github.com/dgraph-io/badger/v4 v4.2.0/go.mod h1:qfCqhPoWDFJRx1gp5QwwyGo8xk1lbHUxvK9nK0OGAak=
|
||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
|
||||
github.com/emirpasic/gods/v2 v2.0.0-alpha/go.mod h1:W0y4M2dtBB9U5z3YlghmpuUhiaZT2h6yoeE+C1sCp6A=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w=
|
||||
github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE=
|
||||
github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
|
||||
github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM=
|
||||
github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-ego/gse v1.0.2 h1:+27lYFPhQEhA9igtdOsJPRKYL/k3TwYsxBF5jr6KFv4=
|
||||
github.com/go-ego/gse v1.0.2/go.mod h1:Fy35G+q7VV7Et1zIKO8o/sW1kkugV3znXap/lF/11zc=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2 h1:u8EpP24GkprogROnJ7htMov9Fc66pTP1eVYrWxiCYOs=
|
||||
github.com/gogf/gf/contrib/drivers/pgsql/v2 v2.10.2/go.mod h1:GmvM3r8GVByVMi4RD2+MCs5+CfxVXPMeT8mVDkAaAXE=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2 h1:iTQegT+lEg/wDKvj2mi3W1wrdrwFarjokf88EXVVgu4=
|
||||
github.com/gogf/gf/contrib/nosql/redis/v2 v2.10.2/go.mod h1:ZRw3GNz5cq4uYrW4TPSVyrYWaoqzujKdWro/AOcGBaE=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5 h1:eUqwJ/qNH8lJ6yssiqskazgp1ACQuNU6zXlLOZVuXTQ=
|
||||
github.com/gogf/gf/contrib/registry/consul/v2 v2.9.5/go.mod h1:sjQyMry9+0POYZCA6lHXBxO77WoNKkruJpRB4xKqk5k=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5 h1:tHUEZYB5GTqEYYVDYnlGobf1xISARKDE4KHVlgjwTec=
|
||||
github.com/gogf/gf/contrib/trace/otlphttp/v2 v2.9.5/go.mod h1:cfzTn2HS9RDX8f5pUVkbGxUWcSosouqfNQ1G6cY0V88=
|
||||
github.com/gogf/gf/v2 v2.10.2 h1:46IO0Uc8e85/FqdftJFskfDejJLBL0JBnGS5qOftUu8=
|
||||
github.com/gogf/gf/v2 v2.10.2/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/glog v1.2.5 h1:DrW6hGnjIhtvhOIiAKT6Psh/Kd/ldepEa81DKeiRJ5I=
|
||||
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=
|
||||
github.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
|
||||
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible h1:haMV2JRRJCe1998HeW/p0X9UaMTK6SDo0ffLn2+DbLs=
|
||||
github.com/google/flatbuffers v25.12.19+incompatible/go.mod h1:1AeVuKshWv4vARoZatz6mlQ0JxURH0Kv5+zNeJKJCa8=
|
||||
github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo=
|
||||
github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
|
||||
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo=
|
||||
github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0 h1:03UrQLjAny8xci+R+qjCce/MYnpNXCtgzltlQbOBae4=
|
||||
github.com/grokify/html-strip-tags-go v0.1.0/go.mod h1:ZdzgfHEzAfz9X6Xe5eBLVblWIxXfYSQ40S/VKrAOGpc=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hashicorp/consul/api v1.33.5 h1:Nn6q87zudRU1rLBTJEgaWxz9STCNadilLCD7B8OA5aI=
|
||||
github.com/hashicorp/consul/api v1.33.5/go.mod h1:pa6fJOSHKLOzNHpUVeqLDtxA5+J1D7NNzLasuk8eRXA=
|
||||
github.com/hashicorp/consul/sdk v0.17.3 h1:oZMMxzQGSsiT+ToOH50y3Qcs0nc9Ud+7L5lRx+EmMU0=
|
||||
github.com/hashicorp/consul/sdk v0.17.3/go.mod h1:jnOmYjiNfVRpBaujQ1DFFVs0N6g3S1y6wygSjLTzYfc=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
|
||||
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c=
|
||||
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI=
|
||||
github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA=
|
||||
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
|
||||
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
|
||||
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
|
||||
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc=
|
||||
github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.2.1 h1:zEfKbn2+PDgroKdiOzqiE8rsmLqU2uwi5PB5pBJ3TkI=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
|
||||
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc=
|
||||
github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM=
|
||||
github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0=
|
||||
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
|
||||
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
|
||||
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11 h1:0OwqZRYI2rFrjS4kvkDnqJkKHdHaRnCm68/DY4OxRzU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.11/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lib/pq v1.12.3 h1:tTWxr2YLKwIvK90ZXEw8GP7UFHtcbTtty8zsI+YjrfQ=
|
||||
github.com/lib/pq v1.12.3/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA=
|
||||
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
|
||||
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
||||
github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M=
|
||||
github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-colorable v0.1.15 h1:+u9SLTRGnXv73cEsnsmoZBom+dMU88B2M0aDcWy0/jY=
|
||||
github.com/mattn/go-colorable v0.1.15/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.24 h1:cpokDiIn0MGnhdHwuWnJBITySJ20QyNGnY2kR/ay2DU=
|
||||
github.com/mattn/go-runewidth v0.0.24/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso=
|
||||
github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI=
|
||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
||||
github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8=
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc=
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.3.0 h1:teJvgLGUEqMzBUms+Dj3/3szNqCG/Jdw9iDbum8fR6U=
|
||||
github.com/olekukonko/errors v1.3.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8=
|
||||
github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw=
|
||||
github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I=
|
||||
github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2 h1:LzwLj0b89qtIy6SSASkzlNvX6WktqurSHwkk2ipF/Ns=
|
||||
github.com/onsi/ginkgo/v2 v2.27.2/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo=
|
||||
github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A=
|
||||
github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
|
||||
github.com/r3labs/diff/v2 v2.15.1 h1:EOrVqPUzi+njlumoqJwiS/TgGgmZo83619FNDB9xQUg=
|
||||
github.com/r3labs/diff/v2 v2.15.1/go.mod h1:I8noH9Fc2fjSaMxqF3G2lhDdC0b+JXCfyx85tWFM9kc=
|
||||
github.com/redis/go-redis/v9 v9.21.0 h1:FPBE4hhbAke+TLmcY3WkpbDffJEomdqPn3HYiqAtL9E=
|
||||
github.com/redis/go-redis/v9 v9.21.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/tiger1103/gfast-token v1.0.10 h1:fNiBE/Dq5iTHvTGlCx3DmXa2o4hr0NtumFpffZ39k6s=
|
||||
github.com/tiger1103/gfast-token v1.0.10/go.mod h1:a/21mxmj7zFeNvjhZSC0XpEAFHfb1aT2k6DXnufFU1s=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/vcaesar/cedar v0.30.0 h1:9fSDpM7FTjjUdPiBUUa0MWYMRGSEcqgFXvppZcZ4d7Y=
|
||||
github.com/vcaesar/cedar v0.30.0/go.mod h1:lyuGvALuZZDPNXwpzv/9LyxW+8Y6faN7zauFezNsnik=
|
||||
github.com/vcaesar/tt v0.20.1 h1:D/jUeeVCNbq3ad8M7hhtB3J9x5RZ6I1n1eZ0BJp7M+4=
|
||||
github.com/vcaesar/tt v0.20.1/go.mod h1:cH2+AwGAJm19Wa6xvEa+0r+sXDJBT0QgNQey6mwqLeU=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI=
|
||||
github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk=
|
||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
|
||||
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0 h1:b9sJOYrkmt4l8bY43ZenFBcPlhYIjaOfYHLtbB/5qi8=
|
||||
go.mongodb.org/mongo-driver/v2 v2.6.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
|
||||
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976 h1:X8Hz2ImujgbmetVuW+w2YkyZChE3cBpZi2P158rTG9M=
|
||||
golang.org/x/exp v0.0.0-20260611194520-c48552f49976/go.mod h1:vnf4pv9iKZXY58sQE1L86zmNWJ4159e1RkcWiLCkeEY=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
|
||||
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
|
||||
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo=
|
||||
gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.35.3 h1:pA2fiBc6+N9PDf7SAiluKGEBuScsTzd2uYBkA5RzNWQ=
|
||||
k8s.io/api v0.35.3/go.mod h1:9Y9tkBcFwKNq2sxwZTQh1Njh9qHl81D0As56tu42GA4=
|
||||
k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
|
||||
k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
|
||||
k8s.io/client-go v0.35.3 h1:s1lZbpN4uI6IxeTM2cpdtrwHcSOBML1ODNTCCfsP1pg=
|
||||
k8s.io/client-go v0.35.3/go.mod h1:RzoXkc0mzpWIDvBrRnD+VlfXP+lRzqQjCmKtiwZ8Q9c=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE=
|
||||
k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ=
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck=
|
||||
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg=
|
||||
sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco=
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE=
|
||||
sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=
|
||||
sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=
|
||||
@@ -0,0 +1,51 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: data-engine-config
|
||||
namespace: default
|
||||
data:
|
||||
config.yml: |
|
||||
server:
|
||||
address : ":3001"
|
||||
name: "data-engine"
|
||||
workerId: 1
|
||||
logPath: "resource/log/server"
|
||||
logStdout: true
|
||||
errorStack: true
|
||||
rate:
|
||||
limit: 200
|
||||
burst: 300
|
||||
database:
|
||||
default:
|
||||
- type: "pgsql"
|
||||
host: "localhost"
|
||||
port: "5432"
|
||||
user: "postgres"
|
||||
pass: "root"
|
||||
name: "cid"
|
||||
role: "master"
|
||||
maxIdle: "5"
|
||||
maxOpen: "20"
|
||||
maxLifetime: "60s"
|
||||
charset: "utf8mb4"
|
||||
debug: true
|
||||
dryRun: false
|
||||
createdAt: "created_at"
|
||||
updatedAt: "updated_at"
|
||||
deletedAt: "deleted_at"
|
||||
timeMaintainDisabled: false
|
||||
redis:
|
||||
default:
|
||||
address: 192.168.3.30:6379
|
||||
db: 0
|
||||
idleTimeout: "60s"
|
||||
maxConnLifetime: "90s"
|
||||
waitTimeout: "60s"
|
||||
dialTimeout: "30s"
|
||||
readTimeout: "30s"
|
||||
writeTimeout: "30s"
|
||||
maxActive: 100
|
||||
consul:
|
||||
address: ""
|
||||
jaeger:
|
||||
addr: ""
|
||||
@@ -0,0 +1,60 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: data-engine
|
||||
namespace: default
|
||||
labels:
|
||||
app: data-engine
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: data-engine
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: data-engine
|
||||
spec:
|
||||
containers:
|
||||
- name: data-engine
|
||||
image: data-engine:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 3002
|
||||
protocol: TCP
|
||||
name: http
|
||||
env:
|
||||
- name: TZ
|
||||
value: "Asia/Seoul"
|
||||
volumeMounts:
|
||||
- name: config-volume
|
||||
mountPath: /usr/local/bin/app/config.yml
|
||||
subPath: config.yml
|
||||
- name: log-volume
|
||||
mountPath: /usr/local/bin/app/resource/log
|
||||
resources:
|
||||
requests:
|
||||
memory: "512Mi"
|
||||
cpu: "500m"
|
||||
limits:
|
||||
memory: "1Gi"
|
||||
cpu: "1000m"
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 3002
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 3002
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 5
|
||||
timeoutSeconds: 3
|
||||
volumes:
|
||||
- name: config-volume
|
||||
configMap:
|
||||
name: data-engine-config
|
||||
- name: log-volume
|
||||
emptyDir: {}
|
||||
restartPolicy: Always
|
||||
@@ -0,0 +1,69 @@
|
||||
# Horizontal Pod Autoscaler - 水平Pod自动扩缩容配置
|
||||
# 根据 CPU 和内存使用率自动调整 Pod 副本数量
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: data-engine-hpa # HPA 资源名称
|
||||
namespace: default # 命名空间
|
||||
spec:
|
||||
# 指定要自动扩缩容的目标 Deployment
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: data-engine
|
||||
|
||||
# 副本数量范围
|
||||
minReplicas: 1 # 最小副本数:始终保持至少 1 个 Pod 运行
|
||||
maxReplicas: 10 # 最大副本数:防止无限扩容,最多扩展到 10 个 Pod
|
||||
|
||||
# 扩缩容触发指标(满足任一条件即触发)
|
||||
metrics:
|
||||
# CPU 使用率指标
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 70 # 当所有 Pod 的平均 CPU 使用率超过 70% 时触发扩容
|
||||
|
||||
# 内存使用率指标
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80 # 当所有 Pod 的平均内存使用率超过 80% 时触发扩容
|
||||
|
||||
# 扩缩容行为策略
|
||||
behavior:
|
||||
# 向上扩容策略(快速响应负载增加)
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 60 # 稳定窗口期:60 秒内只基于最高需求决策,防止抖动
|
||||
policies:
|
||||
# 策略1:按固定数量扩容
|
||||
- type: Pods
|
||||
value: 2 # 每次最多增加 2 个 Pod
|
||||
periodSeconds: 60 # 每 60 秒评估一次
|
||||
|
||||
# 策略2:按比例扩容
|
||||
- type: Percent
|
||||
value: 50 # 每次最多增加当前副本数的 50%
|
||||
periodSeconds: 60 # 每 60 秒评估一次
|
||||
|
||||
selectPolicy: Max # 选择两个策略中扩容幅度更大的方式(更激进)
|
||||
|
||||
# 向下缩容策略(保守缩容,避免频繁波动)
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300 # 稳定窗口期:300 秒(5分钟),比扩容更保守
|
||||
policies:
|
||||
# 策略1:按固定数量缩容
|
||||
- type: Pods
|
||||
value: 1 # 每次最多减少 1 个 Pod
|
||||
periodSeconds: 60 # 每 60 秒评估一次
|
||||
|
||||
# 策略2:按比例缩容
|
||||
- type: Percent
|
||||
value: 25 # 每次最多减少当前副本数的 25%
|
||||
periodSeconds: 60 # 每 60 秒评估一次
|
||||
|
||||
selectPolicy: Min # 选择两个策略中缩容幅度更小的方式(更保守)
|
||||
@@ -0,0 +1,12 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data-engine-log-pvc
|
||||
namespace: default
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
storageClassName: local-path
|
||||
@@ -0,0 +1,17 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: data-engine-service
|
||||
namespace: default
|
||||
labels:
|
||||
app: data-engine
|
||||
spec:
|
||||
type: NodePort
|
||||
selector:
|
||||
app: data-engine
|
||||
ports:
|
||||
- name: http
|
||||
port: 3002
|
||||
targetPort: 3002
|
||||
nodePort: 30302
|
||||
protocol: TCP
|
||||
@@ -1,33 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"cid/controller/copydata"
|
||||
"cid/controller/dict"
|
||||
"dataengine/common/report"
|
||||
"dataengine/controller/debug"
|
||||
"dataengine/controller/dict"
|
||||
"dataengine/controller/public"
|
||||
reportCtrl "dataengine/controller/report"
|
||||
syncCtrl "dataengine/controller/sync"
|
||||
syncSvc "dataengine/service/sync"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"gitea.com/red-future/common/http"
|
||||
"gitea.com/red-future/common/jaeger"
|
||||
_ "gitea.com/red-future/common/ragflow" // RAGFlow 客户端自动初始化
|
||||
"gitea.redpowerfuture.com/red-future/common/http"
|
||||
"gitea.redpowerfuture.com/red-future/common/jaeger"
|
||||
_ "gitea.redpowerfuture.com/red-future/common/k3sconfig"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/nosql/redis/v2"
|
||||
"golang.org/x/net/context"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/os/gctx"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func main() {
|
||||
ctx := context.Background()
|
||||
ctx := gctx.New()
|
||||
defer jaeger.ShutDown(ctx)
|
||||
|
||||
// 内部调度开关(默认关闭,由 PPGo_Job 通过 HTTP 端点统一调度)
|
||||
extractEnabled := g.Cfg().MustGet(ctx, "report.extract_enabled", false).Bool()
|
||||
syncEnabled := g.Cfg().MustGet(ctx, "sync.auto_sync_enabled", false).Bool()
|
||||
if extractEnabled || syncEnabled {
|
||||
if syncEnabled {
|
||||
syncSvc.InitAndStartAutoSync(ctx)
|
||||
}
|
||||
if extractEnabled {
|
||||
report.StartDailyExtractJob()
|
||||
}
|
||||
logrus.Infof("[main] 内部调度已启用 extract=%v sync=%v", extractEnabled, syncEnabled)
|
||||
} else {
|
||||
logrus.Info("[main] 内部调度已禁用,由 PPGo_Job 外部调度")
|
||||
}
|
||||
|
||||
http.RouteRegister([]interface{}{
|
||||
// 接口管理
|
||||
dict.ApiInterface,
|
||||
dict.FieldMappingConfig,
|
||||
dict.DatasourcePlatform,
|
||||
copydata.AccountReportDetail,
|
||||
copydata.TaskReport,
|
||||
copydata.PopulationReport,
|
||||
copydata.MaterialReport,
|
||||
copydata.StorewideReport,
|
||||
copydata.CreativeReport,
|
||||
copydata.UnitReport,
|
||||
// 平台同步引擎
|
||||
syncCtrl.PlatformSyncController,
|
||||
// 公共查询接口
|
||||
public.PublicQuery,
|
||||
// 报表引擎 CRUD API
|
||||
reportCtrl.ReportController,
|
||||
})
|
||||
select {}
|
||||
|
||||
// 管理后台页面
|
||||
g.Server().BindHandler("/admin", debug.DebugController.DebugPage)
|
||||
// 报表引擎管理页面
|
||||
g.Server().BindHandler("/admin/report", reportCtrl.ReportAdminPage)
|
||||
|
||||
// 捕获退出信号,实现优雅关闭
|
||||
quit := make(chan os.Signal, 1)
|
||||
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
|
||||
sig := <-quit
|
||||
logrus.Infof("收到信号 %v,正在退出...", sig)
|
||||
}
|
||||
|
||||
@@ -1,318 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateCidAccountReportDetailReq 创建广告数据报表详情请求
|
||||
type CreateCidAccountReportDetailReq struct {
|
||||
g.Meta `path:"/createCidAccountReportDetail" method:"post" tags:"广告数据报表" summary:"创建广告数据报表详情" dc:"创建新的广告数据报表详情"`
|
||||
*CidAccountReportDetailItem
|
||||
}
|
||||
|
||||
// CreateCidAccountReportDetailRes 创建广告数据报表详情响应
|
||||
type CreateCidAccountReportDetailRes struct {
|
||||
Id int64 `json:"id" dc:"广告数据ID"`
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportDetailReq 批量创建广告数据报表详情请求
|
||||
type BatchCreateCidAccountReportDetailReq struct {
|
||||
g.Meta `path:"/batchCreateCidAccountReportDetail" method:"post" tags:"广告数据报表" summary:"批量创建广告数据报表详情" dc:"批量创建广告数据报表详情"`
|
||||
Items []*CidAccountReportDetailItem `json:"items" v:"required" dc:"广告数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportDetailRes 批量创建广告数据报表详情响应
|
||||
type BatchCreateCidAccountReportDetailRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// CidAccountReportDetailItem 广告数据报表详情项
|
||||
type CidAccountReportDetailItem struct {
|
||||
// 基础信息字段
|
||||
DataType string `json:"dataType" v:"required" dc:"类型type"`
|
||||
|
||||
// 核心指标字段
|
||||
T0OrderPaymentAmt string `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面URL"`
|
||||
CoverId int64 `json:"coverId" dc:"封面Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交GMV"`
|
||||
|
||||
// 视频信息字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频id"`
|
||||
PhotoId string `json:"photoId" dc:"视频id"`
|
||||
|
||||
// 受众属性字段
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间"`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品5秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品3秒播放率"`
|
||||
|
||||
// 订单相关字段
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
|
||||
// 商品信息字段
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
|
||||
// 封面相关字段
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
|
||||
// 点击相关字段
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
|
||||
// 直播时长字段
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
|
||||
// 播放数据字段
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品3秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品3秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品5秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品5秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
|
||||
// 互动数据字段
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动字段
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
|
||||
// 播放效果字段
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
|
||||
// 转化字段
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接GMV"`
|
||||
|
||||
// GMV时间序列字段
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计GMV"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3日累计GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7日累计GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15日累计GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30日累计GMV"`
|
||||
|
||||
// ROI时间序列字段
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计ROI"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3日累计ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7日累计ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15日累计ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30日累计ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30日累计订单数"`
|
||||
|
||||
// 粉丝相关字段
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30日涨粉留存率"`
|
||||
|
||||
// 直播预约字段
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
|
||||
// 直播观看字段
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
|
||||
// 商品点击字段
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30日累计店铺新客"`
|
||||
|
||||
// 间接订单字段
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额字段
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本字段
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
|
||||
// 智能优惠券字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝GMV字段
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉7日GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉15日GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉30日GMV"`
|
||||
|
||||
// 粉丝ROI字段
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉7日ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉15日ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉30日ROI"`
|
||||
|
||||
// 新客GMV字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后1日新客GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后3日新客GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后7日新客GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后15日新客GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后30日新客GMV"`
|
||||
|
||||
// 新客订单量字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后1日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后3日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后7日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后15日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后30日新客成交订单量"`
|
||||
|
||||
// 新客复购率字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后1日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后3日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后7日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后15日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后30日新客复购率"`
|
||||
|
||||
// 新客ROI字段
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后1日新客ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后3日新客ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后7日新客ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后15日新客ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后30日新客ROI"`
|
||||
|
||||
// 电话卡相关字段
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
|
||||
// 时间字段
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
|
||||
// 广告结构字段
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
|
||||
// 补贴相关字段
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
// ListCidAccountReportDetailReq 获取广告数据报表详情列表请求
|
||||
type ListCidAccountReportDetailReq struct {
|
||||
g.Meta `path:"/listCidAccountReportDetail" method:"get" tags:"广告数据报表" summary:"获取广告数据报表详情列表" dc:"分页查询广告数据报表详情列表"`
|
||||
*beans.Page
|
||||
DataType string `json:"dataType" dc:"类型type"`
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListCidAccountReportDetailRes 获取广告数据报表详情列表响应
|
||||
type ListCidAccountReportDetailRes struct {
|
||||
List []*CidAccountReportDetailItem `json:"list" dc:"广告数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,318 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateCidAccountReportSumReq 创建广告数据报表详情请求
|
||||
type CreateCidAccountReportSumReq struct {
|
||||
g.Meta `path:"/createCidAccountReportSum" method:"post" tags:"广告数据报表" summary:"创建广告数据报表详情" dc:"创建新的广告数据报表详情"`
|
||||
*CidAccountReportSumItem
|
||||
}
|
||||
|
||||
// CreateCidAccountReportSumRes 创建广告数据报表详情响应
|
||||
type CreateCidAccountReportSumRes struct {
|
||||
Id int64 `json:"id" dc:"广告数据ID"`
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportSumReq 批量创建广告数据报表详情请求
|
||||
type BatchCreateCidAccountReportSumReq struct {
|
||||
g.Meta `path:"/batchCreateCidAccountReportSum" method:"post" tags:"广告数据报表" summary:"批量创建广告数据报表详情" dc:"批量创建广告数据报表详情"`
|
||||
Items []*CidAccountReportSumItem `json:"items" v:"required" dc:"广告数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateCidAccountReportSumRes 批量创建广告数据报表详情响应
|
||||
type BatchCreateCidAccountReportSumRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// CidAccountReportSumItem 广告数据报表详情项
|
||||
type CidAccountReportSumItem struct {
|
||||
// 基础信息字段
|
||||
DataType string `json:"dataType" v:"required" dc:"类型type"`
|
||||
|
||||
// 核心指标字段
|
||||
T0OrderPaymentAmt string `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面URL"`
|
||||
CoverId int64 `json:"coverId" dc:"封面Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交GMV"`
|
||||
|
||||
// 视频信息字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频id"`
|
||||
PhotoId string `json:"photoId" dc:"视频id"`
|
||||
|
||||
// 受众属性字段
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间"`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品5秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品3秒播放率"`
|
||||
|
||||
// 订单相关字段
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
|
||||
// 商品信息字段
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
|
||||
// 封面相关字段
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
|
||||
// 点击相关字段
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
|
||||
// 直播时长字段
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
|
||||
// 播放数据字段
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品3秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品3秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品5秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品5秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
|
||||
// 互动数据字段
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动字段
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
|
||||
// 播放效果字段
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
|
||||
// 转化字段
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接GMV"`
|
||||
|
||||
// GMV时间序列字段
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计GMV"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3日累计GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7日累计GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15日累计GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30日累计GMV"`
|
||||
|
||||
// ROI时间序列字段
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计ROI"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3日累计ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7日累计ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15日累计ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30日累计ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30日累计订单数"`
|
||||
|
||||
// 粉丝相关字段
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30日涨粉留存率"`
|
||||
|
||||
// 直播预约字段
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
|
||||
// 直播观看字段
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
|
||||
// 商品点击字段
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30日累计店铺新客"`
|
||||
|
||||
// 间接订单字段
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额字段
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本字段
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
|
||||
// 智能优惠券字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝GMV字段
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉7日GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉15日GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉30日GMV"`
|
||||
|
||||
// 粉丝ROI字段
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉7日ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉15日ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉30日ROI"`
|
||||
|
||||
// 新客GMV字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后1日新客GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后3日新客GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后7日新客GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后15日新客GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后30日新客GMV"`
|
||||
|
||||
// 新客订单量字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后1日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后3日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后7日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后15日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后30日新客成交订单量"`
|
||||
|
||||
// 新客复购率字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后1日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后3日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后7日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后15日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后30日新客复购率"`
|
||||
|
||||
// 新客ROI字段
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后1日新客ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后3日新客ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后7日新客ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后15日新客ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后30日新客ROI"`
|
||||
|
||||
// 电话卡相关字段
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
|
||||
// 时间字段
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
|
||||
// 广告结构字段
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
|
||||
// 补贴相关字段
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
// ListCidAccountReportSumReq 获取广告数据报表详情列表请求
|
||||
type ListCidAccountReportSumReq struct {
|
||||
g.Meta `path:"/listCidAccountReportSum" method:"get" tags:"广告数据报表" summary:"获取广告数据报表详情列表" dc:"分页查询广告数据报表详情列表"`
|
||||
*beans.Page
|
||||
DataType string `json:"dataType" dc:"类型type"`
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListCidAccountReportSumRes 获取广告数据报表详情列表响应
|
||||
type ListCidAccountReportSumRes struct {
|
||||
List []*CidAccountReportSumItem `json:"list" dc:"广告数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,230 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
type CreateCreativeReportDetailReq struct {
|
||||
g.Meta `path:"/createCreativeReportDetail" method:"post" tags:"广告效果明细报表" summary:"创建广告效果指标明细表" dc:"创建新的广告效果指标明细表"`
|
||||
*CreativeReportDetailItem
|
||||
}
|
||||
|
||||
type CreateCreativeReportDetailRes struct {
|
||||
Id int64 `json:"id" dc:"广告效果明细 ID"`
|
||||
}
|
||||
|
||||
type BatchCreateCreativeReportDetailReq struct {
|
||||
g.Meta `path:"/batchCreateCreativeReportDetail" method:"post" tags:"广告效果明细报表" summary:"批量创建广告效果指标明细表" dc:"批量创建广告效果指标明细表"`
|
||||
Items []*CreativeReportDetailItem `json:"items" v:"required" dc:"广告效果明细列表"`
|
||||
}
|
||||
|
||||
type BatchCreateCreativeReportDetailRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
type CreativeReportDetailItem struct {
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间"`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"性别"`
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品 3 秒播放率"`
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接 ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接 GMV"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30 日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7 日累计 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15 日累计 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30 日累计 ROI"`
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30 日累计订单数"`
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30 日涨粉留存率"`
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30 日累计店铺新客"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3 日累计订单数"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3 日累计 ROI"`
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30 日新增粉丝人均销售额"`
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉 30 日 GMV"`
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后 30 日新客 GMV"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后 30 日新客成交订单量"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后 30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后 30 日新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
type ListCreativeReportDetailReq struct {
|
||||
g.Meta `path:"/listCreativeReportDetail" method:"get" tags:"广告效果明细报表" summary:"获取广告效果指标明细表列表" dc:"分页查询广告效果指标明细表列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字"`
|
||||
}
|
||||
|
||||
type ListCreativeReportDetailRes struct {
|
||||
List []*CreativeReportDetailItem `json:"list" dc:"广告效果明细列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,342 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateCreativeReportSumReq 创建广告效果指标表请求
|
||||
type CreateCreativeReportSumReq struct {
|
||||
g.Meta `path:"/createCreativeReportSum" method:"post" tags:"广告效果报表" summary:"创建广告效果指标表" dc:"创建新的广告效果指标表"`
|
||||
*CreativeReportSumItem
|
||||
}
|
||||
|
||||
// CreateCreativeReportSumRes 创建广告效果指标表响应
|
||||
type CreateCreativeReportSumRes struct {
|
||||
Id int64 `json:"id" dc:"广告效果 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateCreativeReportSumReq 批量创建广告效果指标表请求
|
||||
type BatchCreateCreativeReportSumReq struct {
|
||||
g.Meta `path:"/batchCreateCreativeReportSum" method:"post" tags:"广告效果报表" summary:"批量创建广告效果指标表" dc:"批量创建广告效果指标表"`
|
||||
Items []*CreativeReportSumItem `json:"items" v:"required" dc:"广告效果列表"`
|
||||
}
|
||||
|
||||
// BatchCreateCreativeReportSumRes 批量创建广告效果指标表响应
|
||||
type BatchCreateCreativeReportSumRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// CreativeReportSumItem 广告效果指标表项
|
||||
type CreativeReportSumItem struct {
|
||||
// 订单金额相关字段
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
|
||||
// 素材类型字段
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
|
||||
// 直播相关字段
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
|
||||
// 图片相关字段
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
|
||||
// 封面相关字段
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
|
||||
// 成本相关字段
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
|
||||
// 净成交相关字段
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
|
||||
// 视频相关字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
|
||||
// 用户属性相关字段
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间"`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品 3 秒播放率"`
|
||||
|
||||
// 订单提交相关字段
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
|
||||
// 用户行为相关字段
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
|
||||
// 转化成本相关字段
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
|
||||
// 商品相关字段
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
|
||||
// 曝光相关字段
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
|
||||
// 行为相关字段
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
|
||||
// 直播时长相关字段
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
|
||||
// 作品播放相关字段
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
|
||||
// 作品互动相关字段
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动相关字段
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
|
||||
// 有效播放相关字段
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
|
||||
// 转化相关字段
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接 ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接 GMV"`
|
||||
|
||||
// 累计 GMV 相关字段
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30 日累计 GMV"`
|
||||
|
||||
// 累计 ROI 相关字段
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7 日累计 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15 日累计 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30 日累计 ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30 日累计订单数"`
|
||||
|
||||
// 涨粉相关字段
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30 日涨粉留存率"`
|
||||
|
||||
// 直播预约相关字段
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
|
||||
// 直播观看相关字段
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
|
||||
// 直播间商品相关字段
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30 日累计店铺新客"`
|
||||
|
||||
// 3 日累计相关字段
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3 日累计订单数"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3 日累计 ROI"`
|
||||
|
||||
// 7 日间接订单相关字段
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7 日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额相关字段
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30 日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本相关字段
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
|
||||
// 智能优惠券相关字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝 GMV 相关字段
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉 30 日 GMV"`
|
||||
|
||||
// 粉丝 ROI 相关字段
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉 30 日 ROI"`
|
||||
|
||||
// 新客 GMV 相关字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后 30 日新客 GMV"`
|
||||
|
||||
// 新客订单数相关字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后 30 日新客成交订单量"`
|
||||
|
||||
// 新客复购率相关字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后 30 日新客复购率"`
|
||||
|
||||
// 新客 ROI 相关字段
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后 30 日新客 ROI"`
|
||||
|
||||
// 制卡订单相关字段
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
|
||||
// 电话卡激活相关字段
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
|
||||
// 制卡订单率相关字段
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
|
||||
// 电话卡激活率相关字段
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 直播观看相关字段
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
|
||||
// 报告日期字段
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
|
||||
// 广告计划相关字段
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
|
||||
// 广告单元相关字段
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
|
||||
// 广告创意相关字段
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
|
||||
// 新增字段
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
// ListCreativeReportSumReq 获取广告效果指标表列表请求
|
||||
type ListCreativeReportSumReq struct {
|
||||
g.Meta `path:"/listCreativeReportSum" method:"get" tags:"广告效果报表" summary:"获取广告效果指标表列表" dc:"分页查询广告效果指标表列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListCreativeReportSumRes 获取广告效果指标表列表响应
|
||||
type ListCreativeReportSumRes struct {
|
||||
List []*CreativeReportSumItem `json:"list" dc:"广告效果列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,234 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateMaterialReportReq 创建素材报表数据请求
|
||||
type CreateMaterialReportReq struct {
|
||||
g.Meta `path:"/createMaterialReport" method:"post" tags:"素材报表" summary:"创建素材报表数据" dc:"创建新的素材报表数据"`
|
||||
*MaterialReportItem
|
||||
}
|
||||
|
||||
// CreateMaterialReportRes 创建素材报表数据响应
|
||||
type CreateMaterialReportRes struct {
|
||||
Id int64 `json:"id" dc:"素材报表数据 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateMaterialReportReq 批量创建素材报表数据请求
|
||||
type BatchCreateMaterialReportReq struct {
|
||||
g.Meta `path:"/batchCreateMaterialReport" method:"post" tags:"素材报表" summary:"批量创建素材报表数据" dc:"批量创建素材报表数据"`
|
||||
Items []*MaterialReportItem `json:"items" v:"required" dc:"素材报表数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateMaterialReportRes 批量创建素材报表数据响应
|
||||
type BatchCreateMaterialReportRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// MaterialReportItem 素材报表数据项
|
||||
type MaterialReportItem struct {
|
||||
CreatedBy *string `json:"createdBy" dc:"创建人"`
|
||||
UpdatedBy *string `json:"updatedBy" dc:"更新人"`
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日订单支付金额"`
|
||||
CreativeMaterialType *string `json:"creativeMaterialType" dc:"创意素材类型"`
|
||||
LiveName *string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId *string `json:"authorId" dc:"作者 ID"`
|
||||
PicUrl *string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName *string `json:"picName" dc:"图片名称"`
|
||||
PicId *string `json:"picId" dc:"图片 ID"`
|
||||
CoverUrl *string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 ID"`
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"商品订单转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"商品卡点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"商家涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"商家涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"当日净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"当日净成交 GMV"`
|
||||
PhotoName *string `json:"photoName" dc:"照片名称"`
|
||||
PhotoIdStr *string `json:"photoIdStr" dc:"照片 ID 字符串"`
|
||||
PhotoId *string `json:"photoId" dc:"照片 ID"`
|
||||
ModPriceSegment *string `json:"modPriceSegment" dc:"价格区间"`
|
||||
AgeSegment *string `json:"ageSegment" dc:"年龄段"`
|
||||
Province *string `json:"province" dc:"省份"`
|
||||
Gender *string `json:"gender" dc:"性别"`
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"广告照片播放 5 秒比率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"广告照片播放 3 秒比率"`
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"提交订单 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"提交订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"事件订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"事件订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"事件订单支付 ROI"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"事件 APP 调用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"事件加购数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化数量成本"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"广告有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"广告商品点击数"`
|
||||
MerchantProductId *string `json:"merchantProductId" dc:"商家商品 ID"`
|
||||
CostTotal *float64 `json:"costTotal" dc:"总花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"广告展示数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"千次展示成本"`
|
||||
Impression *int64 `json:"impression" dc:"曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"照片点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"照片点击率"`
|
||||
Click *int64 `json:"click" dc:"点击数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"操作栏点击数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"操作栏点击成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"ESP 点击率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"操作比率"`
|
||||
AdItemCount *int64 `json:"adItemCount" dc:"广告商品点击次数"`
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"ESP 直播播放秒数"`
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"播放 3 秒数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"播放 5 秒数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"播放结束数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"完播率"`
|
||||
Share *int64 `json:"share" dc:"分享数"`
|
||||
Comment *int64 `json:"comment" dc:"评论数"`
|
||||
Likes *int64 `json:"likes" dc:"点赞数"`
|
||||
Report *int64 `json:"report" dc:"举报数"`
|
||||
Block *int64 `json:"block" dc:"拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"商品负反馈数"`
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播分享数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播打赏数"`
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本 ESP"`
|
||||
Roi *float64 `json:"roi" dc:"ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"GMV"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7 日 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15 日 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30 日 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7 日 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15 日 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30 日 ROI"`
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"支付订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"订单比率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"当日订单比率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7 日订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15 日订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30 日订单数"`
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"商家推荐粉丝数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日留存率"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7 日留存率"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15 日留存率"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30 日留存率"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日留存比率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7 日留存比率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15 日留存比率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30 日留存比率"`
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"预约成本"`
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"标准直播开始数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"广告直播播放数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"广告直播播放成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观众成本"`
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播事件商品浏览数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接吸引平台新买家数"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30 日吸引平台总买家数"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接吸引商家新买家数"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30 日吸引商家总买家数"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3 日 GMV"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3 日订单数"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3 日 ROI"`
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"粉丝当日人均 GMV"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"粉丝 3 日人均 GMV"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"粉丝 7 日人均 GMV"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"粉丝 15 日人均 GMV"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"粉丝 30 日人均 GMV"`
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"推荐粉丝成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"白盒直接订单支付金额"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"白盒直接订单数"`
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"粉丝当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"粉丝次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"粉丝 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"粉丝 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"粉丝 30 日 GMV"`
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"粉丝当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"粉丝次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"粉丝 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"粉丝 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"粉丝 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日店铺新客订单支付金额"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"次日店铺新客订单支付金额"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"3 日店铺新客订单支付金额"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"7 日店铺新客订单支付金额"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"15 日店铺新客订单支付金额"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"30 日店铺新客订单支付金额"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日店铺新客订单数"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"次日店铺新客订单数"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"3 日店铺新客订单数"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"7 日店铺新客订单数"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"15 日店铺新客订单数"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"30 日店铺新客订单数"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"次日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"当日店铺新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"次日店铺新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"3 日店铺新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"7 日店铺新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"15 日店铺新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"30 日店铺新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"创建卡片订单数"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"转发 TS 创建卡片订单数"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"创建卡片订单成本"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"转发 TS 创建卡片订单成本"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"激活卡片订单数"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"转发 TS 激活卡片订单数"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"激活卡片订单成本"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"转发 TS 激活卡片订单成本"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"创建卡片订单比率"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"转发 TS 创建卡片订单比率"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"激活卡片订单比率"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"转发 TS 激活卡片订单比率"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"直播播放数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"商品入口点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"展示数"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"报告日期(格式:YYYY-MM-DD)"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName *string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName *string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName *string `json:"creativeName" dc:"创意名称"`
|
||||
}
|
||||
|
||||
// ListMaterialReportReq 获取素材报表数据列表请求
|
||||
type ListMaterialReportReq struct {
|
||||
g.Meta `path:"/listMaterialReport" method:"get" tags:"素材报表" summary:"获取素材报表数据列表" dc:"分页查询素材报表数据列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"报告日期"`
|
||||
PhotoId string `json:"photoId" dc:"照片 ID"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索照片名称、计划名称等)"`
|
||||
}
|
||||
|
||||
// ListMaterialReportRes 获取素材报表数据列表响应
|
||||
type ListMaterialReportRes struct {
|
||||
List []*MaterialReportItem `json:"list" dc:"素材报表数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,210 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// PopulationReportItem 调控任务数据项
|
||||
type PopulationReportItem struct {
|
||||
PhotoName string `json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `json:"photoId" description:"视频 id"`
|
||||
ModPriceSegment string `json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `json:"ageSegment" description:"年龄段"`
|
||||
Province string `json:"province" description:"省份名称"`
|
||||
Gender string `json:"gender" description:"性别"`
|
||||
MerchantProductId string `json:"merchantProductId" description:"商品 ID"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" description:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" description:"单元 ID"`
|
||||
UnitName string `json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" description:"创意名称"`
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" description:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" description:"作品 3 秒播放率"`
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" description:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" description:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" description:"行为数"`
|
||||
CostTotal *float64 `json:"costTotal" description:"花费"`
|
||||
AdShow *int64 `json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
Impression *int64 `json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" description:"封面点击率"`
|
||||
Click *int64 `json:"click" description:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" description:"预约组件点击数"`
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" description:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" description:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" description:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" description:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" description:"作品完播率"`
|
||||
Share *int64 `json:"share" description:"作品分享数"`
|
||||
Comment *int64 `json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `json:"report" description:"作品举报数"`
|
||||
Block *int64 `json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
LiveShare *int64 `json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" description:"直播送礼数"`
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" description:"有效播放率"`
|
||||
ConversionNum *int64 `json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `json:"roi" description:"直接 ROI"`
|
||||
Gmv *float64 `json:"gmv" description:"直接 GMV"`
|
||||
T0Gmv *float64 `json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" description:"次日累计 GMV"`
|
||||
T3Gmv *float64 `json:"t3Gmv" description:"3 日累计 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" description:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" description:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" description:"30 日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" description:"当日累计 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" description:"次日累计 ROI"`
|
||||
T3Roi *float64 `json:"t3Roi" description:"3 日累计 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" description:"7 日累计 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" description:"15 日累计 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" description:"30 日累计 ROI"`
|
||||
PaiedOrder *int64 `json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" description:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" description:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" description:"30 日累计订单数"`
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" description:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" description:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" description:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" description:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" description:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" description:"30 日涨粉留存率"`
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" description:"直播预约成功成本"`
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" description:"直播观看成本"`
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" description:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" description:"30 日累计店铺新客"`
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" description:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" description:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" description:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" description:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" description:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" description:"30 日新增粉丝人均销售额"`
|
||||
RecoFansCost *float64 `json:"recoFansCost" description:"涨粉成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" description:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" description:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" description:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" description:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" description:"涨粉 30 日 GMV"`
|
||||
FansT0Roi *float64 `json:"fansT0Roi" description:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" description:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" description:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" description:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" description:"涨粉 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后 30 日新客 GMV"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" description:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" description:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" description:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" description:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" description:"投后 30 日新客成交订单量"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" description:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" description:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" description:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" description:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" description:"投后 30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" description:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" description:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" description:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" description:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" description:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" description:"投后 30 日新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" description:"全站曝光"`
|
||||
}
|
||||
|
||||
// CreatePopulationReportReq 创建调控任务数据请求参数
|
||||
type CreatePopulationReportReq struct {
|
||||
g.Meta `path:"/createPopulationReport" method:"post"`
|
||||
*PopulationReportItem
|
||||
}
|
||||
|
||||
// CreatePopulationReportRes 创建调控任务数据响应参数
|
||||
type CreatePopulationReportRes struct {
|
||||
Id int64 `json:"id"`
|
||||
}
|
||||
|
||||
// BatchCreatePopulationReportReq 批量创建调控任务数据请求参数
|
||||
type BatchCreatePopulationReportReq struct {
|
||||
g.Meta `path:"/batchCreatePopulationReport" method:"post"`
|
||||
Items []*PopulationReportItem `json:"items" v:"required"`
|
||||
}
|
||||
|
||||
// BatchCreatePopulationReportRes 批量创建调控任务数据响应参数
|
||||
type BatchCreatePopulationReportRes struct {
|
||||
SuccessCount int64 `json:"successCount"`
|
||||
FailCount int64 `json:"failCount"`
|
||||
FailedItems []int64 `json:"failedItems"`
|
||||
}
|
||||
|
||||
// ListPopulationReportReq 查询调控任务数据列表请求参数
|
||||
type ListPopulationReportReq struct {
|
||||
g.Meta `path:"/listPopulationReport" method:"post"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" description:"时间"`
|
||||
PhotoId string `json:"photoId" description:"视频 id"`
|
||||
Keyword string `json:"keyword" description:"关键词"`
|
||||
}
|
||||
|
||||
// ListPopulationReportRes 查询调控任务数据列表响应参数
|
||||
type ListPopulationReportRes struct {
|
||||
List []*PopulationReportItem `json:"list"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateStorewideReportDetailReq 创建广告效果指标表请求
|
||||
type CreateStorewideReportDetailReq struct {
|
||||
g.Meta `path:"/createStorewideReportDetail" method:"post" tags:"广告效果指标" summary:"创建广告效果指标表" dc:"创建新的广告效果指标表"`
|
||||
*StorewideReportDetailItem
|
||||
}
|
||||
|
||||
// CreateStorewideReportDetailRes 创建广告效果指标表响应
|
||||
type CreateStorewideReportDetailRes struct {
|
||||
Id int64 `json:"id" dc:"广告效果指标 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReportDetailReq 批量创建广告效果指标表请求
|
||||
type BatchCreateStorewideReportDetailReq struct {
|
||||
g.Meta `path:"/batchCreateStorewideReportDetail" method:"post" tags:"广告效果指标" summary:"批量创建广告效果指标表" dc:"批量创建广告效果指标表"`
|
||||
Items []*StorewideReportDetailItem `json:"items" v:"required" dc:"广告效果指标列表"`
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReportDetailRes 批量创建广告效果指标表响应
|
||||
type BatchCreateStorewideReportDetailRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// StorewideReportDetailItem 广告效果指标表项
|
||||
type StorewideReportDetailItem struct {
|
||||
// 订单金额相关字段
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
|
||||
// 素材类型字段
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
|
||||
// 直播相关字段
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
|
||||
// 图片相关字段
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
|
||||
// 封面相关字段
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
|
||||
// 商品卡点击相关字段
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
|
||||
// 净成交相关字段
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
|
||||
// 视频相关字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
|
||||
// 行为相关字段
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
|
||||
// 商品相关字段
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
|
||||
// 行为率字段
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
|
||||
// 当日 GMV 相关字段
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
|
||||
// 当日订单相关字段
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
|
||||
// 智能优惠券相关字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
|
||||
// 直播观看相关字段
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
|
||||
// 商品入口点击相关字段
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
|
||||
// 曝光相关字段
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
|
||||
// 报告日期字段
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
|
||||
// 广告计划相关字段
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
|
||||
// 广告单元相关字段
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
|
||||
// 广告创意相关字段
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
}
|
||||
|
||||
// ListStorewideReportDetailReq 获取广告效果指标表列表请求
|
||||
type ListStorewideReportDetailReq struct {
|
||||
g.Meta `path:"/listStorewideReportDetail" method:"get" tags:"广告效果指标" summary:"获取广告效果指标表列表" dc:"分页查询广告效果指标表列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListStorewideReportDetailRes 获取广告效果指标表列表响应
|
||||
type ListStorewideReportDetailRes struct {
|
||||
List []*StorewideReportDetailItem `json:"list" dc:"广告效果指标列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateStorewideReportSumReq 创建广告效果指标表请求
|
||||
type CreateStorewideReportSumReq struct {
|
||||
g.Meta `path:"/createStorewideReportSum" method:"post" tags:"广告效果指标" summary:"创建广告效果指标表" dc:"创建新的广告效果指标表"`
|
||||
*StorewideReportSumItem
|
||||
}
|
||||
|
||||
// CreateStorewideReportSumRes 创建广告效果指标表响应
|
||||
type CreateStorewideReportSumRes struct {
|
||||
Id int64 `json:"id" dc:"广告效果指标 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReportSumReq 批量创建广告效果指标表请求
|
||||
type BatchCreateStorewideReportSumReq struct {
|
||||
g.Meta `path:"/batchCreateStorewideReportSum" method:"post" tags:"广告效果指标" summary:"批量创建广告效果指标表" dc:"批量创建广告效果指标表"`
|
||||
Items []*StorewideReportSumItem `json:"items" v:"required" dc:"广告效果指标列表"`
|
||||
}
|
||||
|
||||
// BatchCreateStorewideReportSumRes 批量创建广告效果指标表响应
|
||||
type BatchCreateStorewideReportSumRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// StorewideReportSumItem 广告效果指标表项
|
||||
type StorewideReportSumItem struct {
|
||||
// 新增字段
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
|
||||
// 原有字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
}
|
||||
|
||||
// ListStorewideReportSumReq 获取广告效果指标表列表请求
|
||||
type ListStorewideReportSumReq struct {
|
||||
g.Meta `path:"/listStorewideReportSum" method:"get" tags:"广告效果指标" summary:"获取广告效果指标表列表" dc:"分页查询广告效果指标表列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListStorewideReportSumRes 获取广告效果指标表列表响应
|
||||
type ListStorewideReportSumRes struct {
|
||||
List []*StorewideReportSumItem `json:"list" dc:"广告效果指标列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
package copydata
|
||||
|
||||
// CreateSyncTaskLogReq 创建同步任务日志请求
|
||||
type CreateSyncTaskLogReq struct {
|
||||
TaskID string `json:"taskId"`
|
||||
TaskType string `json:"taskType"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
InterfaceCode string `json:"interfaceCode"`
|
||||
AdvertiserID int64 `json:"advertiserId"`
|
||||
StartTime interface{} `json:"startTime"`
|
||||
EndTime interface{} `json:"endTime"`
|
||||
Status string `json:"status"`
|
||||
MaxRetry int `json:"maxRetry"`
|
||||
PageInfo interface{} `json:"pageInfo,omitempty"`
|
||||
RequestParams interface{} `json:"requestParams,omitempty"`
|
||||
}
|
||||
|
||||
// UpdateSyncTaskLogReq 更新同步任务日志请求
|
||||
type UpdateSyncTaskLogReq struct {
|
||||
ID int64 `json:"id"`
|
||||
Status string `json:"status,omitempty"`
|
||||
RetryCount *int `json:"retryCount,omitempty"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
ErrorCode string `json:"errorCode,omitempty"`
|
||||
ResultSummary interface{} `json:"resultSummary,omitempty"`
|
||||
NextRetryTime interface{} `json:"nextRetryTime,omitempty"`
|
||||
CompletedAt interface{} `json:"completedAt,omitempty"`
|
||||
DurationMs *int64 `json:"durationMs,omitempty"`
|
||||
}
|
||||
|
||||
// QueryFailedTasksReq 查询失败任务请求
|
||||
type QueryFailedTasksReq struct {
|
||||
Status []string `json:"status,omitempty"` // 查询的状态列表
|
||||
TaskType string `json:"taskType,omitempty"` // 任务类型
|
||||
MaxRetries *int `json:"maxRetries,omitempty"` // 最大重试次数过滤
|
||||
Limit int `json:"limit,omitempty"` // 限制数量
|
||||
}
|
||||
|
||||
// SyncTaskLogItem 同步任务日志项
|
||||
type SyncTaskLogItem struct {
|
||||
Id int64 `json:"id"`
|
||||
TaskID string `json:"taskId"`
|
||||
TaskType string `json:"taskType"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
InterfaceCode string `json:"interfaceCode"`
|
||||
AdvertiserID int64 `json:"advertiserId"`
|
||||
StartTime interface{} `json:"startTime"`
|
||||
EndTime interface{} `json:"endTime"`
|
||||
Status string `json:"status"`
|
||||
RetryCount int `json:"retryCount"`
|
||||
MaxRetry int `json:"maxRetry"`
|
||||
ErrorMessage string `json:"errorMessage,omitempty"`
|
||||
ErrorCode string `json:"errorCode,omitempty"`
|
||||
ResultSummary interface{} `json:"resultSummary,omitempty"`
|
||||
PageInfo interface{} `json:"pageInfo,omitempty"`
|
||||
RequestParams interface{} `json:"requestParams,omitempty"`
|
||||
NextRetryTime interface{} `json:"nextRetryTime,omitempty"`
|
||||
CompletedAt interface{} `json:"completedAt,omitempty"`
|
||||
DurationMs int64 `json:"durationMs"`
|
||||
CreatedAt interface{} `json:"createdAt"`
|
||||
UpdatedAt interface{} `json:"updatedAt"`
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateTaskReportReq 创建调控任务数据请求
|
||||
type CreateTaskReportReq struct {
|
||||
g.Meta `path:"/createTaskReport" method:"post" tags:"调控任务" summary:"创建调控任务数据" dc:"创建新的调控任务数据"`
|
||||
*TaskReportItem
|
||||
}
|
||||
|
||||
// CreateTaskReportRes 创建调控任务数据响应
|
||||
type CreateTaskReportRes struct {
|
||||
Id int64 `json:"id" dc:"调控任务数据 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateTaskReportReq 批量创建调控任务数据请求
|
||||
type BatchCreateTaskReportReq struct {
|
||||
g.Meta `path:"/batchCreateTaskReport" method:"post" tags:"调控任务" summary:"批量创建调控任务数据" dc:"批量创建调控任务数据"`
|
||||
Items []*TaskReportItem `json:"items" v:"required" dc:"调控任务数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateTaskReportRes 批量创建调控任务数据响应
|
||||
type BatchCreateTaskReportRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// TaskReportItem 调控任务数据项
|
||||
type TaskReportItem struct {
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
|
||||
// 视频信息字段
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
|
||||
// 核心指标字段
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
|
||||
// 粉丝 GMV 字段
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉 30 日 GMV"`
|
||||
|
||||
// 粉丝 ROI 字段
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉 30 日 ROI"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
|
||||
// 时间字段
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间(格式:YYYY-MM-DD)"`
|
||||
}
|
||||
|
||||
// ListTaskReportReq 获取调控任务数据列表请求
|
||||
type ListTaskReportReq struct {
|
||||
g.Meta `path:"/listTaskReport" method:"get" tags:"调控任务" summary:"获取调控任务数据列表" dc:"分页查询调控任务数据列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索视频名称等)"`
|
||||
}
|
||||
|
||||
// ListTaskReportRes 获取调控任务数据列表响应
|
||||
type ListTaskReportRes struct {
|
||||
List []*TaskReportItem `json:"list" dc:"调控任务数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateUnitReportDetailReq 创建广告效果指标详情请求
|
||||
type CreateUnitReportDetailReq struct {
|
||||
g.Meta `path:"/createUnitReportDetail" method:"post" tags:"广告效果指标详情" summary:"创建广告效果指标详情" dc:"创建新的广告效果指标详情"`
|
||||
*UnitReportDetailItem
|
||||
}
|
||||
|
||||
// CreateUnitReportDetailRes 创建广告效果指标详情响应
|
||||
type CreateUnitReportDetailRes struct {
|
||||
Id int64 `json:"id" dc:"广告数据 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateUnitReportDetailReq 批量创建广告效果指标详情请求
|
||||
type BatchCreateUnitReportDetailReq struct {
|
||||
g.Meta `path:"/batchCreateUnitReportDetail" method:"post" tags:"广告效果指标详情" summary:"批量创建广告效果指标详情" dc:"批量创建广告效果指标详情"`
|
||||
Items []*UnitReportDetailItem `json:"items" v:"required" dc:"广告数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateUnitReportDetailRes 批量创建广告效果指标详情响应
|
||||
type BatchCreateUnitReportDetailRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// UnitReportDetailItem 广告效果指标详情项
|
||||
type UnitReportDetailItem struct {
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间,"0-500", "501-1000", "1001-1500", "1501-2000", "2001-2500", "2501-3000", "3001-3500", "3501-4000", "4001-4500", "4501-5000", "5001-5500", "5501+", "未知""`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"0-17", "18-23", "24-30", "31-40", "41-49", "50+","未知""`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"男,女,未知三种类型"`
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品 3 秒播放率"`
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接 ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接 GMV"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30 日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7 日累计 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15 日累计 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30 日累计 ROI"`
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30 日累计订单数"`
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30 日涨粉留存率"`
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30 日累计店铺新客"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3 日累计订单数"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3 日累计 ROI"`
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30 日新增粉丝人均销售额"`
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉 30 日 GMV"`
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后 30 日新客 GMV"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后 30 日新客成交订单量"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后 30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后 30 日新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
// ListUnitReportDetailReq 获取广告效果指标详情列表请求
|
||||
type ListUnitReportDetailReq struct {
|
||||
g.Meta `path:"/listUnitReportDetail" method:"get" tags:"广告效果指标详情" summary:"获取广告效果指标详情列表" dc:"分页查询广告效果指标详情列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListUnitReportDetailRes 获取广告效果指标详情列表响应
|
||||
type ListUnitReportDetailRes struct {
|
||||
List []*UnitReportDetailItem `json:"list" dc:"广告数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import (
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateUnitReportSumReq 创建广告效果指标请求
|
||||
type CreateUnitReportSumReq struct {
|
||||
g.Meta `path:"/createUnitReportSum" method:"post" tags:"广告效果指标" summary:"创建广告效果指标" dc:"创建新的广告效果指标"`
|
||||
*UnitReportSumItem
|
||||
}
|
||||
|
||||
// CreateUnitReportSumRes 创建广告效果指标响应
|
||||
type CreateUnitReportSumRes struct {
|
||||
Id int64 `json:"id" dc:"广告数据 ID"`
|
||||
}
|
||||
|
||||
// BatchCreateUnitReportSumReq 批量创建广告效果指标请求
|
||||
type BatchCreateUnitReportSumReq struct {
|
||||
g.Meta `path:"/batchCreateUnitReportSum" method:"post" tags:"广告效果指标" summary:"批量创建广告效果指标" dc:"批量创建广告效果指标"`
|
||||
Items []*UnitReportSumItem `json:"items" v:"required" dc:"广告数据列表"`
|
||||
}
|
||||
|
||||
// BatchCreateUnitReportSumRes 批量创建广告效果指标响应
|
||||
type BatchCreateUnitReportSumRes struct {
|
||||
SuccessCount int64 `json:"successCount" dc:"成功数量"`
|
||||
FailCount int64 `json:"failCount" dc:"失败数量"`
|
||||
FailedItems []int64 `json:"failedItems" dc:"失败项索引"`
|
||||
}
|
||||
|
||||
// UnitReportSumItem 广告效果指标项
|
||||
type UnitReportSumItem struct {
|
||||
T0OrderPaymentAmt *float64 `json:"t0OrderPaymentAmt" dc:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `json:"creativeMaterialType" dc:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
LiveName string `json:"liveName" dc:"直播间名称"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
PicUrl string `json:"picUrl" dc:"图片 URL"`
|
||||
PicName string `json:"picName" dc:"图片名称"`
|
||||
PicId string `json:"picId" dc:"图片 Id"`
|
||||
CoverUrl string `json:"coverUrl" dc:"封面 URL"`
|
||||
CoverId *int64 `json:"coverId" dc:"封面 Id"`
|
||||
ItemOrderConversionRatio *float64 `json:"itemOrderConversionRatio" dc:"转化率"`
|
||||
ItemCardClickRatio *float64 `json:"itemCardClickRatio" dc:"点击率"`
|
||||
ItemCardClkCnt *int64 `json:"itemCardClkCnt" dc:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `json:"livePlayCntCost" dc:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `json:"adMerchantFollowCost" dc:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `json:"adMerchantFollow" dc:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `json:"netT0OrderCnt" dc:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `json:"netT0Roi" dc:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `json:"netT0Gmv" dc:"净成交 GMV"`
|
||||
PhotoName string `json:"photoName" dc:"视频名称"`
|
||||
PhotoIdStr string `json:"photoIdStr" dc:"视频 id"`
|
||||
PhotoId string `json:"photoId" dc:"视频 id"`
|
||||
ModPriceSegment string `json:"modPriceSegment" dc:"设备价格区间,"0-500", "501-1000", "1001-1500", "1501-2000", "2001-2500", "2501-3000", "3001-3500", "3501-4000", "4001-4500", "4501-5000", "5001-5500", "5501+", "未知""`
|
||||
AgeSegment string `json:"ageSegment" dc:"年龄段"0-17", "18-23", "24-30", "31-40", "41-49", "50+","未知""`
|
||||
Province string `json:"province" dc:"省份名称"`
|
||||
Gender string `json:"gender" dc:"男,女,未知三种类型"`
|
||||
AdPhotoPlayedFiveRatio *float64 `json:"adPhotoPlayedFiveRatio" dc:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `json:"adPhotoPlayedThreeRatio" dc:"作品 3 秒播放率"`
|
||||
OrderSubmitRoi *float64 `json:"orderSubmitRoi" dc:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `json:"orderSubmitAmt" dc:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `json:"eventOrderSubmitCost" dc:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `json:"eventOrderSubmit" dc:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `json:"eventOrderPaidRoi" dc:"订单支付率"`
|
||||
EventAppInvoked *int64 `json:"eventAppInvoked" dc:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `json:"eventAddShoppingCart" dc:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `json:"conversionNumCost" dc:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `json:"adEffectivePlayNum" dc:"有效播放数"`
|
||||
AdItemClick *int64 `json:"adItemClick" dc:"行为数"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
CostTotal *float64 `json:"costTotal" dc:"花费"`
|
||||
AdShow *int64 `json:"adShow" dc:"曝光数"`
|
||||
AdShow1kCost *float64 `json:"adShow1kCost" dc:"平均千次广告曝光花费"`
|
||||
Impression *int64 `json:"impression" dc:"封面曝光数"`
|
||||
PhotoClick *int64 `json:"photoClick" dc:"封面点击数"`
|
||||
PhotoClickRatio *float64 `json:"photoClickRatio" dc:"封面点击率"`
|
||||
Click *int64 `json:"click" dc:"素材曝光数"`
|
||||
ActionbarClick *int64 `json:"actionbarClick" dc:"行为数"`
|
||||
ActionbarClickCost *float64 `json:"actionbarClickCost" dc:"行为成本"`
|
||||
EspClickRatio *float64 `json:"espClickRatio" dc:"行为率"`
|
||||
ActionRatio *float64 `json:"actionRatio" dc:"素材点击率"`
|
||||
AdItemClickCount *int64 `json:"adItemClickCount" dc:"预约组件点击数"`
|
||||
EspLivePlayedSeconds *int64 `json:"espLivePlayedSeconds" dc:"直播平均观看时长"`
|
||||
PlayedThreeSeconds *int64 `json:"playedThreeSeconds" dc:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `json:"play3sRatio" dc:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `json:"playedFiveSeconds" dc:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `json:"play5sRatio" dc:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `json:"playedEnd" dc:"作品完播数"`
|
||||
PlayEndRatio *float64 `json:"playEndRatio" dc:"作品完播率"`
|
||||
Share *int64 `json:"share" dc:"作品分享数"`
|
||||
Comment *int64 `json:"comment" dc:"作品评论数"`
|
||||
Likes *int64 `json:"likes" dc:"作品点赞数"`
|
||||
Report *int64 `json:"report" dc:"作品举报数"`
|
||||
Block *int64 `json:"block" dc:"作品拉黑数"`
|
||||
ItemNegative *int64 `json:"itemNegative" dc:"详情页减少此类作品数"`
|
||||
LiveShare *int64 `json:"liveShare" dc:"直播送礼数"`
|
||||
LiveComment *int64 `json:"liveComment" dc:"直播评论数"`
|
||||
LiveReward *int64 `json:"liveReward" dc:"直播送礼数"`
|
||||
EffectivePlayCount *int64 `json:"effectivePlayCount" dc:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `json:"effectivePlayRatio" dc:"有效播放率"`
|
||||
ConversionNum *int64 `json:"conversionNum" dc:"转化数"`
|
||||
ConversionCostEsp *float64 `json:"conversionCostEsp" dc:"转化成本"`
|
||||
Roi *float64 `json:"roi" dc:"直接 ROI"`
|
||||
Gmv *float64 `json:"gmv" dc:"直接 GMV"`
|
||||
T0Gmv *float64 `json:"t0Gmv" dc:"当日累计 GMV"`
|
||||
T1Gmv *float64 `json:"t1Gmv" dc:"次日累计 GMV"`
|
||||
T7Gmv *float64 `json:"t7Gmv" dc:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `json:"t15Gmv" dc:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `json:"t30Gmv" dc:"30 日累计 GMV"`
|
||||
T0Roi *float64 `json:"t0Roi" dc:"当日累计 ROI"`
|
||||
T1Roi *float64 `json:"t1Roi" dc:"次日累计 ROI"`
|
||||
T7Roi *float64 `json:"t7Roi" dc:"7 日累计 ROI"`
|
||||
T15Roi *float64 `json:"t15Roi" dc:"15 日累计 ROI"`
|
||||
T30Roi *float64 `json:"t30Roi" dc:"30 日累计 ROI"`
|
||||
PaiedOrder *int64 `json:"paiedOrder" dc:"直接订单数"`
|
||||
OrderRatio *float64 `json:"orderRatio" dc:"直接下单率"`
|
||||
T0OrderCnt *int64 `json:"t0OrderCnt" dc:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `json:"t0OrderCntCost" dc:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `json:"t0OrderCntRatio" dc:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `json:"t1OrderCnt" dc:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `json:"t7OrderCnt" dc:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `json:"t15OrderCnt" dc:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `json:"t30OrderCnt" dc:"30 日累计订单数"`
|
||||
MerchantRecoFans *int64 `json:"merchantRecoFans" dc:"涨粉数"`
|
||||
T1Retention *float64 `json:"t1Retention" dc:"次日涨粉留存数"`
|
||||
T7Retention *float64 `json:"t7Retention" dc:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `json:"t15Retention" dc:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `json:"t30Retention" dc:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `json:"t1RetentionRatio" dc:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `json:"t7RetentionRatio" dc:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `json:"t15RetentionRatio" dc:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `json:"t30RetentionRatio" dc:"30 日涨粉留存率"`
|
||||
ReservationSuccess *int64 `json:"reservationSuccess" dc:"直播预约成功数"`
|
||||
ReservationCost *float64 `json:"reservationCost" dc:"直播预约成功成本"`
|
||||
StandardLivePlayedStarted *int64 `json:"standardLivePlayedStarted" dc:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `json:"adLivePlayCnt" dc:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `json:"adLivePlayCntCost" dc:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `json:"liveAudienceCost" dc:"直播观看成本"`
|
||||
LiveEventGoodsView *int64 `json:"liveEventGoodsView" dc:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `json:"goodsClickRatio" dc:"直播间商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `json:"directAttrPlatNewBuyerCnt" dc:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `json:"t30AttrPlatTotalBuyerCnt" dc:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `json:"directAttrSellerNewBuyerCnt" dc:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `json:"t30AttrSellerTotalBuyerCnt" dc:"30 日累计店铺新客"`
|
||||
T3Gmv *float64 `json:"t3Gmv" dc:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `json:"t3OrderCnt" dc:"3 日累计订单数"`
|
||||
T3Roi *float64 `json:"t3Roi" dc:"3 日累计 ROI"`
|
||||
T7IndirectOrderAmt *float64 `json:"t7IndirectOrderAmt" dc:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `json:"t7IndirectOrderCnt" dc:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `json:"fansT0GmvPerFans" dc:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `json:"fansT3GmvPerFans" dc:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `json:"fansT7GmvPerFans" dc:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `json:"fansT15GmvPerFans" dc:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `json:"fansT30GmvPerFans" dc:"30 日新增粉丝人均销售额"`
|
||||
RecoFansCost *float64 `json:"recoFansCost" dc:"涨粉成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `json:"qcpxWhiteboxDirectOrderPaymentAmt" dc:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `json:"qcpxWhiteboxDirectOrderCnt" dc:"智能优惠券订单数"`
|
||||
FansT0Gmv *float64 `json:"fansT0Gmv" dc:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `json:"fansT1Gmv" dc:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `json:"fansT7Gmv" dc:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `json:"fansT15Gmv" dc:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `json:"fansT30Gmv" dc:"涨粉 30 日 GMV"`
|
||||
FansT0Roi *float64 `json:"fansT0Roi" dc:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `json:"fansT1Roi" dc:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `json:"fansT7Roi" dc:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `json:"fansT15Roi" dc:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `json:"fansT30Roi" dc:"涨粉 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `json:"t0ShopNewBuyerOrderPaymentAmt" dc:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `json:"t1ShopNewBuyerOrderPaymentAmt" dc:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `json:"t3ShopNewBuyerOrderPaymentAmt" dc:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `json:"t7ShopNewBuyerOrderPaymentAmt" dc:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `json:"t15ShopNewBuyerOrderPaymentAmt" dc:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `json:"t30ShopNewBuyerOrderPaymentAmt" dc:"投后 30 日新客 GMV"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `json:"t0ShopNewBuyerOrderCnt" dc:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `json:"t1ShopNewBuyerOrderCnt" dc:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `json:"t3ShopNewBuyerOrderCnt" dc:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `json:"t7ShopNewBuyerOrderCnt" dc:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `json:"t15ShopNewBuyerOrderCnt" dc:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `json:"t30ShopNewBuyerOrderCnt" dc:"投后 30 日新客成交订单量"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `json:"t1NewBuyerRepurchaseRatio" dc:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `json:"t3NewBuyerRepurchaseRatio" dc:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `json:"t7NewBuyerRepurchaseRatio" dc:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `json:"t15NewBuyerRepurchaseRatio" dc:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `json:"t30NewBuyerRepurchaseRatio" dc:"投后 30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `json:"t0ShopNewBuyerRoi" dc:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `json:"t1ShopNewBuyerRoi" dc:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `json:"t3ShopNewBuyerRoi" dc:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `json:"t7ShopNewBuyerRoi" dc:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `json:"t15ShopNewBuyerRoi" dc:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `json:"t30ShopNewBuyerRoi" dc:"投后 30 日新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `json:"createCardOrderCnt" dc:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `json:"forwardTsCreateCardOrderCnt" dc:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `json:"createCardOrderCost" dc:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `json:"forwardTsCreateCardOrderCost" dc:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `json:"activateCardOrderCnt" dc:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `json:"forwardTsActivateCardOrderCnt" dc:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `json:"activateCardOrderCost" dc:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `json:"forwardTsActivateCardOrderCost" dc:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `json:"createCardOrderRatio" dc:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `json:"forwardTsCreateCardOrderRatio" dc:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `json:"activateCardOrderCntRatio" dc:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `json:"forwardTsActivateCardOrderRatio" dc:"电话卡激活率(计费时间)"`
|
||||
LivePlayCnt *int64 `json:"livePlayCnt" dc:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `json:"itemEntranceClkCnt" dc:"小黄车点击数"`
|
||||
ShowCnt *int64 `json:"showCnt" dc:"全站曝光"`
|
||||
ReportDateStr string `json:"reportDateStr" v:"required" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
CampaignName string `json:"campaignName" dc:"计划名称"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
UnitName string `json:"unitName" dc:"单元名称"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
CreativeName string `json:"creativeName" dc:"创意名称"`
|
||||
CidActualRoiAfterSubsidy *float64 `json:"cidActualRoiAfterSubsidy" dc:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `json:"cidCouponAmount" dc:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `json:"cidCouponCallbackPaidRefundAmount" dc:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `json:"cidVoucherCost" dc:"券成本"`
|
||||
}
|
||||
|
||||
// ListUnitReportSumReq 获取广告效果指标列表请求
|
||||
type ListUnitReportSumReq struct {
|
||||
g.Meta `path:"/listUnitReportSum" method:"get" tags:"广告效果指标" summary:"获取广告效果指标列表" dc:"分页查询广告效果指标列表"`
|
||||
*beans.Page
|
||||
ReportDateStr string `json:"reportDateStr" dc:"时间"`
|
||||
CampaignId *int64 `json:"campaignId" dc:"计划 ID"`
|
||||
UnitId *int64 `json:"unitId" dc:"单元 ID"`
|
||||
CreativeId *int64 `json:"creativeId" dc:"创意 ID"`
|
||||
AuthorId string `json:"authorId" dc:"直播用户快手 Id"`
|
||||
MerchantProductId string `json:"merchantProductId" dc:"商品 ID"`
|
||||
Keyword string `json:"keyword" dc:"关键字(搜索直播间名称、视频名称等)"`
|
||||
}
|
||||
|
||||
// ListUnitReportSumRes 获取广告效果指标列表响应
|
||||
type ListUnitReportSumRes struct {
|
||||
List []*UnitReportSumItem `json:"list" dc:"广告数据列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package api_feature
|
||||
package dict
|
||||
|
||||
import (
|
||||
"cid/consts/api-feature"
|
||||
entity "cid/model/entity/dict"
|
||||
"dataengine/consts/api-feature"
|
||||
entity "dataengine/model/entity/dict"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
@@ -27,10 +27,7 @@ type CreateDatasourcePlatformReq struct {
|
||||
RequestTimeoutMs int `json:"requestTimeoutMs" dc:"请求超时时间(毫秒)" d:"30000"`
|
||||
MaxRetries int `json:"maxRetries" dc:"最大重试次数" d:"3"`
|
||||
RetryDelayMs int `json:"retryDelayMs" dc:"重试延迟(毫秒)" d:"1000"`
|
||||
CreatedBy string `json:"createdBy" v:"required" dc:"创建人"`
|
||||
CreatedAt string `json:"createdAt" v:"required" dc:"创建时间"`
|
||||
UpdatedBy string `json:"updatedBy" v:"required" dc:"修改人"`
|
||||
UpdatedAt string `json:"updatedAt" v:"required" dc:"修改人"`
|
||||
AuthConfig map[string]interface{} `json:"authConfig" dc:"自定义认证配置(JSON),支持各平台特有认证方式"`
|
||||
}
|
||||
|
||||
// CreateDatasourcePlatformRes 创建数据源平台响应
|
||||
@@ -57,6 +54,7 @@ type ListDatasourcePlatformRes struct {
|
||||
|
||||
type DatasourcePlatformItem struct {
|
||||
Id int64 `json:"id,string"`
|
||||
TenantId uint64 `json:"tenantId"`
|
||||
PlatformCode string `json:"platformCode"`
|
||||
PlatformName string `json:"platformName"`
|
||||
Description string `json:"description"`
|
||||
@@ -108,9 +106,7 @@ type UpdateDatasourcePlatformReq struct {
|
||||
RequestTimeoutMs int `json:"requestTimeoutMs" dc:"请求超时时间(毫秒)"`
|
||||
MaxRetries int `json:"maxRetries" dc:"最大重试次数"`
|
||||
RetryDelayMs int `json:"retryDelayMs" dc:"重试延迟(毫秒)"`
|
||||
UpdatedBy string `json:"updatedBy" v:"required" dc:"更新人"`
|
||||
UpdatedAt string `json:"updatedAt" v:"required" dc:"更新时间"`
|
||||
Version string `json:"version" v:"required" dc:"版本"`
|
||||
AuthConfig map[string]interface{} `json:"authConfig" dc:"自定义认证配置(JSON)"`
|
||||
}
|
||||
|
||||
// DeleteDatasourcePlatformReq 删除数据源平台请求
|
||||
@@ -121,10 +117,9 @@ type DeleteDatasourcePlatformReq struct {
|
||||
|
||||
// UpdateDatasourcePlatformStatusReq 更新数据源平台状态请求
|
||||
type UpdateDatasourcePlatformStatusReq struct {
|
||||
g.Meta `path:"/updateDatasourcePlatformStatus" method:"put" tags:"数据源平台管理" summary:"更新数据源平台状态" dc:"更新数据源平台状态"`
|
||||
Id int64 `json:"id" v:"required" dc:"平台ID"`
|
||||
Status api_feature.PlatformStatus `json:"status" v:"required|in:ACTIVE,INACTIVE" dc:"状态:ACTIVE启用/INACTIVE停用"`
|
||||
UpdatedBy string `json:"updatedBy" v:"required" dc:"更新人"`
|
||||
g.Meta `path:"/updateDatasourcePlatformStatus" method:"put" tags:"数据源平台管理" summary:"更新数据源平台状态" dc:"更新数据源平台状态"`
|
||||
Id int64 `json:"id" v:"required" dc:"平台ID"`
|
||||
Status api_feature.PlatformStatus `json:"status" v:"required|in:ACTIVE,INACTIVE" dc:"状态:ACTIVE启用/INACTIVE停用"`
|
||||
}
|
||||
|
||||
// 以下是几个额外的实用API接口
|
||||
|
||||
@@ -1,221 +0,0 @@
|
||||
package api_feature
|
||||
|
||||
import (
|
||||
entity "cid/model/entity/dict"
|
||||
"time"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateFieldMappingConfigReq 创建字段映射配置请求
|
||||
type CreateFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs" method:"post" tags:"字段映射配置" summary:"创建字段映射配置" dc:"创建新的字段映射配置"`
|
||||
ConfigName string `json:"configName" v:"required#配置名称不能为空" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" v:"required#厂商名称不能为空" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" v:"required#接口名称不能为空" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
SourceField string `json:"sourceField" v:"required#源字段不能为空" dc:"源字段名"`
|
||||
SourceFieldType string `json:"sourceFieldType" dc:"源字段数据类型"`
|
||||
SourceFieldDesc string `json:"sourceFieldDesc" dc:"源字段描述"`
|
||||
TargetField string `json:"targetField" v:"required#目标字段不能为空" dc:"目标字段名"`
|
||||
TargetFieldType string `json:"targetFieldType" v:"required#目标字段类型不能为空" dc:"目标数据类型"`
|
||||
TargetFieldDesc string `json:"targetFieldDesc" dc:"字段描述"`
|
||||
TransformType string `json:"transformType" dc:"转换类型" d:"direct"`
|
||||
TransformParams map[string]interface{} `json:"transformParams" dc:"转换参数"`
|
||||
ValidationRules map[string]interface{} `json:"validationRules" dc:"验证规则"`
|
||||
DefaultValue string `json:"defaultValue" dc:"默认值"`
|
||||
IsRequired bool `json:"isRequired" dc:"是否必填" d:"false"`
|
||||
IsActive bool `json:"isActive" dc:"是否启用" d:"true"`
|
||||
Priority int `json:"priority" dc:"优先级" d:"0"`
|
||||
BusinessDomain string `json:"businessDomain" dc:"业务域"`
|
||||
FieldGroup string `json:"fieldGroup" dc:"字段分组"`
|
||||
ConfigVersion int `json:"configVersion" dc:"配置版本号" d:"1"`
|
||||
EffectiveDate *time.Time `json:"effectiveDate" dc:"生效时间"`
|
||||
ExpiryDate *time.Time `json:"expiryDate" dc:"失效时间"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
}
|
||||
|
||||
// CreateFieldMappingConfigRes 创建字段映射配置响应
|
||||
type CreateFieldMappingConfigRes struct {
|
||||
Id int64 `json:"id" dc:"配置ID"`
|
||||
}
|
||||
|
||||
// ListFieldMappingConfigReq 获取字段映射配置列表请求
|
||||
type ListFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs" method:"get" tags:"字段映射配置" summary:"获取字段映射配置列表" dc:"分页查询字段映射配置列表"`
|
||||
*beans.Page
|
||||
ConfigName string `json:"configName" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
SourceField string `json:"sourceField" dc:"源字段名"`
|
||||
TargetField string `json:"targetField" dc:"目标字段名"`
|
||||
TransformType string `json:"transformType" dc:"转换类型"`
|
||||
IsActive *bool `json:"isActive" dc:"是否启用"`
|
||||
BusinessDomain string `json:"businessDomain" dc:"业务域"`
|
||||
FieldGroup string `json:"fieldGroup" dc:"字段分组"`
|
||||
Keyword string `json:"keyword" dc:"关键字搜索"`
|
||||
}
|
||||
|
||||
// ListFieldMappingConfigRes 获取字段映射配置列表响应
|
||||
type ListFieldMappingConfigRes struct {
|
||||
List []FieldMappingConfigItem `json:"list" dc:"配置列表"`
|
||||
Total int `json:"total" dc:"总数"`
|
||||
}
|
||||
|
||||
// FieldMappingConfigItem 字段映射配置列表项
|
||||
type FieldMappingConfigItem struct {
|
||||
Id int64 `json:"id,string" dc:"配置ID"`
|
||||
ConfigName string `json:"configName" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
SourceField string `json:"sourceField" dc:"源字段名"`
|
||||
TargetField string `json:"targetField" dc:"目标字段名"`
|
||||
TargetFieldType string `json:"targetFieldType" dc:"目标数据类型"`
|
||||
TransformType string `json:"transformType" dc:"转换类型"`
|
||||
TransformTypeName string `json:"transformTypeName" dc:"转换类型名称"`
|
||||
IsActive bool `json:"isActive" dc:"是否启用"`
|
||||
Priority int `json:"priority" dc:"优先级"`
|
||||
BusinessDomain string `json:"businessDomain" dc:"业务域"`
|
||||
BusinessDomainName string `json:"businessDomainName" dc:"业务域名称"`
|
||||
FieldGroup string `json:"fieldGroup" dc:"字段分组"`
|
||||
ConfigVersion int `json:"configVersion" dc:"配置版本号"`
|
||||
CreatedBy string `json:"createdBy" dc:"创建人"`
|
||||
CreatedTime time.Time `json:"createdTime" dc:"创建时间"`
|
||||
UpdatedBy string `json:"updatedBy" dc:"更新人"`
|
||||
UpdatedTime time.Time `json:"updatedTime" dc:"更新时间"`
|
||||
}
|
||||
|
||||
// GetFieldMappingConfigReq 获取字段映射配置详情请求
|
||||
type GetFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/{id}" method:"get" tags:"字段映射配置" summary:"获取字段映射配置详情" dc:"获取字段映射配置详情"`
|
||||
Id int64 `json:"id" v:"required#配置ID不能为空" dc:"配置ID"`
|
||||
}
|
||||
|
||||
// GetFieldMappingConfigRes 获取字段映射配置详情响应
|
||||
type GetFieldMappingConfigRes struct {
|
||||
*entity.FieldMappingConfig
|
||||
TransformTypeName string `json:"transformTypeName,omitempty" dc:"转换类型名称"`
|
||||
BusinessDomainName string `json:"businessDomainName,omitempty" dc:"业务域名称"`
|
||||
}
|
||||
|
||||
// UpdateFieldMappingConfigReq 更新字段映射配置请求
|
||||
type UpdateFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/{id}" method:"put" tags:"字段映射配置" summary:"更新字段映射配置" dc:"更新字段映射配置信息"`
|
||||
Id int64 `json:"id" v:"required#配置ID不能为空" dc:"配置ID"`
|
||||
ConfigName string `json:"configName" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
SourceField string `json:"sourceField" dc:"源字段名"`
|
||||
SourceFieldType string `json:"sourceFieldType" dc:"源字段数据类型"`
|
||||
SourceFieldDesc string `json:"sourceFieldDesc" dc:"源字段描述"`
|
||||
TargetField string `json:"targetField" dc:"目标字段名"`
|
||||
TargetFieldType string `json:"targetFieldType" dc:"目标数据类型"`
|
||||
TargetFieldDesc string `json:"targetFieldDesc" dc:"字段描述"`
|
||||
TransformType string `json:"transformType" dc:"转换类型"`
|
||||
TransformParams map[string]interface{} `json:"transformParams" dc:"转换参数"`
|
||||
ValidationRules map[string]interface{} `json:"validationRules" dc:"验证规则"`
|
||||
DefaultValue string `json:"defaultValue" dc:"默认值"`
|
||||
IsRequired bool `json:"isRequired" dc:"是否必填"`
|
||||
IsActive bool `json:"isActive" dc:"是否启用"`
|
||||
Priority int `json:"priority" dc:"优先级"`
|
||||
BusinessDomain string `json:"businessDomain" dc:"业务域"`
|
||||
FieldGroup string `json:"fieldGroup" dc:"字段分组"`
|
||||
ConfigVersion int `json:"configVersion" dc:"配置版本号"`
|
||||
EffectiveDate *time.Time `json:"effectiveDate" dc:"生效时间"`
|
||||
ExpiryDate *time.Time `json:"expiryDate" dc:"失效时间"`
|
||||
UpdatedBy string `json:"updatedBy" dc:"更新人"`
|
||||
}
|
||||
|
||||
// DeleteFieldMappingConfigReq 删除字段映射配置请求
|
||||
type DeleteFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/{id}" method:"delete" tags:"字段映射配置" summary:"删除字段映射配置" dc:"删除字段映射配置"`
|
||||
Id int64 `json:"id" v:"required#配置ID不能为空" dc:"配置ID"`
|
||||
}
|
||||
|
||||
// UpdateFieldMappingConfigStatusReq 更新字段映射配置状态请求
|
||||
type UpdateFieldMappingConfigStatusReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/{id}/status" method:"put" tags:"字段映射配置" summary:"更新字段映射配置状态" dc:"更新字段映射配置状态"`
|
||||
Id int64 `json:"id" v:"required#配置ID不能为空" dc:"配置ID"`
|
||||
IsActive bool `json:"isActive" v:"required#状态不能为空" dc:"是否启用:true启用/false停用"`
|
||||
}
|
||||
|
||||
// BatchCreateFieldMappingConfigReq 批量创建字段映射配置请求
|
||||
type BatchCreateFieldMappingConfigReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/batch" method:"post" tags:"字段映射配置" summary:"批量创建字段映射配置" dc:"批量创建字段映射配置"`
|
||||
Configs []BatchFieldMappingConfigItem `json:"configs" v:"required#配置列表不能为空" dc:"配置列表"`
|
||||
}
|
||||
|
||||
// BatchFieldMappingConfigItem 批量创建配置项
|
||||
type BatchFieldMappingConfigItem struct {
|
||||
ConfigName string `json:"configName" v:"required#配置名称不能为空" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" v:"required#厂商名称不能为空" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" v:"required#接口名称不能为空" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
SourceField string `json:"sourceField" v:"required#源字段不能为空" dc:"源字段名"`
|
||||
SourceFieldType string `json:"sourceFieldType" dc:"源字段数据类型"`
|
||||
SourceFieldDesc string `json:"sourceFieldDesc" dc:"源字段描述"`
|
||||
TargetField string `json:"targetField" v:"required#目标字段不能为空" dc:"目标字段名"`
|
||||
TargetFieldType string `json:"targetFieldType" v:"required#目标字段类型不能为空" dc:"目标数据类型"`
|
||||
TargetFieldDesc string `json:"targetFieldDesc" dc:"字段描述"`
|
||||
TransformType string `json:"transformType" dc:"转换类型" d:"direct"`
|
||||
TransformParams map[string]interface{} `json:"transformParams" dc:"转换参数"`
|
||||
ValidationRules map[string]interface{} `json:"validationRules" dc:"验证规则"`
|
||||
DefaultValue string `json:"defaultValue" dc:"默认值"`
|
||||
IsRequired bool `json:"isRequired" dc:"是否必填" d:"false"`
|
||||
IsActive bool `json:"isActive" dc:"是否启用" d:"true"`
|
||||
Priority int `json:"priority" dc:"优先级" d:"0"`
|
||||
BusinessDomain string `json:"businessDomain" dc:"业务域"`
|
||||
FieldGroup string `json:"fieldGroup" dc:"字段分组"`
|
||||
ConfigVersion int `json:"configVersion" dc:"配置版本号" d:"1"`
|
||||
}
|
||||
|
||||
// BatchCreateFieldMappingConfigRes 批量创建字段映射配置响应
|
||||
type BatchCreateFieldMappingConfigRes struct {
|
||||
SuccessCount int `json:"successCount" dc:"成功数量"`
|
||||
FailedCount int `json:"failedCount" dc:"失败数量"`
|
||||
FailedItems []BatchCreateFailedItem `json:"failedItems" dc:"失败项列表"`
|
||||
}
|
||||
|
||||
// BatchCreateFailedItem 批量创建失败项
|
||||
type BatchCreateFailedItem struct {
|
||||
Index int `json:"index" dc:"项索引"`
|
||||
ConfigName string `json:"configName" dc:"配置名称"`
|
||||
Error string `json:"error" dc:"错误信息"`
|
||||
}
|
||||
|
||||
// QueryFieldMappingByVendorApiReq 根据厂商和接口查询字段映射请求
|
||||
type QueryFieldMappingByVendorApiReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/query" method:"get" tags:"字段映射配置" summary:"根据厂商和接口查询字段映射" dc:"根据厂商和接口查询字段映射配置"`
|
||||
VendorName string `json:"vendorName" v:"required#厂商名称不能为空" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" v:"required#接口名称不能为空" dc:"接口名称"`
|
||||
ApiVersion string `json:"apiVersion" dc:"接口版本"`
|
||||
IsActive *bool `json:"isActive" dc:"是否启用" d:"true"`
|
||||
}
|
||||
|
||||
// QueryFieldMappingByVendorApiRes 根据厂商和接口查询字段映射响应
|
||||
type QueryFieldMappingByVendorApiRes struct {
|
||||
List []*entity.FieldMappingConfig `json:"list" dc:"字段映射配置列表"`
|
||||
}
|
||||
|
||||
// ValidateFieldMappingReq 验证字段映射配置请求
|
||||
type ValidateFieldMappingReq struct {
|
||||
g.Meta `path:"/field-mapping-configs/validate" method:"post" tags:"字段映射配置" summary:"验证字段映射配置" dc:"验证字段映射配置的有效性"`
|
||||
ConfigName string `json:"configName" v:"required#配置名称不能为空" dc:"配置名称"`
|
||||
VendorName string `json:"vendorName" v:"required#厂商名称不能为空" dc:"厂商名称"`
|
||||
ApiName string `json:"apiName" v:"required#接口名称不能为空" dc:"接口名称"`
|
||||
SourceField string `json:"sourceField" v:"required#源字段不能为空" dc:"源字段名"`
|
||||
TargetField string `json:"targetField" v:"required#目标字段不能为空" dc:"目标字段名"`
|
||||
TestValue interface{} `json:"testValue" dc:"测试值"`
|
||||
}
|
||||
|
||||
// ValidateFieldMappingRes 验证字段映射配置响应
|
||||
type ValidateFieldMappingRes struct {
|
||||
IsValid bool `json:"isValid" dc:"是否有效"`
|
||||
TransformedValue interface{} `json:"transformedValue" dc:"转换后的值"`
|
||||
Error string `json:"error" dc:"错误信息"`
|
||||
Warnings []string `json:"warnings" dc:"警告信息"`
|
||||
}
|
||||
@@ -1,26 +1,27 @@
|
||||
package api_feature
|
||||
package dict
|
||||
|
||||
import (
|
||||
"cid/consts/api-feature"
|
||||
entity "cid/model/entity/dict"
|
||||
"dataengine/consts/api-feature"
|
||||
entity "dataengine/model/entity/dict"
|
||||
|
||||
"gitea.com/red-future/common/beans"
|
||||
"gitea.redpowerfuture.com/red-future/common/beans"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// CreateApiInterfaceReq 创建接口请求
|
||||
type CreateApiInterfaceReq struct {
|
||||
g.Meta `path:"/createApiInterface" method:"post" tags:"接口管理" summary:"创建接口" dc:"创建新的数据接口"`
|
||||
PlatformId int64 `json:"platformId" v:"required" dc:"所属平台ID"`
|
||||
Name string `json:"name" v:"required" dc:"接口名称"`
|
||||
Code string `json:"code" v:"required" dc:"接口编码"`
|
||||
Url string `json:"url" v:"required" dc:"接口地址"`
|
||||
Method api_feature.ApiMethod `json:"method" v:"required" dc:"请求方法"`
|
||||
Status api_feature.PlatformStatus `json:"status" dc:"接口状态" d:"active"`
|
||||
AuthType string `json:"authType" dc:"认证类型"`
|
||||
RequestConfig map[string]interface{} `json:"requestConfig" dc:"请求配置"`
|
||||
ResponseConfig map[string]interface{} `json:"responseConfig" dc:"响应配置"`
|
||||
LimitConfig map[string]interface{} `json:"limitConfig" dc:"接口独立限流配置"`
|
||||
g.Meta `path:"/createApiInterface" method:"post" tags:"接口管理" summary:"创建接口" dc:"创建新的数据接口"`
|
||||
PlatformId int64 `json:"platformId" v:"required" dc:"所属平台ID"`
|
||||
Name string `json:"name" v:"required" dc:"接口名称"`
|
||||
Code string `json:"code" v:"required" dc:"接口编码"`
|
||||
Url string `json:"url" v:"required" dc:"接口地址"`
|
||||
Method api_feature.ApiMethod `json:"method" v:"required" dc:"请求方法"`
|
||||
Status api_feature.PlatformStatus `json:"status" dc:"接口状态" d:"active"`
|
||||
AuthType string `json:"authType" dc:"认证类型"`
|
||||
RequestConfig map[string]interface{} `json:"requestConfig" dc:"请求配置"`
|
||||
ResponseConfig map[string]interface{} `json:"responseConfig" dc:"响应配置"`
|
||||
LimitConfig map[string]interface{} `json:"limitConfig" dc:"接口独立限流配置"`
|
||||
TableDefinition map[string]interface{} `json:"tableDefinition" dc:"表结构定义(JSON),用于自动建表"`
|
||||
}
|
||||
|
||||
// CreateApiInterfaceRes 创建接口响应
|
||||
@@ -56,7 +57,9 @@ type ApiInterfaceItem struct {
|
||||
Method api_feature.ApiMethod `json:"method"`
|
||||
Status api_feature.PlatformStatus `json:"status"`
|
||||
StatusName string `json:"statusName"`
|
||||
CreatedBy string `json:"createdBy"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedBy string `json:"updatedBy"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
@@ -74,18 +77,19 @@ type GetApiInterfaceRes struct {
|
||||
|
||||
// UpdateApiInterfaceReq 更新接口请求
|
||||
type UpdateApiInterfaceReq struct {
|
||||
g.Meta `path:"/updateApiInterface" method:"put" tags:"接口管理" summary:"更新接口" dc:"更新接口信息"`
|
||||
Id int64 `json:"id" v:"required" dc:"接口ID"`
|
||||
PlatformId int64 `json:"platformId" dc:"所属平台ID"`
|
||||
Name string `json:"name" dc:"接口名称"`
|
||||
Code string `json:"code" dc:"接口编码"`
|
||||
Url string `json:"url" dc:"接口地址"`
|
||||
Method api_feature.ApiMethod `json:"method" dc:"请求方法"`
|
||||
Status api_feature.PlatformStatus `json:"status,omitempty" dc:"接口状态"`
|
||||
AuthType string `json:"authType" dc:"认证类型"`
|
||||
RequestConfig map[string]interface{} `json:"requestConfig" dc:"请求配置"`
|
||||
ResponseConfig map[string]interface{} `json:"responseConfig" dc:"响应配置"`
|
||||
LimitConfig map[string]interface{} `json:"limitConfig" dc:"接口独立限流配置"`
|
||||
g.Meta `path:"/updateApiInterface" method:"put" tags:"接口管理" summary:"更新接口" dc:"更新接口信息"`
|
||||
Id int64 `json:"id" v:"required" dc:"接口ID"`
|
||||
PlatformId int64 `json:"platformId" dc:"所属平台ID"`
|
||||
Name string `json:"name" dc:"接口名称"`
|
||||
Code string `json:"code" dc:"接口编码"`
|
||||
Url string `json:"url" dc:"接口地址"`
|
||||
Method api_feature.ApiMethod `json:"method" dc:"请求方法"`
|
||||
Status api_feature.PlatformStatus `json:"status,omitempty" dc:"接口状态"`
|
||||
AuthType string `json:"authType" dc:"认证类型"`
|
||||
RequestConfig map[string]interface{} `json:"requestConfig" dc:"请求配置"`
|
||||
ResponseConfig map[string]interface{} `json:"responseConfig" dc:"响应配置"`
|
||||
LimitConfig map[string]interface{} `json:"limitConfig" dc:"接口独立限流配置"`
|
||||
TableDefinition map[string]interface{} `json:"tableDefinition" dc:"表结构定义(JSON),用于自动建表"`
|
||||
}
|
||||
|
||||
// DeleteApiInterfaceReq 删除接口请求
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package public
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
)
|
||||
|
||||
// QueryReq 公共查询请求
|
||||
type QueryReq struct {
|
||||
g.Meta `path:"/public/query" method:"post" tags:"公共查询" summary:"公共数据查询" dc:"支持字段/表名/过滤/分组/分页的通用查询接口"`
|
||||
Table string `json:"table" v:"required" dc:"表名(白名单限制)"`
|
||||
Fields string `json:"fields" dc:"查询字段,逗号分隔,默认 *"`
|
||||
Where map[string]interface{} `json:"where" dc:"过滤条件,支持操作符: =, !=, >, <, >=, <=, like, in, between"`
|
||||
GroupBy string `json:"groupBy" dc:"分组字段,逗号分隔"`
|
||||
OrderBy string `json:"orderBy" dc:"排序字段,如: create_time desc"`
|
||||
Page int `json:"page" dc:"页码,默认1" d:"1"`
|
||||
PageSize int `json:"pageSize" dc:"每页条数,默认20,最大100" d:"20"`
|
||||
}
|
||||
|
||||
// QueryRes 公共查询响应
|
||||
type QueryRes struct {
|
||||
List []map[string]interface{} `json:"list" dc:"数据列表"`
|
||||
Total int64 `json:"total" dc:"总数"`
|
||||
Page int `json:"page" dc:"当前页码"`
|
||||
Size int `json:"size" dc:"每页条数"`
|
||||
}
|
||||
|
||||
// TableListReq 获取可查询表列表请求
|
||||
type TableListReq struct {
|
||||
g.Meta `path:"/public/tables" method:"get" tags:"公共查询" summary:"获取可查询表列表" dc:"获取配置了表结构的同步表列表"`
|
||||
}
|
||||
|
||||
// TableListRes 获取可查询表列表响应
|
||||
type TableListRes struct {
|
||||
List []TableInfo `json:"list" dc:"表列表"`
|
||||
}
|
||||
|
||||
// TableInfo 表信息
|
||||
type TableInfo struct {
|
||||
TableName string `json:"tableName" dc:"表名"`
|
||||
PlatformName string `json:"platformName" dc:"平台名称"`
|
||||
InterfaceName string `json:"interfaceName" dc:"接口名称"`
|
||||
Columns []string `json:"columns" dc:"可用字段列表"`
|
||||
}
|
||||
|
||||
// ColumnListReq 获取表字段列表请求
|
||||
type ColumnListReq struct {
|
||||
g.Meta `path:"/public/tables/{table}/columns" method:"get" tags:"公共查询" summary:"获取表字段列表" dc:"获取指定表的字段列表"`
|
||||
Table string `json:"table" dc:"表名"`
|
||||
}
|
||||
|
||||
// ColumnListRes 获取表字段列表响应
|
||||
type ColumnListRes struct {
|
||||
TableName string `json:"tableName" dc:"表名"`
|
||||
Columns []Column `json:"columns" dc:"字段列表"`
|
||||
}
|
||||
|
||||
// Column 字段信息
|
||||
type Column struct {
|
||||
Name string `json:"name" dc:"字段名"`
|
||||
Type string `json:"type" dc:"字段类型"`
|
||||
Comment string `json:"comment" dc:"字段说明"`
|
||||
}
|
||||
@@ -1,664 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// CidAccountReportDetail 广告数据报表详情实体
|
||||
type CidAccountReportDetail struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 基础信息字段
|
||||
DataType string `orm:"data_type" json:"dataType" description:"类型type"`
|
||||
|
||||
// 核心指标字段
|
||||
T0OrderPaymentAmt string `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型"`
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手Id"`
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片Id"`
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面URL"`
|
||||
CoverId int64 `orm:"cover_id" json:"coverId" description:"封面Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `orm:"item_order_conversion_ratio" json:"itemOrderConversionRatio" description:"转化率"`
|
||||
ItemCardClickRatio *float64 `orm:"item_card_click_ratio" json:"itemCardClickRatio" description:"点击率"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `orm:"live_play_cnt_cost" json:"livePlayCntCost" description:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `orm:"ad_merchant_follow_cost" json:"adMerchantFollowCost" description:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `orm:"ad_merchant_follow" json:"adMerchantFollow" description:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交GMV"`
|
||||
|
||||
// 视频信息字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频id"`
|
||||
|
||||
// 受众属性字段
|
||||
ModPriceSegment string `orm:"mod_price_segment" json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province string `orm:"province" json:"province" description:"省份名称"`
|
||||
Gender string `orm:"gender" json:"gender" description:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"作品5秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"作品3秒播放率"`
|
||||
|
||||
// 订单相关字段
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"订单提交ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
|
||||
// 商品信息字段
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
|
||||
// 封面相关字段
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"封面点击率"`
|
||||
|
||||
// 点击相关字段
|
||||
Click *int64 `orm:"click" json:"click" description:"素材曝光数"`
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `orm:"ad_item_click_count" json:"adItemClickCount" description:"预约组件点击数"`
|
||||
|
||||
// 直播时长字段
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
|
||||
// 播放数据字段
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"作品3秒播放数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"作品3秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"作品5秒播放数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"作品5秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"作品完播率"`
|
||||
|
||||
// 互动数据字段
|
||||
Share *int64 `orm:"share" json:"share" description:"作品分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"作品举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动字段
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播送礼数"`
|
||||
|
||||
// 播放效果字段
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
|
||||
// 转化字段
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"直接ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"直接GMV"`
|
||||
|
||||
// GMV时间序列字段
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日累计GMV"`
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3日累计GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7日累计GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15日累计GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30日累计GMV"`
|
||||
|
||||
// ROI时间序列字段
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日累计ROI"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3日累计ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7日累计ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15日累计ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30日累计ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T3OrderCnt *int64 `orm:"t3_order_cnt" json:"t3OrderCnt" description:"3日累计订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7日累计订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15日累计订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30日累计订单数"`
|
||||
|
||||
// 粉丝相关字段
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7日涨粉留存数"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15日涨粉留存数"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30日涨粉留存率"`
|
||||
|
||||
// 直播预约字段
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"直播预约成功成本"`
|
||||
|
||||
// 直播观看字段
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观看成本"`
|
||||
|
||||
// 商品点击字段
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30日累计店铺新客"`
|
||||
|
||||
// 间接订单字段
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额字段
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"3日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"7日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"15日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"30日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本字段
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"涨粉成本"`
|
||||
|
||||
// 智能优惠券字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝GMV字段
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"涨粉当日GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"涨粉次日GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"涨粉7日GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"涨粉15日GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"涨粉30日GMV"`
|
||||
|
||||
// 粉丝ROI字段
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"涨粉当日ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"涨粉次日ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"涨粉7日ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"涨粉15日ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"涨粉30日ROI"`
|
||||
|
||||
// 新客GMV字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后1日新客GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后3日新客GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后7日新客GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后15日新客GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后30日新客GMV"`
|
||||
|
||||
// 新客订单量字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"投后1日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"投后3日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"投后7日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"投后15日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"投后30日新客成交订单量"`
|
||||
|
||||
// 新客复购率字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"投后1日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"投后3日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"投后7日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"投后15日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"投后30日新客复购率"`
|
||||
|
||||
// 新客ROI字段
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"投后当日新客ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"投后1日新客ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"投后3日新客ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"投后7日新客ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"投后15日新客ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"投后30日新客ROI"`
|
||||
|
||||
// 电话卡相关字段
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
|
||||
// 时间字段
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
|
||||
// 广告结构字段
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
|
||||
// 补贴相关字段
|
||||
CidActualRoiAfterSubsidy *float64 `orm:"cid_actual_roi_after_subsidy" json:"cidActualRoiAfterSubsidy" description:"补贴后实际ROI"`
|
||||
CidCouponAmount *int64 `orm:"cid_coupon_amount" json:"cidCouponAmount" description:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `orm:"cid_coupon_callback_paid_refund_amount" json:"cidCouponCallbackPaidRefundAmount" description:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `orm:"cid_voucher_cost" json:"cidVoucherCost" description:"券成本"`
|
||||
}
|
||||
|
||||
// CidAccountReportDetailCol 广告数据报表详情表字段定义
|
||||
type CidAccountReportDetailCol struct {
|
||||
beans.SQLBaseCol
|
||||
DataType string
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemOrderConversionRatio string
|
||||
ItemCardClickRatio string
|
||||
ItemCardClkCnt string
|
||||
LivePlayCntCost string
|
||||
AdMerchantFollowCost string
|
||||
AdMerchantFollow string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemClickCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T3Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T3Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T3OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
CidActualRoiAfterSubsidy string
|
||||
CidCouponAmount string
|
||||
CidCouponCallbackPaidRefundAmount string
|
||||
CidVoucherCost string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *CidAccountReportDetail) TableName() string {
|
||||
return "cid_account_report_detail"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *CidAccountReportDetail) GetCols() *CidAccountReportDetailCol {
|
||||
return &CidAccountReportDetailCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
DataType: "data_type",
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemOrderConversionRatio: "item_order_conversion_ratio",
|
||||
ItemCardClickRatio: "item_card_click_ratio",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
LivePlayCntCost: "live_play_cnt_cost",
|
||||
AdMerchantFollowCost: "ad_merchant_follow_cost",
|
||||
AdMerchantFollow: "ad_merchant_follow",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemClickCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T3Gmv: "t3_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T3Roi: "t3_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T3OrderCnt: "t3_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
CidActualRoiAfterSubsidy: "cid_actual_roi_after_subsidy",
|
||||
CidCouponAmount: "cid_coupon_amount",
|
||||
CidCouponCallbackPaidRefundAmount: "cid_coupon_callback_paid_refund_amount",
|
||||
CidVoucherCost: "cid_voucher_cost",
|
||||
}
|
||||
}
|
||||
@@ -1,665 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// CidAccountReportSum 广告数据报表详情实体
|
||||
type CidAccountReportSum struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 基础信息字段
|
||||
DataType string `orm:"data_type" json:"dataType" description:"类型type"`
|
||||
|
||||
// 核心指标字段
|
||||
T0OrderPaymentAmt string `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型"`
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手Id"`
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片Id"`
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面URL"`
|
||||
CoverId int64 `orm:"cover_id" json:"coverId" description:"封面Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `orm:"item_order_conversion_ratio" json:"itemOrderConversionRatio" description:"转化率"`
|
||||
ItemCardClickRatio *float64 `orm:"item_card_click_ratio" json:"itemCardClickRatio" description:"点击率"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `orm:"live_play_cnt_cost" json:"livePlayCntCost" description:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `orm:"ad_merchant_follow_cost" json:"adMerchantFollowCost" description:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `orm:"ad_merchant_follow" json:"adMerchantFollow" description:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交GMV"`
|
||||
|
||||
// 视频信息字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频id"`
|
||||
|
||||
// 受众属性字段
|
||||
ModPriceSegment string `orm:"mod_price_segment" json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province string `orm:"province" json:"province" description:"省份名称"`
|
||||
Gender string `orm:"gender" json:"gender" description:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"作品5秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"作品3秒播放率"`
|
||||
|
||||
// 订单相关字段
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"订单提交ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
|
||||
// 商品信息字段
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
|
||||
// 封面相关字段
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"封面点击率"`
|
||||
|
||||
// 点击相关字段
|
||||
Click *int64 `orm:"click" json:"click" description:"素材曝光数"`
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `orm:"ad_item_click_count" json:"adItemClickCount" description:"预约组件点击数"`
|
||||
|
||||
// 直播时长字段
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
|
||||
// 播放数据字段
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"作品3秒播放数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"作品3秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"作品5秒播放数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"作品5秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"作品完播率"`
|
||||
|
||||
// 互动数据字段
|
||||
Share *int64 `orm:"share" json:"share" description:"作品分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"作品举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动字段
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播送礼数"`
|
||||
|
||||
// 播放效果字段
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
|
||||
// 转化字段
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"直接ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"直接GMV"`
|
||||
|
||||
// GMV时间序列字段
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日累计GMV"`
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3日累计GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7日累计GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15日累计GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30日累计GMV"`
|
||||
|
||||
// ROI时间序列字段
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日累计ROI"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3日累计ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7日累计ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15日累计ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30日累计ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T3OrderCnt *int64 `orm:"t3_order_cnt" json:"t3OrderCnt" description:"3日累计订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7日累计订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15日累计订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30日累计订单数"`
|
||||
|
||||
// 粉丝相关字段
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7日涨粉留存数"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15日涨粉留存数"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30日涨粉留存率"`
|
||||
|
||||
// 直播预约字段
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"直播预约成功成本"`
|
||||
|
||||
// 直播观看字段
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观看成本"`
|
||||
|
||||
// 商品点击字段
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30日累计店铺新客"`
|
||||
|
||||
// 间接订单字段
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额字段
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"3日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"7日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"15日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"30日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本字段
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"涨粉成本"`
|
||||
|
||||
// 智能优惠券字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝GMV字段
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"涨粉当日GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"涨粉次日GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"涨粉7日GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"涨粉15日GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"涨粉30日GMV"`
|
||||
|
||||
// 粉丝ROI字段
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"涨粉当日ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"涨粉次日ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"涨粉7日ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"涨粉15日ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"涨粉30日ROI"`
|
||||
|
||||
// 新客GMV字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后1日新客GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后3日新客GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后7日新客GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后15日新客GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后30日新客GMV"`
|
||||
|
||||
// 新客订单量字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"投后1日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"投后3日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"投后7日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"投后15日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"投后30日新客成交订单量"`
|
||||
|
||||
// 新客复购率字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"投后1日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"投后3日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"投后7日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"投后15日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"投后30日新客复购率"`
|
||||
|
||||
// 新客ROI字段
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"投后当日新客ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"投后1日新客ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"投后3日新客ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"投后7日新客ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"投后15日新客ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"投后30日新客ROI"`
|
||||
|
||||
// 电话卡相关字段
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
|
||||
// 时间字段
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
|
||||
// 广告结构字段
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
|
||||
// 补贴相关字段
|
||||
CidActualRoiAfterSubsidy *float64 `orm:"cid_actual_roi_after_subsidy" json:"cidActualRoiAfterSubsidy" description:"补贴后实际ROI"`
|
||||
CidCouponAmount *int64 `orm:"cid_coupon_amount" json:"cidCouponAmount" description:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `orm:"cid_coupon_callback_paid_refund_amount" json:"cidCouponCallbackPaidRefundAmount" description:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `orm:"cid_voucher_cost" json:"cidVoucherCost" description:"券成本"`
|
||||
}
|
||||
|
||||
// CidAccountReportSumCol 广告数据报表详情表字段定义
|
||||
type CidAccountReportSumCol struct {
|
||||
beans.SQLBaseCol
|
||||
DataType string
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemOrderConversionRatio string
|
||||
ItemCardClickRatio string
|
||||
ItemCardClkCnt string
|
||||
LivePlayCntCost string
|
||||
AdMerchantFollowCost string
|
||||
AdMerchantFollow string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemClickCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T3Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T3Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T3OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
// model/entity/cid_account_report_detail.go (续)
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
CidActualRoiAfterSubsidy string
|
||||
CidCouponAmount string
|
||||
CidCouponCallbackPaidRefundAmount string
|
||||
CidVoucherCost string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *CidAccountReportSum) TableName() string {
|
||||
return "cid_account_report_detail"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *CidAccountReportSum) GetCols() *CidAccountReportSumCol {
|
||||
return &CidAccountReportSumCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
DataType: "data_type",
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemOrderConversionRatio: "item_order_conversion_ratio",
|
||||
ItemCardClickRatio: "item_card_click_ratio",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
LivePlayCntCost: "live_play_cnt_cost",
|
||||
AdMerchantFollowCost: "ad_merchant_follow_cost",
|
||||
AdMerchantFollow: "ad_merchant_follow",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemClickCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T3Gmv: "t3_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T3Roi: "t3_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T3OrderCnt: "t3_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
CidActualRoiAfterSubsidy: "cid_actual_roi_after_subsidy",
|
||||
CidCouponAmount: "cid_coupon_amount",
|
||||
CidCouponCallbackPaidRefundAmount: "cid_coupon_callback_paid_refund_amount",
|
||||
CidVoucherCost: "cid_voucher_cost",
|
||||
}
|
||||
}
|
||||
@@ -1,582 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// CreativeReportDetail 广告效果指标明细表实体
|
||||
type CreativeReportDetail struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
T0OrderPaymentAmt *float64 `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型"`
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手 Id"`
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片 URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片 Id"`
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面 URL"`
|
||||
CoverId *int64 `orm:"cover_id" json:"coverId" description:"封面 Id"`
|
||||
ItemOrderConversionRatio *float64 `orm:"item_order_conversion_ratio" json:"itemOrderConversionRatio" description:"转化率"`
|
||||
ItemCardClickRatio *float64 `orm:"item_card_click_ratio" json:"itemCardClickRatio" description:"点击率"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `orm:"live_play_cnt_cost" json:"livePlayCntCost" description:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `orm:"ad_merchant_follow_cost" json:"adMerchantFollowCost" description:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `orm:"ad_merchant_follow" json:"adMerchantFollow" description:"涨粉数"`
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交 GMV"`
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频 id"`
|
||||
ModPriceSegment string `orm:"mod_price_segment" json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province string `orm:"province" json:"province" description:"省份名称"`
|
||||
Gender string `orm:"gender" json:"gender" description:"性别"`
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"作品 3 秒播放率"`
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品 ID"`
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"封面点击率"`
|
||||
Click *int64 `orm:"click" json:"click" description:"素材曝光数"`
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `orm:"ad_item_click_count" json:"adItemClickCount" description:"预约组件点击数"`
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"作品完播率"`
|
||||
Share *int64 `orm:"share" json:"share" description:"作品分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"作品举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播送礼数"`
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"直接 ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"直接 GMV"`
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日累计 GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30 日累计 GMV"`
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计 ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日累计 ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7 日累计 ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15 日累计 ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30 日累计 ROI"`
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30 日累计订单数"`
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30 日涨粉留存率"`
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"直播预约成功成本"`
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观看成本"`
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30 日累计店铺新客"`
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `orm:"t3_order_cnt" json:"t3OrderCnt" description:"3 日累计订单数"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3 日累计 ROI"`
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"30 日新增粉丝人均销售额"`
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"涨粉成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"涨粉 30 日 GMV"`
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"涨粉 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后 30 日新客 GMV"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"投后 30 日新客成交订单量"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"投后 30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"投后 30 日新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
CidActualRoiAfterSubsidy *float64 `orm:"cid_actual_roi_after_subsidy" json:"cidActualRoiAfterSubsidy" description:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `orm:"cid_coupon_amount" json:"cidCouponAmount" description:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `orm:"cid_coupon_callback_paid_refund_amount" json:"cidCouponCallbackPaidRefundAmount" description:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `orm:"cid_voucher_cost" json:"cidVoucherCost" description:"券成本"`
|
||||
}
|
||||
|
||||
// CreativeReportDetailCol 广告效果指标明细表字段定义
|
||||
type CreativeReportDetailCol struct {
|
||||
beans.SQLBaseCol
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemOrderConversionRatio string
|
||||
ItemCardClickRatio string
|
||||
ItemCardClkCnt string
|
||||
LivePlayCntCost string
|
||||
AdMerchantFollowCost string
|
||||
AdMerchantFollow string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemClickCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T3Gmv string
|
||||
T3OrderCnt string
|
||||
T3Roi string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
CidActualRoiAfterSubsidy string
|
||||
CidCouponAmount string
|
||||
CidCouponCallbackPaidRefundAmount string
|
||||
CidVoucherCost string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *CreativeReportDetail) TableName() string {
|
||||
return "creative_report_detail"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *CreativeReportDetail) GetCols() *CreativeReportDetailCol {
|
||||
return &CreativeReportDetailCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemOrderConversionRatio: "item_order_conversion_ratio",
|
||||
ItemCardClickRatio: "item_card_click_ratio",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
LivePlayCntCost: "live_play_cnt_cost",
|
||||
AdMerchantFollowCost: "ad_merchant_follow_cost",
|
||||
AdMerchantFollow: "ad_merchant_follow",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemClickCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T3Gmv: "t3_gmv",
|
||||
T3OrderCnt: "t3_order_cnt",
|
||||
T3Roi: "t3_roi",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
CidActualRoiAfterSubsidy: "cid_actual_roi_after_subsidy",
|
||||
CidCouponAmount: "cid_coupon_amount",
|
||||
CidCouponCallbackPaidRefundAmount: "cid_coupon_callback_paid_refund_amount",
|
||||
CidVoucherCost: "cid_voucher_cost",
|
||||
}
|
||||
}
|
||||
@@ -1,687 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// CreativeReportSum 广告效果指标表实体
|
||||
type CreativeReportSum struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 订单金额相关字段
|
||||
T0OrderPaymentAmt *float64 `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
|
||||
// 素材类型字段
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
|
||||
// 直播相关字段
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手 Id"`
|
||||
|
||||
// 图片相关字段
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片 URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片 Id"`
|
||||
|
||||
// 封面相关字段
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面 URL"`
|
||||
CoverId *int64 `orm:"cover_id" json:"coverId" description:"封面 Id"`
|
||||
|
||||
// 转化率相关字段
|
||||
ItemOrderConversionRatio *float64 `orm:"item_order_conversion_ratio" json:"itemOrderConversionRatio" description:"转化率"`
|
||||
ItemCardClickRatio *float64 `orm:"item_card_click_ratio" json:"itemCardClickRatio" description:"点击率"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
|
||||
// 成本相关字段
|
||||
LivePlayCntCost *float64 `orm:"live_play_cnt_cost" json:"livePlayCntCost" description:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `orm:"ad_merchant_follow_cost" json:"adMerchantFollowCost" description:"涨粉成本"`
|
||||
AdMerchantFollow *int64 `orm:"ad_merchant_follow" json:"adMerchantFollow" description:"涨粉数"`
|
||||
|
||||
// 净成交相关字段
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交 GMV"`
|
||||
|
||||
// 视频相关字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频 id"`
|
||||
|
||||
// 用户属性相关字段
|
||||
ModPriceSegment string `orm:"mod_price_segment" json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province string `orm:"province" json:"province" description:"省份名称"`
|
||||
Gender string `orm:"gender" json:"gender" description:"性别"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"作品 3 秒播放率"`
|
||||
|
||||
// 订单提交相关字段
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
|
||||
// 用户行为相关字段
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
|
||||
// 转化成本相关字段
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化成本(回传时间)"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
|
||||
// 商品相关字段
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品 ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
|
||||
// 曝光相关字段
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"封面点击率"`
|
||||
Click *int64 `orm:"click" json:"click" description:"素材曝光数"`
|
||||
|
||||
// 行为相关字段
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `orm:"ad_item_click_count" json:"adItemClickCount" description:"预约组件点击数"`
|
||||
|
||||
// 直播时长相关字段
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
|
||||
// 作品播放相关字段
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"作品完播率"`
|
||||
|
||||
// 作品互动相关字段
|
||||
Share *int64 `orm:"share" json:"share" description:"作品分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"作品举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动相关字段
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播送礼数"`
|
||||
|
||||
// 有效播放相关字段
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
|
||||
// 转化相关字段
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"直接 ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"直接 GMV"`
|
||||
|
||||
// 累计 GMV 相关字段
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日累计 GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30 日累计 GMV"`
|
||||
|
||||
// 累计 ROI 相关字段
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计 ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日累计 ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7 日累计 ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15 日累计 ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30 日累计 ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30 日累计订单数"`
|
||||
|
||||
// 涨粉相关字段
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30 日涨粉留存率"`
|
||||
|
||||
// 直播预约相关字段
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"直播预约成功成本"`
|
||||
|
||||
// 直播观看相关字段
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观看成本"`
|
||||
|
||||
// 直播间商品相关字段
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30 日累计店铺新客"`
|
||||
|
||||
// 3 日累计相关字段
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3 日累计 GMV"`
|
||||
T3OrderCnt *int64 `orm:"t3_order_cnt" json:"t3OrderCnt" description:"3 日累计订单数"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3 日累计 ROI"`
|
||||
|
||||
// 7 日间接订单相关字段
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7 日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额相关字段
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"30 日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本相关字段
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"涨粉成本"`
|
||||
|
||||
// 智能优惠券相关字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝 GMV 相关字段
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"涨粉 30 日 GMV"`
|
||||
|
||||
// 粉丝 ROI 相关字段
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"涨粉 30 日 ROI"`
|
||||
|
||||
// 新客 GMV 相关字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后 30 日新客 GMV"`
|
||||
|
||||
// 新客订单数相关字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"投后 30 日新客成交订单量"`
|
||||
|
||||
// 新客复购率相关字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"投后 30 日新客复购率"`
|
||||
|
||||
// 新客 ROI 相关字段
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"投后 30 日新客 ROI"`
|
||||
|
||||
// 制卡订单相关字段
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
|
||||
// 电话卡激活相关字段
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
|
||||
// 制卡订单率相关字段
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
|
||||
// 电话卡激活率相关字段
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 直播观看相关字段
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
|
||||
// 报告日期字段
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
|
||||
// 广告计划相关字段
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
|
||||
// 广告单元相关字段
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
|
||||
// 广告创意相关字段
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
|
||||
// 新增字段
|
||||
CidActualRoiAfterSubsidy *float64 `orm:"cid_actual_roi_after_subsidy" json:"cidActualRoiAfterSubsidy" description:"补贴后实际 ROI"`
|
||||
CidCouponAmount *int64 `orm:"cid_coupon_amount" json:"cidCouponAmount" description:"核销券金额"`
|
||||
CidCouponCallbackPaidRefundAmount *int64 `orm:"cid_coupon_callback_paid_refund_amount" json:"cidCouponCallbackPaidRefundAmount" description:"退单有回传_核销券金额"`
|
||||
CidVoucherCost *float64 `orm:"cid_voucher_cost" json:"cidVoucherCost" description:"券成本"`
|
||||
}
|
||||
|
||||
// CreativeReportSumCol 广告效果指标表字段定义
|
||||
type CreativeReportSumCol struct {
|
||||
beans.SQLBaseCol
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemOrderConversionRatio string
|
||||
ItemCardClickRatio string
|
||||
ItemCardClkCnt string
|
||||
LivePlayCntCost string
|
||||
AdMerchantFollowCost string
|
||||
AdMerchantFollow string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemClickCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T3Gmv string
|
||||
T3OrderCnt string
|
||||
T3Roi string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
CidActualRoiAfterSubsidy string
|
||||
CidCouponAmount string
|
||||
CidCouponCallbackPaidRefundAmount string
|
||||
CidVoucherCost string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *CreativeReportSum) TableName() string {
|
||||
return "creative_report_sum"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *CreativeReportSum) GetCols() *CreativeReportSumCol {
|
||||
return &CreativeReportSumCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemOrderConversionRatio: "item_order_conversion_ratio",
|
||||
ItemCardClickRatio: "item_card_click_ratio",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
LivePlayCntCost: "live_play_cnt_cost",
|
||||
AdMerchantFollowCost: "ad_merchant_follow_cost",
|
||||
AdMerchantFollow: "ad_merchant_follow",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemClickCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T3Gmv: "t3_gmv",
|
||||
T3OrderCnt: "t3_order_cnt",
|
||||
T3Roi: "t3_roi",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
CidActualRoiAfterSubsidy: "cid_actual_roi_after_subsidy",
|
||||
CidCouponAmount: "cid_coupon_amount",
|
||||
CidCouponCallbackPaidRefundAmount: "cid_coupon_callback_paid_refund_amount",
|
||||
CidVoucherCost: "cid_voucher_cost",
|
||||
}
|
||||
}
|
||||
@@ -1,576 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// MaterialReport 素材报表数据实体
|
||||
type MaterialReport struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
CreatedBy *string `orm:"created_by" json:"createdBy" description:"创建人"`
|
||||
UpdatedBy *string `orm:"updated_by" json:"updatedBy" description:"更新人"`
|
||||
T0OrderPaymentAmt *float64 `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日订单支付金额"`
|
||||
CreativeMaterialType *string `orm:"creative_material_type" json:"creativeMaterialType" description:"创意素材类型"`
|
||||
LiveName *string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId *string `orm:"author_id" json:"authorId" description:"作者 ID"`
|
||||
PicUrl *string `orm:"pic_url" json:"picUrl" description:"图片 URL"`
|
||||
PicName *string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId *string `orm:"pic_id" json:"picId" description:"图片 ID"`
|
||||
CoverUrl *string `orm:"cover_url" json:"coverUrl" description:"封面 URL"`
|
||||
CoverId *int64 `orm:"cover_id" json:"coverId" description:"封面 ID"`
|
||||
ItemOrderConversionRatio *float64 `orm:"item_order_conversion_ratio" json:"itemOrderConversionRatio" description:"商品订单转化率"`
|
||||
ItemCardClickRatio *float64 `orm:"item_card_click_ratio" json:"itemCardClickRatio" description:"商品卡点击率"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
LivePlayCntCost *float64 `orm:"live_play_cnt_cost" json:"livePlayCntCost" description:"直播间观看成本"`
|
||||
AdMerchantFollowCost *float64 `orm:"ad_merchant_follow_cost" json:"adMerchantFollowCost" description:"商家涨粉成本"`
|
||||
AdMerchantFollow *int64 `orm:"ad_merchant_follow" json:"adMerchantFollow" description:"商家涨粉数"`
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"当日净成交 ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"当日净成交 GMV"`
|
||||
PhotoName *string `orm:"photo_name" json:"photoName" description:"照片名称"`
|
||||
PhotoIdStr *string `orm:"photo_id_str" json:"photoIdStr" description:"照片 ID 字符串"`
|
||||
PhotoId *string `orm:"photo_id" json:"photoId" description:"照片 ID"`
|
||||
ModPriceSegment *string `orm:"mod_price_segment" json:"modPriceSegment" description:"价格区间"`
|
||||
AgeSegment *string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province *string `orm:"province" json:"province" description:"省份"`
|
||||
Gender *string `orm:"gender" json:"gender" description:"性别"`
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"广告照片播放 5 秒比率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"广告照片播放 3 秒比率"`
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"提交订单 ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"提交订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"事件订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"事件订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"事件订单支付 ROI"`
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"事件 APP 调用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"事件加购数"`
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化数量成本"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"广告有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"广告商品点击数"`
|
||||
MerchantProductId *string `orm:"merchant_product_id" json:"merchantProductId" description:"商家商品 ID"`
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"总花费"`
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"广告展示数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"千次展示成本"`
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"照片点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"照片点击率"`
|
||||
Click *int64 `orm:"click" json:"click" description:"点击数"`
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"操作栏点击数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"操作栏点击成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"ESP 点击率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"操作比率"`
|
||||
AdItemCount *int64 `orm:"ad_item_click_count" json:"adItemCount" description:"广告商品点击次数"`
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"ESP 直播播放秒数"`
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"播放 3 秒数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"播放 5 秒数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"5 秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"播放结束数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"完播率"`
|
||||
Share *int64 `orm:"share" json:"share" description:"分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"商品负反馈数"`
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播分享数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播打赏数"`
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本 ESP"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"GMV"`
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日 GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日 GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7 日 GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15 日 GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30 日 GMV"`
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日 ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日 ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7 日 ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15 日 ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30 日 ROI"`
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"支付订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"订单比率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"当日订单比率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7 日订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15 日订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30 日订单数"`
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"商家推荐粉丝数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日留存率"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7 日留存率"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15 日留存率"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30 日留存率"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日留存比率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7 日留存比率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15 日留存比率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30 日留存比率"`
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"预约成本"`
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"标准直播开始数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"广告直播播放数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"广告直播播放成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观众成本"`
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播事件商品浏览数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"商品点击率"`
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接吸引平台新买家数"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30 日吸引平台总买家数"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接吸引商家新买家数"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30 日吸引商家总买家数"`
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3 日 GMV"`
|
||||
T3OrderCnt *int64 `orm:"t3_order_cnt" json:"t3OrderCnt" description:"3 日订单数"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3 日 ROI"`
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7 日间接订单数"`
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"粉丝当日人均 GMV"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"粉丝 3 日人均 GMV"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"粉丝 7 日人均 GMV"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"粉丝 15 日人均 GMV"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"粉丝 30 日人均 GMV"`
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"推荐粉丝成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"白盒直接订单支付金额"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"白盒直接订单数"`
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"粉丝当日 GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"粉丝次日 GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"粉丝 7 日 GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"粉丝 15 日 GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"粉丝 30 日 GMV"`
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"粉丝当日 ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"粉丝次日 ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"粉丝 7 日 ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"粉丝 15 日 ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"粉丝 30 日 ROI"`
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日店铺新客订单支付金额"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"次日店铺新客订单支付金额"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"3 日店铺新客订单支付金额"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"7 日店铺新客订单支付金额"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"15 日店铺新客订单支付金额"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"30 日店铺新客订单支付金额"`
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日店铺新客订单数"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"次日店铺新客订单数"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"3 日店铺新客订单数"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"7 日店铺新客订单数"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"15 日店铺新客订单数"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"30 日店铺新客订单数"`
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"次日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"30 日新客复购率"`
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"当日店铺新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"次日店铺新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"3 日店铺新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"7 日店铺新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"15 日店铺新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"30 日店铺新客 ROI"`
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"创建卡片订单数"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"转发 TS 创建卡片订单数"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"创建卡片订单成本"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"转发 TS 创建卡片订单成本"`
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"激活卡片订单数"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"转发 TS 激活卡片订单数"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"激活卡片订单成本"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"转发 TS 激活卡片订单成本"`
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"创建卡片订单比率"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"转发 TS 创建卡片订单比率"`
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"激活卡片订单比率"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"转发 TS 激活卡片订单比率"`
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"直播播放数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"商品入口点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"展示数"`
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"报告日期(格式:YYYY-MM-DD)"`
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName *string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName *string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName *string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
}
|
||||
|
||||
// MaterialReportCol 素材报表数据表字段定义
|
||||
type MaterialReportCol struct {
|
||||
beans.SQLBaseCol
|
||||
CreatedBy string
|
||||
UpdatedBy string
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemOrderConversionRatio string
|
||||
ItemCardClickRatio string
|
||||
ItemCardClkCnt string
|
||||
LivePlayCntCost string
|
||||
AdMerchantFollowCost string
|
||||
AdMerchantFollow string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T3Gmv string
|
||||
T3OrderCnt string
|
||||
T3Roi string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *MaterialReport) TableName() string {
|
||||
return "material_report"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *MaterialReport) GetCols() *MaterialReportCol {
|
||||
return &MaterialReportCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
CreatedBy: "created_by",
|
||||
UpdatedBy: "updated_by",
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemOrderConversionRatio: "item_order_conversion_ratio",
|
||||
ItemCardClickRatio: "item_card_click_ratio",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
LivePlayCntCost: "live_play_cnt_cost",
|
||||
AdMerchantFollowCost: "ad_merchant_follow_cost",
|
||||
AdMerchantFollow: "ad_merchant_follow",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T3Gmv: "t3_gmv",
|
||||
T3OrderCnt: "t3_order_cnt",
|
||||
T3Roi: "t3_roi",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
}
|
||||
}
|
||||
@@ -1,578 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// PopulationReport 人群报表实体
|
||||
type PopulationReport struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 基础信息字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频 id"`
|
||||
ModPriceSegment string `orm:"mod_price_segment" json:"modPriceSegment" description:"设备价格区间"`
|
||||
AgeSegment string `orm:"age_segment" json:"ageSegment" description:"年龄段"`
|
||||
Province string `orm:"province" json:"province" description:"省份名称"`
|
||||
Gender string `orm:"gender" json:"gender" description:"性别"`
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品 ID"`
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
|
||||
// 广告结构字段
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
|
||||
// 播放率相关字段
|
||||
AdPhotoPlayedFiveRatio *float64 `orm:"ad_photo_played_five_ratio" json:"adPhotoPlayedFiveRatio" description:"作品 5 秒播放率"`
|
||||
AdPhotoPlayedThreeRatio *float64 `orm:"ad_photo_played_three_ratio" json:"adPhotoPlayedThreeRatio" description:"作品 3 秒播放率"`
|
||||
|
||||
// 订单相关字段
|
||||
OrderSubmitRoi *float64 `orm:"order_submit_roi" json:"orderSubmitRoi" description:"订单提交 ROI"`
|
||||
OrderSubmitAmt *int64 `orm:"order_submit_amt" json:"orderSubmitAmt" description:"外部订单金额"`
|
||||
EventOrderSubmitCost *float64 `orm:"event_order_submit_cost" json:"eventOrderSubmitCost" description:"订单提交成本"`
|
||||
EventOrderSubmit *int64 `orm:"event_order_submit" json:"eventOrderSubmit" description:"订单提交数"`
|
||||
EventOrderPaidRoi *float64 `orm:"event_order_paid_roi" json:"eventOrderPaidRoi" description:"订单支付率"`
|
||||
EventAppInvoked *int64 `orm:"event_app_invoked" json:"eventAppInvoked" description:"唤起应用数"`
|
||||
EventAddShoppingCart *int64 `orm:"event_add_shopping_cart" json:"eventAddShoppingCart" description:"添加购物车次数"`
|
||||
ConversionNumCost *float64 `orm:"conversion_num_cost" json:"conversionNumCost" description:"转化成本"`
|
||||
AdEffectivePlayNum *int64 `orm:"ad_effective_play_num" json:"adEffectivePlayNum" description:"有效播放数"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
AdShow *int64 `orm:"ad_show" json:"adShow" description:"曝光数"`
|
||||
AdShow1kCost *float64 `orm:"ad_show1k_cost" json:"adShow1kCost" description:"平均千次广告曝光花费"`
|
||||
|
||||
// 封面相关字段
|
||||
Impression *int64 `orm:"impression" json:"impression" description:"封面曝光数"`
|
||||
PhotoClick *int64 `orm:"photo_click" json:"photoClick" description:"封面点击数"`
|
||||
PhotoClickRatio *float64 `orm:"photo_click_ratio" json:"photoClickRatio" description:"封面点击率"`
|
||||
|
||||
// 点击相关字段
|
||||
Click *int64 `orm:"click" json:"click" description:"素材曝光数"`
|
||||
ActionbarClick *int64 `orm:"actionbar_click" json:"actionbarClick" description:"行为数"`
|
||||
ActionbarClickCost *float64 `orm:"actionbar_click_cost" json:"actionbarClickCost" description:"行为成本"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
ActionRatio *float64 `orm:"action_ratio" json:"actionRatio" description:"素材点击率"`
|
||||
AdItemClickCount *int64 `orm:"ad_item_click_count" json:"adItemClickCount" description:"预约组件点击数"`
|
||||
|
||||
// 直播时长字段
|
||||
EspLivePlayedSeconds *int64 `orm:"esp_live_played_seconds" json:"espLivePlayedSeconds" description:"直播平均观看时长"`
|
||||
|
||||
// 播放数据字段
|
||||
PlayedThreeSeconds *int64 `orm:"played_three_seconds" json:"playedThreeSeconds" description:"作品 3 秒播放数"`
|
||||
Play3sRatio *float64 `orm:"play3s_ratio" json:"play3sRatio" description:"作品 3 秒播放率"`
|
||||
PlayedFiveSeconds *int64 `orm:"played_five_seconds" json:"playedFiveSeconds" description:"作品 5 秒播放数"`
|
||||
Play5sRatio *float64 `orm:"play5s_ratio" json:"play5sRatio" description:"作品 5 秒播放率"`
|
||||
PlayedEnd *int64 `orm:"played_end" json:"playedEnd" description:"作品完播数"`
|
||||
PlayEndRatio *float64 `orm:"play_end_ratio" json:"playEndRatio" description:"作品完播率"`
|
||||
|
||||
// 互动数据字段
|
||||
Share *int64 `orm:"share" json:"share" description:"作品分享数"`
|
||||
Comment *int64 `orm:"comment" json:"comment" description:"作品评论数"`
|
||||
Likes *int64 `orm:"likes" json:"likes" description:"作品点赞数"`
|
||||
Report *int64 `orm:"report" json:"report" description:"作品举报数"`
|
||||
Block *int64 `orm:"block" json:"block" description:"作品拉黑数"`
|
||||
ItemNegative *int64 `orm:"item_negative" json:"itemNegative" description:"详情页减少此类作品数"`
|
||||
|
||||
// 直播互动字段
|
||||
LiveShare *int64 `orm:"live_share" json:"liveShare" description:"直播送礼数"`
|
||||
LiveComment *int64 `orm:"live_comment" json:"liveComment" description:"直播评论数"`
|
||||
LiveReward *int64 `orm:"live_reward" json:"liveReward" description:"直播送礼数"`
|
||||
|
||||
// 播放效果字段
|
||||
EffectivePlayCount *int64 `orm:"effective_play_count" json:"effectivePlayCount" description:"有效播放数"`
|
||||
EffectivePlayRatio *float64 `orm:"effective_play_ratio" json:"effectivePlayRatio" description:"有效播放率"`
|
||||
|
||||
// 转化字段
|
||||
ConversionNum *int64 `orm:"conversion_num" json:"conversionNum" description:"转化数"`
|
||||
ConversionCostEsp *float64 `orm:"conversion_cost_esp" json:"conversionCostEsp" description:"转化成本"`
|
||||
Roi *float64 `orm:"roi" json:"roi" description:"直接 ROI"`
|
||||
Gmv *float64 `orm:"gmv" json:"gmv" description:"直接 GMV"`
|
||||
|
||||
// GMV 时间序列字段
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T1Gmv *float64 `orm:"t1_gmv" json:"t1Gmv" description:"次日累计 GMV"`
|
||||
T3Gmv *float64 `orm:"t3_gmv" json:"t3Gmv" description:"3 日累计 GMV"`
|
||||
T7Gmv *float64 `orm:"t7_gmv" json:"t7Gmv" description:"7 日累计 GMV"`
|
||||
T15Gmv *float64 `orm:"t15_gmv" json:"t15Gmv" description:"15 日累计 GMV"`
|
||||
T30Gmv *float64 `orm:"t30_gmv" json:"t30Gmv" description:"30 日累计 GMV"`
|
||||
|
||||
// ROI 时间序列字段
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计 ROI"`
|
||||
T1Roi *float64 `orm:"t1_roi" json:"t1Roi" description:"次日累计 ROI"`
|
||||
T3Roi *float64 `orm:"t3_roi" json:"t3Roi" description:"3 日累计 ROI"`
|
||||
T7Roi *float64 `orm:"t7_roi" json:"t7Roi" description:"7 日累计 ROI"`
|
||||
T15Roi *float64 `orm:"t15_roi" json:"t15Roi" description:"15 日累计 ROI"`
|
||||
T30Roi *float64 `orm:"t30_roi" json:"t30Roi" description:"30 日累计 ROI"`
|
||||
|
||||
// 订单相关字段
|
||||
PaiedOrder *int64 `orm:"paied_order" json:"paiedOrder" description:"直接订单数"`
|
||||
OrderRatio *float64 `orm:"order_ratio" json:"orderRatio" description:"直接下单率"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
T0OrderCntRatio *float64 `orm:"t0_order_cnt_ratio" json:"t0OrderCntRatio" description:"累计订单下单率"`
|
||||
T1OrderCnt *int64 `orm:"t1_order_cnt" json:"t1OrderCnt" description:"次日累计订单数"`
|
||||
T7OrderCnt *int64 `orm:"t7_order_cnt" json:"t7OrderCnt" description:"7 日累计订单数"`
|
||||
T15OrderCnt *int64 `orm:"t15_order_cnt" json:"t15OrderCnt" description:"15 日累计订单数"`
|
||||
T30OrderCnt *int64 `orm:"t30_order_cnt" json:"t30OrderCnt" description:"30 日累计订单数"`
|
||||
|
||||
// 粉丝相关字段
|
||||
MerchantRecoFans *int64 `orm:"merchant_reco_fans" json:"merchantRecoFans" description:"涨粉数"`
|
||||
T1Retention *float64 `orm:"t1_retention" json:"t1Retention" description:"次日涨粉留存数"`
|
||||
T7Retention *float64 `orm:"t7_retention" json:"t7Retention" description:"7 日涨粉留存数"`
|
||||
T15Retention *float64 `orm:"t15_retention" json:"t15Retention" description:"15 日涨粉留存数"`
|
||||
T30Retention *float64 `orm:"t30_retention" json:"t30Retention" description:"30 日涨粉留存数"`
|
||||
T1RetentionRatio *float64 `orm:"t1_retention_ratio" json:"t1RetentionRatio" description:"次日涨粉留存率"`
|
||||
T7RetentionRatio *float64 `orm:"t7_retention_ratio" json:"t7RetentionRatio" description:"7 日涨粉留存率"`
|
||||
T15RetentionRatio *float64 `orm:"t15_retention_ratio" json:"t15RetentionRatio" description:"15 日涨粉留存率"`
|
||||
T30RetentionRatio *float64 `orm:"t30_retention_ratio" json:"t30RetentionRatio" description:"30 日涨粉留存率"`
|
||||
|
||||
// 直播预约字段
|
||||
ReservationSuccess *int64 `orm:"reservation_success" json:"reservationSuccess" description:"直播预约成功数"`
|
||||
ReservationCost *float64 `orm:"reservation_cost" json:"reservationCost" description:"直播预约成功成本"`
|
||||
|
||||
// 直播观看字段
|
||||
StandardLivePlayedStarted *int64 `orm:"standard_live_played_started" json:"standardLivePlayedStarted" description:"直播观看数"`
|
||||
AdLivePlayCnt *int64 `orm:"ad_live_play_cnt" json:"adLivePlayCnt" description:"直播间人气数"`
|
||||
AdLivePlayCntCost *float64 `orm:"ad_live_play_cnt_cost" json:"adLivePlayCntCost" description:"直播间人气成本"`
|
||||
LiveAudienceCost *float64 `orm:"live_audience_cost" json:"liveAudienceCost" description:"直播观看成本"`
|
||||
|
||||
// 商品点击字段
|
||||
LiveEventGoodsView *int64 `orm:"live_event_goods_view" json:"liveEventGoodsView" description:"直播间商品点击数"`
|
||||
GoodsClickRatio *float64 `orm:"goods_click_ratio" json:"goodsClickRatio" description:"直播间商品点击率"`
|
||||
|
||||
// 新客相关字段
|
||||
DirectAttrPlatNewBuyerCnt *int64 `orm:"direct_attr_plat_new_buyer_cnt" json:"directAttrPlatNewBuyerCnt" description:"直接平台新客"`
|
||||
T30AttrPlatTotalBuyerCnt *int64 `orm:"t30_attr_plat_total_buyer_cnt" json:"t30AttrPlatTotalBuyerCnt" description:"30 日累计平台新客"`
|
||||
DirectAttrSellerNewBuyerCnt *int64 `orm:"direct_attr_seller_new_buyer_cnt" json:"directAttrSellerNewBuyerCnt" description:"直接店铺新客"`
|
||||
T30AttrSellerTotalBuyerCnt *int64 `orm:"t30_attr_seller_total_buyer_cnt" json:"t30AttrSellerTotalBuyerCnt" description:"30 日累计店铺新客"`
|
||||
|
||||
// 间接订单字段
|
||||
T7IndirectOrderAmt *float64 `orm:"t7_indirect_order_amt" json:"t7IndirectOrderAmt" description:"7 日间接订单金额"`
|
||||
T7IndirectOrderCnt *int64 `orm:"t7_indirect_order_cnt" json:"t7IndirectOrderCnt" description:"7 日间接订单数"`
|
||||
|
||||
// 粉丝人均销售额字段
|
||||
FansT0GmvPerFans *float64 `orm:"fans_t0_gmv_per_fans" json:"fansT0GmvPerFans" description:"新增粉丝人均销售额"`
|
||||
FansT3GmvPerFans *float64 `orm:"fans_t3_gmv_per_fans" json:"fansT3GmvPerFans" description:"3 日新增粉丝人均销售额"`
|
||||
FansT7GmvPerFans *float64 `orm:"fans_t7_gmv_per_fans" json:"fansT7GmvPerFans" description:"7 日新增粉丝人均销售额"`
|
||||
FansT15GmvPerFans *float64 `orm:"fans_t15_gmv_per_fans" json:"fansT15GmvPerFans" description:"15 日新增粉丝人均销售额"`
|
||||
FansT30GmvPerFans *float64 `orm:"fans_t30_gmv_per_fans" json:"fansT30GmvPerFans" description:"30 日新增粉丝人均销售额"`
|
||||
|
||||
// 涨粉成本字段
|
||||
RecoFansCost *float64 `orm:"reco_fans_cost" json:"recoFansCost" description:"涨粉成本"`
|
||||
|
||||
// 智能优惠券字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
|
||||
// 粉丝 GMV 字段
|
||||
FansT0Gmv *float64 `orm:"fans_t0_gmv" json:"fansT0Gmv" description:"涨粉当日 GMV"`
|
||||
FansT1Gmv *float64 `orm:"fans_t1_gmv" json:"fansT1Gmv" description:"涨粉次日 GMV"`
|
||||
FansT7Gmv *float64 `orm:"fans_t7_gmv" json:"fansT7Gmv" description:"涨粉 7 日 GMV"`
|
||||
FansT15Gmv *float64 `orm:"fans_t15_gmv" json:"fansT15Gmv" description:"涨粉 15 日 GMV"`
|
||||
FansT30Gmv *float64 `orm:"fans_t30_gmv" json:"fansT30Gmv" description:"涨粉 30 日 GMV"`
|
||||
|
||||
// 粉丝 ROI 字段
|
||||
FansT0Roi *float64 `orm:"fans_t0_roi" json:"fansT0Roi" description:"涨粉当日 ROI"`
|
||||
FansT1Roi *float64 `orm:"fans_t1_roi" json:"fansT1Roi" description:"涨粉次日 ROI"`
|
||||
FansT7Roi *float64 `orm:"fans_t7_roi" json:"fansT7Roi" description:"涨粉 7 日 ROI"`
|
||||
FansT15Roi *float64 `orm:"fans_t15_roi" json:"fansT15Roi" description:"涨粉 15 日 ROI"`
|
||||
FansT30Roi *float64 `orm:"fans_t30_roi" json:"fansT30Roi" description:"涨粉 30 日 ROI"`
|
||||
|
||||
// 新客 GMV 字段
|
||||
T0ShopNewBuyerOrderPaymentAmt *float64 `orm:"t0_shop_new_buyer_order_payment_amt" json:"t0ShopNewBuyerOrderPaymentAmt" description:"当日新客 GMV"`
|
||||
T1ShopNewBuyerOrderPaymentAmt *float64 `orm:"t1_shop_new_buyer_order_payment_amt" json:"t1ShopNewBuyerOrderPaymentAmt" description:"投后 1 日新客 GMV"`
|
||||
T3ShopNewBuyerOrderPaymentAmt *float64 `orm:"t3_shop_new_buyer_order_payment_amt" json:"t3ShopNewBuyerOrderPaymentAmt" description:"投后 3 日新客 GMV"`
|
||||
T7ShopNewBuyerOrderPaymentAmt *float64 `orm:"t7_shop_new_buyer_order_payment_amt" json:"t7ShopNewBuyerOrderPaymentAmt" description:"投后 7 日新客 GMV"`
|
||||
T15ShopNewBuyerOrderPaymentAmt *float64 `orm:"t15_shop_new_buyer_order_payment_amt" json:"t15ShopNewBuyerOrderPaymentAmt" description:"投后 15 日新客 GMV"`
|
||||
T30ShopNewBuyerOrderPaymentAmt *float64 `orm:"t30_shop_new_buyer_order_payment_amt" json:"t30ShopNewBuyerOrderPaymentAmt" description:"投后 30 日新客 GMV"`
|
||||
|
||||
// 新客订单量字段
|
||||
T0ShopNewBuyerOrderCnt *int64 `orm:"t0_shop_new_buyer_order_cnt" json:"t0ShopNewBuyerOrderCnt" description:"当日新客成交订单量"`
|
||||
T1ShopNewBuyerOrderCnt *int64 `orm:"t1_shop_new_buyer_order_cnt" json:"t1ShopNewBuyerOrderCnt" description:"投后 1 日新客成交订单量"`
|
||||
T3ShopNewBuyerOrderCnt *int64 `orm:"t3_shop_new_buyer_order_cnt" json:"t3ShopNewBuyerOrderCnt" description:"投后 3 日新客成交订单量"`
|
||||
T7ShopNewBuyerOrderCnt *int64 `orm:"t7_shop_new_buyer_order_cnt" json:"t7ShopNewBuyerOrderCnt" description:"投后 7 日新客成交订单量"`
|
||||
T15ShopNewBuyerOrderCnt *int64 `orm:"t15_shop_new_buyer_order_cnt" json:"t15ShopNewBuyerOrderCnt" description:"投后 15 日新客成交订单量"`
|
||||
T30ShopNewBuyerOrderCnt *int64 `orm:"t30_shop_new_buyer_order_cnt" json:"t30ShopNewBuyerOrderCnt" description:"投后 30 日新客成交订单量"`
|
||||
|
||||
// 新客复购率字段
|
||||
T1NewBuyerRepurchaseRatio *float64 `orm:"t1_new_buyer_repurchase_ratio" json:"t1NewBuyerRepurchaseRatio" description:"投后 1 日新客复购率"`
|
||||
T3NewBuyerRepurchaseRatio *float64 `orm:"t3_new_buyer_repurchase_ratio" json:"t3NewBuyerRepurchaseRatio" description:"投后 3 日新客复购率"`
|
||||
T7NewBuyerRepurchaseRatio *float64 `orm:"t7_new_buyer_repurchase_ratio" json:"t7NewBuyerRepurchaseRatio" description:"投后 7 日新客复购率"`
|
||||
T15NewBuyerRepurchaseRatio *float64 `orm:"t15_new_buyer_repurchase_ratio" json:"t15NewBuyerRepurchaseRatio" description:"投后 15 日新客复购率"`
|
||||
T30NewBuyerRepurchaseRatio *float64 `orm:"t30_new_buyer_repurchase_ratio" json:"t30NewBuyerRepurchaseRatio" description:"投后 30 日新客复购率"`
|
||||
|
||||
// 新客 ROI 字段
|
||||
T0ShopNewBuyerRoi *float64 `orm:"t0_shop_new_buyer_roi" json:"t0ShopNewBuyerRoi" description:"投后当日新客 ROI"`
|
||||
T1ShopNewBuyerRoi *float64 `orm:"t1_shop_new_buyer_roi" json:"t1ShopNewBuyerRoi" description:"投后 1 日新客 ROI"`
|
||||
T3ShopNewBuyerRoi *float64 `orm:"t3_shop_new_buyer_roi" json:"t3ShopNewBuyerRoi" description:"投后 3 日新客 ROI"`
|
||||
T7ShopNewBuyerRoi *float64 `orm:"t7_shop_new_buyer_roi" json:"t7ShopNewBuyerRoi" description:"投后 7 日新客 ROI"`
|
||||
T15ShopNewBuyerRoi *float64 `orm:"t15_shop_new_buyer_roi" json:"t15ShopNewBuyerRoi" description:"投后 15 日新客 ROI"`
|
||||
T30ShopNewBuyerRoi *float64 `orm:"t30_shop_new_buyer_roi" json:"t30ShopNewBuyerRoi" description:"投后 30 日新客 ROI"`
|
||||
|
||||
// 电话卡相关字段
|
||||
CreateCardOrderCnt *int64 `orm:"create_card_order_cnt" json:"createCardOrderCnt" description:"有效制卡订单数(回传时间)"`
|
||||
ForwardTsCreateCardOrderCnt *int64 `orm:"forward_ts_create_card_order_cnt" json:"forwardTsCreateCardOrderCnt" description:"有效制卡订单数(计费时间)"`
|
||||
CreateCardOrderCost *float64 `orm:"create_card_order_cost" json:"createCardOrderCost" description:"有效制卡订单成本(回传时间)"`
|
||||
ForwardTsCreateCardOrderCost *float64 `orm:"forward_ts_create_card_order_cost" json:"forwardTsCreateCardOrderCost" description:"有效制卡订单成本(计费时间)"`
|
||||
ActivateCardOrderCnt *int64 `orm:"activate_card_order_cnt" json:"activateCardOrderCnt" description:"电话卡激活订单数(回传时间)"`
|
||||
ForwardTsActivateCardOrderCnt *int64 `orm:"forward_ts_activate_card_order_cnt" json:"forwardTsActivateCardOrderCnt" description:"电话卡激活订单数(计费时间)"`
|
||||
ActivateCardOrderCost *float64 `orm:"activate_card_order_cost" json:"activateCardOrderCost" description:"电话卡激活订单成本(回传时间)"`
|
||||
ForwardTsActivateCardOrderCost *float64 `orm:"forward_ts_activate_card_order_cost" json:"forwardTsActivateCardOrderCost" description:"电话卡激活订单成本(计费时间)"`
|
||||
CreateCardOrderRatio *float64 `orm:"create_card_order_ratio" json:"createCardOrderRatio" description:"有效制卡订单率(回传时间)"`
|
||||
ForwardTsCreateCardOrderRatio *float64 `orm:"forward_ts_create_card_order_ratio" json:"forwardTsCreateCardOrderRatio" description:"有效制卡订单率(计费时间)"`
|
||||
ActivateCardOrderCntRatio *float64 `orm:"activate_card_order_cnt_ratio" json:"activateCardOrderCntRatio" description:"电话卡激活率(回传时间)"`
|
||||
ForwardTsActivateCardOrderRatio *float64 `orm:"forward_ts_activate_card_order_ratio" json:"forwardTsActivateCardOrderRatio" description:"电话卡激活率(计费时间)"`
|
||||
|
||||
// 全站数据字段
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
}
|
||||
|
||||
// PopulationReportCol 人群报表字段定义
|
||||
type PopulationReportCol struct {
|
||||
beans.SQLBaseCol
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
ModPriceSegment string
|
||||
AgeSegment string
|
||||
Province string
|
||||
Gender string
|
||||
MerchantProductId string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
AdPhotoPlayedFiveRatio string
|
||||
AdPhotoPlayedThreeRatio string
|
||||
OrderSubmitRoi string
|
||||
OrderSubmitAmt string
|
||||
EventOrderSubmitCost string
|
||||
EventOrderSubmit string
|
||||
EventOrderPaidRoi string
|
||||
EventAppInvoked string
|
||||
EventAddShoppingCart string
|
||||
ConversionNumCost string
|
||||
AdEffectivePlayNum string
|
||||
AdItemClick string
|
||||
CostTotal string
|
||||
AdShow string
|
||||
AdShow1kCost string
|
||||
Impression string
|
||||
PhotoClick string
|
||||
PhotoClickRatio string
|
||||
Click string
|
||||
ActionbarClick string
|
||||
ActionbarClickCost string
|
||||
EspClickRatio string
|
||||
ActionRatio string
|
||||
AdItemClickCount string
|
||||
EspLivePlayedSeconds string
|
||||
PlayedThreeSeconds string
|
||||
Play3sRatio string
|
||||
PlayedFiveSeconds string
|
||||
Play5sRatio string
|
||||
PlayedEnd string
|
||||
PlayEndRatio string
|
||||
Share string
|
||||
Comment string
|
||||
Likes string
|
||||
Report string
|
||||
Block string
|
||||
ItemNegative string
|
||||
LiveShare string
|
||||
LiveComment string
|
||||
LiveReward string
|
||||
EffectivePlayCount string
|
||||
EffectivePlayRatio string
|
||||
ConversionNum string
|
||||
ConversionCostEsp string
|
||||
Roi string
|
||||
Gmv string
|
||||
T0Gmv string
|
||||
T1Gmv string
|
||||
T3Gmv string
|
||||
T7Gmv string
|
||||
T15Gmv string
|
||||
T30Gmv string
|
||||
T0Roi string
|
||||
T1Roi string
|
||||
T3Roi string
|
||||
T7Roi string
|
||||
T15Roi string
|
||||
T30Roi string
|
||||
PaiedOrder string
|
||||
OrderRatio string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
T0OrderCntRatio string
|
||||
T1OrderCnt string
|
||||
T7OrderCnt string
|
||||
T15OrderCnt string
|
||||
T30OrderCnt string
|
||||
MerchantRecoFans string
|
||||
T1Retention string
|
||||
T7Retention string
|
||||
T15Retention string
|
||||
T30Retention string
|
||||
T1RetentionRatio string
|
||||
T7RetentionRatio string
|
||||
T15RetentionRatio string
|
||||
T30RetentionRatio string
|
||||
ReservationSuccess string
|
||||
ReservationCost string
|
||||
StandardLivePlayedStarted string
|
||||
AdLivePlayCnt string
|
||||
AdLivePlayCntCost string
|
||||
LiveAudienceCost string
|
||||
LiveEventGoodsView string
|
||||
GoodsClickRatio string
|
||||
DirectAttrPlatNewBuyerCnt string
|
||||
T30AttrPlatTotalBuyerCnt string
|
||||
DirectAttrSellerNewBuyerCnt string
|
||||
T30AttrSellerTotalBuyerCnt string
|
||||
T7IndirectOrderAmt string
|
||||
T7IndirectOrderCnt string
|
||||
FansT0GmvPerFans string
|
||||
FansT3GmvPerFans string
|
||||
FansT7GmvPerFans string
|
||||
FansT15GmvPerFans string
|
||||
FansT30GmvPerFans string
|
||||
RecoFansCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
FansT0Gmv string
|
||||
FansT1Gmv string
|
||||
FansT7Gmv string
|
||||
FansT15Gmv string
|
||||
FansT30Gmv string
|
||||
FansT0Roi string
|
||||
FansT1Roi string
|
||||
FansT7Roi string
|
||||
FansT15Roi string
|
||||
FansT30Roi string
|
||||
T0ShopNewBuyerOrderPaymentAmt string
|
||||
T1ShopNewBuyerOrderPaymentAmt string
|
||||
T3ShopNewBuyerOrderPaymentAmt string
|
||||
T7ShopNewBuyerOrderPaymentAmt string
|
||||
T15ShopNewBuyerOrderPaymentAmt string
|
||||
T30ShopNewBuyerOrderPaymentAmt string
|
||||
T0ShopNewBuyerOrderCnt string
|
||||
T1ShopNewBuyerOrderCnt string
|
||||
T3ShopNewBuyerOrderCnt string
|
||||
T7ShopNewBuyerOrderCnt string
|
||||
T15ShopNewBuyerOrderCnt string
|
||||
T30ShopNewBuyerOrderCnt string
|
||||
T1NewBuyerRepurchaseRatio string
|
||||
T3NewBuyerRepurchaseRatio string
|
||||
T7NewBuyerRepurchaseRatio string
|
||||
T15NewBuyerRepurchaseRatio string
|
||||
T30NewBuyerRepurchaseRatio string
|
||||
T0ShopNewBuyerRoi string
|
||||
T1ShopNewBuyerRoi string
|
||||
T3ShopNewBuyerRoi string
|
||||
T7ShopNewBuyerRoi string
|
||||
T15ShopNewBuyerRoi string
|
||||
T30ShopNewBuyerRoi string
|
||||
CreateCardOrderCnt string
|
||||
ForwardTsCreateCardOrderCnt string
|
||||
CreateCardOrderCost string
|
||||
ForwardTsCreateCardOrderCost string
|
||||
ActivateCardOrderCnt string
|
||||
ForwardTsActivateCardOrderCnt string
|
||||
ActivateCardOrderCost string
|
||||
ForwardTsActivateCardOrderCost string
|
||||
CreateCardOrderRatio string
|
||||
ForwardTsCreateCardOrderRatio string
|
||||
ActivateCardOrderCntRatio string
|
||||
ForwardTsActivateCardOrderRatio string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *PopulationReport) TableName() string {
|
||||
return "population_report"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *PopulationReport) GetCols() *PopulationReportCol {
|
||||
return &PopulationReportCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
ModPriceSegment: "mod_price_segment",
|
||||
AgeSegment: "age_segment",
|
||||
Province: "province",
|
||||
Gender: "gender",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
AdPhotoPlayedFiveRatio: "ad_photo_played_five_ratio",
|
||||
AdPhotoPlayedThreeRatio: "ad_photo_played_three_ratio",
|
||||
OrderSubmitRoi: "order_submit_roi",
|
||||
OrderSubmitAmt: "order_submit_amt",
|
||||
EventOrderSubmitCost: "event_order_submit_cost",
|
||||
EventOrderSubmit: "event_order_submit",
|
||||
EventOrderPaidRoi: "event_order_paid_roi",
|
||||
EventAppInvoked: "event_app_invoked",
|
||||
EventAddShoppingCart: "event_add_shopping_cart",
|
||||
ConversionNumCost: "conversion_num_cost",
|
||||
AdEffectivePlayNum: "ad_effective_play_num",
|
||||
AdItemClick: "ad_item_click",
|
||||
CostTotal: "cost_total",
|
||||
AdShow: "ad_show",
|
||||
AdShow1kCost: "ad_show1k_cost",
|
||||
Impression: "impression",
|
||||
PhotoClick: "photo_click",
|
||||
PhotoClickRatio: "photo_click_ratio",
|
||||
Click: "click",
|
||||
ActionbarClick: "actionbar_click",
|
||||
ActionbarClickCost: "actionbar_click_cost",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
ActionRatio: "action_ratio",
|
||||
AdItemClickCount: "ad_item_click_count",
|
||||
EspLivePlayedSeconds: "esp_live_played_seconds",
|
||||
PlayedThreeSeconds: "played_three_seconds",
|
||||
Play3sRatio: "play3s_ratio",
|
||||
PlayedFiveSeconds: "played_five_seconds",
|
||||
Play5sRatio: "play5s_ratio",
|
||||
PlayedEnd: "played_end",
|
||||
PlayEndRatio: "play_end_ratio",
|
||||
Share: "share",
|
||||
Comment: "comment",
|
||||
Likes: "likes",
|
||||
Report: "report",
|
||||
Block: "block",
|
||||
ItemNegative: "item_negative",
|
||||
LiveShare: "live_share",
|
||||
LiveComment: "live_comment",
|
||||
LiveReward: "live_reward",
|
||||
EffectivePlayCount: "effective_play_count",
|
||||
EffectivePlayRatio: "effective_play_ratio",
|
||||
ConversionNum: "conversion_num",
|
||||
ConversionCostEsp: "conversion_cost_esp",
|
||||
Roi: "roi",
|
||||
Gmv: "gmv",
|
||||
T0Gmv: "t0_gmv",
|
||||
T1Gmv: "t1_gmv",
|
||||
T3Gmv: "t3_gmv",
|
||||
T7Gmv: "t7_gmv",
|
||||
T15Gmv: "t15_gmv",
|
||||
T30Gmv: "t30_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T1Roi: "t1_roi",
|
||||
T3Roi: "t3_roi",
|
||||
T7Roi: "t7_roi",
|
||||
T15Roi: "t15_roi",
|
||||
T30Roi: "t30_roi",
|
||||
PaiedOrder: "paied_order",
|
||||
OrderRatio: "order_ratio",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
T0OrderCntRatio: "t0_order_cnt_ratio",
|
||||
T1OrderCnt: "t1_order_cnt",
|
||||
T7OrderCnt: "t7_order_cnt",
|
||||
T15OrderCnt: "t15_order_cnt",
|
||||
T30OrderCnt: "t30_order_cnt",
|
||||
MerchantRecoFans: "merchant_reco_fans",
|
||||
T1Retention: "t1_retention",
|
||||
T7Retention: "t7_retention",
|
||||
T15Retention: "t15_retention",
|
||||
T30Retention: "t30_retention",
|
||||
T1RetentionRatio: "t1_retention_ratio",
|
||||
T7RetentionRatio: "t7_retention_ratio",
|
||||
T15RetentionRatio: "t15_retention_ratio",
|
||||
T30RetentionRatio: "t30_retention_ratio",
|
||||
ReservationSuccess: "reservation_success",
|
||||
ReservationCost: "reservation_cost",
|
||||
StandardLivePlayedStarted: "standard_live_played_started",
|
||||
AdLivePlayCnt: "ad_live_play_cnt",
|
||||
AdLivePlayCntCost: "ad_live_play_cnt_cost",
|
||||
LiveAudienceCost: "live_audience_cost",
|
||||
LiveEventGoodsView: "live_event_goods_view",
|
||||
GoodsClickRatio: "goods_click_ratio",
|
||||
DirectAttrPlatNewBuyerCnt: "direct_attr_plat_new_buyer_cnt",
|
||||
T30AttrPlatTotalBuyerCnt: "t30_attr_plat_total_buyer_cnt",
|
||||
DirectAttrSellerNewBuyerCnt: "direct_attr_seller_new_buyer_cnt",
|
||||
T30AttrSellerTotalBuyerCnt: "t30_attr_seller_total_buyer_cnt",
|
||||
T7IndirectOrderAmt: "t7_indirect_order_amt",
|
||||
T7IndirectOrderCnt: "t7_indirect_order_cnt",
|
||||
FansT0GmvPerFans: "fans_t0_gmv_per_fans",
|
||||
FansT3GmvPerFans: "fans_t3_gmv_per_fans",
|
||||
FansT7GmvPerFans: "fans_t7_gmv_per_fans",
|
||||
FansT15GmvPerFans: "fans_t15_gmv_per_fans",
|
||||
FansT30GmvPerFans: "fans_t30_gmv_per_fans",
|
||||
RecoFansCost: "reco_fans_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
FansT0Gmv: "fans_t0_gmv",
|
||||
FansT1Gmv: "fans_t1_gmv",
|
||||
FansT7Gmv: "fans_t7_gmv",
|
||||
FansT15Gmv: "fans_t15_gmv",
|
||||
FansT30Gmv: "fans_t30_gmv",
|
||||
FansT0Roi: "fans_t0_roi",
|
||||
FansT1Roi: "fans_t1_roi",
|
||||
FansT7Roi: "fans_t7_roi",
|
||||
FansT15Roi: "fans_t15_roi",
|
||||
FansT30Roi: "fans_t30_roi",
|
||||
T0ShopNewBuyerOrderPaymentAmt: "t0_shop_new_buyer_order_payment_amt",
|
||||
T1ShopNewBuyerOrderPaymentAmt: "t1_shop_new_buyer_order_payment_amt",
|
||||
T3ShopNewBuyerOrderPaymentAmt: "t3_shop_new_buyer_order_payment_amt",
|
||||
T7ShopNewBuyerOrderPaymentAmt: "t7_shop_new_buyer_order_payment_amt",
|
||||
T15ShopNewBuyerOrderPaymentAmt: "t15_shop_new_buyer_order_payment_amt",
|
||||
T30ShopNewBuyerOrderPaymentAmt: "t30_shop_new_buyer_order_payment_amt",
|
||||
T0ShopNewBuyerOrderCnt: "t0_shop_new_buyer_order_cnt",
|
||||
T1ShopNewBuyerOrderCnt: "t1_shop_new_buyer_order_cnt",
|
||||
T3ShopNewBuyerOrderCnt: "t3_shop_new_buyer_order_cnt",
|
||||
T7ShopNewBuyerOrderCnt: "t7_shop_new_buyer_order_cnt",
|
||||
T15ShopNewBuyerOrderCnt: "t15_shop_new_buyer_order_cnt",
|
||||
T30ShopNewBuyerOrderCnt: "t30_shop_new_buyer_order_cnt",
|
||||
T1NewBuyerRepurchaseRatio: "t1_new_buyer_repurchase_ratio",
|
||||
T3NewBuyerRepurchaseRatio: "t3_new_buyer_repurchase_ratio",
|
||||
T7NewBuyerRepurchaseRatio: "t7_new_buyer_repurchase_ratio",
|
||||
T15NewBuyerRepurchaseRatio: "t15_new_buyer_repurchase_ratio",
|
||||
T30NewBuyerRepurchaseRatio: "t30_new_buyer_repurchase_ratio",
|
||||
T0ShopNewBuyerRoi: "t0_shop_new_buyer_roi",
|
||||
T1ShopNewBuyerRoi: "t1_shop_new_buyer_roi",
|
||||
T3ShopNewBuyerRoi: "t3_shop_new_buyer_roi",
|
||||
T7ShopNewBuyerRoi: "t7_shop_new_buyer_roi",
|
||||
T15ShopNewBuyerRoi: "t15_shop_new_buyer_roi",
|
||||
T30ShopNewBuyerRoi: "t30_shop_new_buyer_roi",
|
||||
CreateCardOrderCnt: "create_card_order_cnt",
|
||||
ForwardTsCreateCardOrderCnt: "forward_ts_create_card_order_cnt",
|
||||
CreateCardOrderCost: "create_card_order_cost",
|
||||
ForwardTsCreateCardOrderCost: "forward_ts_create_card_order_cost",
|
||||
ActivateCardOrderCnt: "activate_card_order_cnt",
|
||||
ForwardTsActivateCardOrderCnt: "forward_ts_activate_card_order_cnt",
|
||||
ActivateCardOrderCost: "activate_card_order_cost",
|
||||
ForwardTsActivateCardOrderCost: "forward_ts_activate_card_order_cost",
|
||||
CreateCardOrderRatio: "create_card_order_ratio",
|
||||
ForwardTsCreateCardOrderRatio: "forward_ts_create_card_order_ratio",
|
||||
ActivateCardOrderCntRatio: "activate_card_order_cnt_ratio",
|
||||
ForwardTsActivateCardOrderRatio: "forward_ts_activate_card_order_ratio",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
}
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// StorewideReportDetail 广告效果指标表实体
|
||||
type StorewideReportDetail struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 订单金额相关字段
|
||||
T0OrderPaymentAmt *float64 `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
|
||||
// 素材类型字段
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型(视频:HORIZONTAL_SCREEN, VERTICAL_SCREEN, UNKNOWN_TYPE;图集:ATLAS;长图:ATLAS_VERTICAL)"`
|
||||
|
||||
// 直播相关字段
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手 Id"`
|
||||
|
||||
// 图片相关字段
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片 URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片 Id"`
|
||||
|
||||
// 封面相关字段
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面 URL"`
|
||||
CoverId *int64 `orm:"cover_id" json:"coverId" description:"封面 Id"`
|
||||
|
||||
// 商品卡点击相关字段
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
|
||||
// 净成交相关字段
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交 GMV"`
|
||||
|
||||
// 视频相关字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频 id"`
|
||||
|
||||
// 行为相关字段
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
|
||||
// 商品相关字段
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品 ID"`
|
||||
|
||||
// 花费相关字段
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
|
||||
// 行为率字段
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
|
||||
// 当日 GMV 相关字段
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计 ROI"`
|
||||
|
||||
// 当日订单相关字段
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
|
||||
// 智能优惠券相关字段
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
|
||||
// 直播观看相关字段
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
|
||||
// 商品入口点击相关字段
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
|
||||
// 曝光相关字段
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
|
||||
// 报告日期字段
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
|
||||
// 广告计划相关字段
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
|
||||
// 广告单元相关字段
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
|
||||
// 广告创意相关字段
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
}
|
||||
|
||||
// StorewideReportDetailCol 广告效果指标表表字段定义
|
||||
type StorewideReportDetailCol struct {
|
||||
beans.SQLBaseCol
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemCardClkCnt string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
EspClickRatio string
|
||||
T0Gmv string
|
||||
T0Roi string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *StorewideReportDetail) TableName() string {
|
||||
return "storewide_report_detail"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *StorewideReportDetail) GetCols() *StorewideReportDetailCol {
|
||||
return &StorewideReportDetailCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
T0Gmv: "t0_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
}
|
||||
}
|
||||
@@ -1,141 +0,0 @@
|
||||
package copydata
|
||||
|
||||
import "gitea.com/red-future/common/beans"
|
||||
|
||||
// StorewideReportSum 广告效果指标表实体
|
||||
type StorewideReportSum struct {
|
||||
beans.SQLBaseDO `orm:",inherit"`
|
||||
|
||||
// 新增字段
|
||||
T0OrderPaymentAmt *float64 `orm:"t0_order_payment_amt" json:"t0OrderPaymentAmt" description:"当日总成交订单金额"`
|
||||
CreativeMaterialType string `orm:"creative_material_type" json:"creativeMaterialType" description:"视频素材类型"`
|
||||
LiveName string `orm:"live_name" json:"liveName" description:"直播间名称"`
|
||||
AuthorId string `orm:"author_id" json:"authorId" description:"直播用户快手 Id"`
|
||||
PicUrl string `orm:"pic_url" json:"picUrl" description:"图片 URL"`
|
||||
PicName string `orm:"pic_name" json:"picName" description:"图片名称"`
|
||||
PicId string `orm:"pic_id" json:"picId" description:"图片 Id"`
|
||||
CoverUrl string `orm:"cover_url" json:"coverUrl" description:"封面 URL"`
|
||||
CoverId *int64 `orm:"cover_id" json:"coverId" description:"封面 Id"`
|
||||
ItemCardClkCnt *int64 `orm:"item_card_clk_cnt" json:"itemCardClkCnt" description:"商品卡点击数"`
|
||||
NetT0OrderCnt *int64 `orm:"net_t0_order_cnt" json:"netT0OrderCnt" description:"当日累计净成交订单数"`
|
||||
NetT0Roi *float64 `orm:"net_t0_roi" json:"netT0Roi" description:"净成交 ROI"`
|
||||
NetT0Gmv *float64 `orm:"net_t0_gmv" json:"netT0Gmv" description:"净成交 GMV"`
|
||||
|
||||
// 原有字段
|
||||
PhotoName string `orm:"photo_name" json:"photoName" description:"视频名称"`
|
||||
PhotoIdStr string `orm:"photo_id_str" json:"photoIdStr" description:"视频 id"`
|
||||
PhotoId string `orm:"photo_id" json:"photoId" description:"视频 id"`
|
||||
AdItemClick *int64 `orm:"ad_item_click" json:"adItemClick" description:"行为数"`
|
||||
MerchantProductId string `orm:"merchant_product_id" json:"merchantProductId" description:"商品 ID"`
|
||||
CostTotal *float64 `orm:"cost_total" json:"costTotal" description:"花费"`
|
||||
EspClickRatio *float64 `orm:"esp_click_ratio" json:"espClickRatio" description:"行为率"`
|
||||
T0Gmv *float64 `orm:"t0_gmv" json:"t0Gmv" description:"当日累计 GMV"`
|
||||
T0Roi *float64 `orm:"t0_roi" json:"t0Roi" description:"当日累计 ROI"`
|
||||
T0OrderCnt *int64 `orm:"t0_order_cnt" json:"t0OrderCnt" description:"当日累计订单数"`
|
||||
T0OrderCntCost *float64 `orm:"t0_order_cnt_cost" json:"t0OrderCntCost" description:"当日累计订单成本"`
|
||||
QcpxWhiteboxDirectOrderPaymentAmt *float64 `orm:"qcpx_whitebox_direct_order_payment_amt" json:"qcpxWhiteboxDirectOrderPaymentAmt" description:"智能优惠券订单 GMV"`
|
||||
QcpxWhiteboxDirectOrderCnt *int64 `orm:"qcpx_whitebox_direct_order_cnt" json:"qcpxWhiteboxDirectOrderCnt" description:"智能优惠券订单数"`
|
||||
LivePlayCnt *int64 `orm:"live_play_cnt" json:"livePlayCnt" description:"全站直播观看数"`
|
||||
ItemEntranceClkCnt *int64 `orm:"item_entrance_clk_cnt" json:"itemEntranceClkCnt" description:"小黄车点击数"`
|
||||
ShowCnt *int64 `orm:"show_cnt" json:"showCnt" description:"全站曝光"`
|
||||
ReportDateStr string `orm:"report_date_str" json:"reportDateStr" description:"时间"`
|
||||
CampaignId *int64 `orm:"campaign_id" json:"campaignId" description:"计划 ID"`
|
||||
CampaignName string `orm:"campaign_name" json:"campaignName" description:"计划名称"`
|
||||
UnitId *int64 `orm:"unit_id" json:"unitId" description:"单元 ID"`
|
||||
UnitName string `orm:"unit_name" json:"unitName" description:"单元名称"`
|
||||
CreativeId *int64 `orm:"creative_id" json:"creativeId" description:"创意 ID"`
|
||||
CreativeName string `orm:"creative_name" json:"creativeName" description:"创意名称"`
|
||||
}
|
||||
|
||||
// StorewideReportSumCol 广告效果指标表表字段定义
|
||||
type StorewideReportSumCol struct {
|
||||
beans.SQLBaseCol
|
||||
T0OrderPaymentAmt string
|
||||
CreativeMaterialType string
|
||||
LiveName string
|
||||
AuthorId string
|
||||
PicUrl string
|
||||
PicName string
|
||||
PicId string
|
||||
CoverUrl string
|
||||
CoverId string
|
||||
ItemCardClkCnt string
|
||||
NetT0OrderCnt string
|
||||
NetT0Roi string
|
||||
NetT0Gmv string
|
||||
PhotoName string
|
||||
PhotoIdStr string
|
||||
PhotoId string
|
||||
AdItemClick string
|
||||
MerchantProductId string
|
||||
CostTotal string
|
||||
EspClickRatio string
|
||||
T0Gmv string
|
||||
T0Roi string
|
||||
T0OrderCnt string
|
||||
T0OrderCntCost string
|
||||
QcpxWhiteboxDirectOrderPaymentAmt string
|
||||
QcpxWhiteboxDirectOrderCnt string
|
||||
LivePlayCnt string
|
||||
ItemEntranceClkCnt string
|
||||
ShowCnt string
|
||||
ReportDateStr string
|
||||
CampaignId string
|
||||
CampaignName string
|
||||
UnitId string
|
||||
UnitName string
|
||||
CreativeId string
|
||||
CreativeName string
|
||||
}
|
||||
|
||||
// TableName 返回表名
|
||||
func (e *StorewideReportSum) TableName() string {
|
||||
return "storewide_report_sum"
|
||||
}
|
||||
|
||||
// GetCols 获取所有字段名
|
||||
func (e *StorewideReportSum) GetCols() *StorewideReportSumCol {
|
||||
return &StorewideReportSumCol{
|
||||
SQLBaseCol: beans.SQLBaseCol{
|
||||
Id: "id",
|
||||
CreatedAt: "created_at",
|
||||
UpdatedAt: "updated_at",
|
||||
},
|
||||
T0OrderPaymentAmt: "t0_order_payment_amt",
|
||||
CreativeMaterialType: "creative_material_type",
|
||||
LiveName: "live_name",
|
||||
AuthorId: "author_id",
|
||||
PicUrl: "pic_url",
|
||||
PicName: "pic_name",
|
||||
PicId: "pic_id",
|
||||
CoverUrl: "cover_url",
|
||||
CoverId: "cover_id",
|
||||
ItemCardClkCnt: "item_card_clk_cnt",
|
||||
NetT0OrderCnt: "net_t0_order_cnt",
|
||||
NetT0Roi: "net_t0_roi",
|
||||
NetT0Gmv: "net_t0_gmv",
|
||||
PhotoName: "photo_name",
|
||||
PhotoIdStr: "photo_id_str",
|
||||
PhotoId: "photo_id",
|
||||
AdItemClick: "ad_item_click",
|
||||
MerchantProductId: "merchant_product_id",
|
||||
CostTotal: "cost_total",
|
||||
EspClickRatio: "esp_click_ratio",
|
||||
T0Gmv: "t0_gmv",
|
||||
T0Roi: "t0_roi",
|
||||
T0OrderCnt: "t0_order_cnt",
|
||||
T0OrderCntCost: "t0_order_cnt_cost",
|
||||
QcpxWhiteboxDirectOrderPaymentAmt: "qcpx_whitebox_direct_order_payment_amt",
|
||||
QcpxWhiteboxDirectOrderCnt: "qcpx_whitebox_direct_order_cnt",
|
||||
LivePlayCnt: "live_play_cnt",
|
||||
ItemEntranceClkCnt: "item_entrance_clk_cnt",
|
||||
ShowCnt: "show_cnt",
|
||||
ReportDateStr: "report_date_str",
|
||||
CampaignId: "campaign_id",
|
||||
CampaignName: "campaign_name",
|
||||
UnitId: "unit_id",
|
||||
UnitName: "unit_name",
|
||||
CreativeId: "creative_id",
|
||||
CreativeName: "creative_name",
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user