@guandata/guanwf 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ ## @guandata/guanwf 0.1.3 - 2026-05-28
4
+
5
+ - 优化试用账号场景下的运行上下文识别,提升命令执行记录与问题排查的一致性。
6
+ - 重新构建发布产物,纳入共享运行上下文处理更新。
7
+
8
+ ## @guandata/guanwf 0.1.2 - 2026-05-26
9
+
10
+ - 构建流程会将 npm 包版本注入到 `guanwf` 二进制,确保运行时版本信息与发布包一致。
11
+ - 改进工作流数据流命令的 profile 与版本上下文传递,提升多环境排查时的一致性。
12
+ - 补充相关测试覆盖,提升数据流命令辅助逻辑稳定性。
13
+
14
+ ## @guandata/guanwf 0.1.1 - 2026-05-21
15
+
16
+ - 强化工作流数据流编辑的源文件驱动流程,明确修改应落在 `etl/etl.go`、`etl/*.sql` 或被引用的 `etl/node_*.json`。
17
+ - 明确 `_exported.json`、`_parent_snapshot.json`、`_input.json` 等根目录 JSON 为系统状态或派生产物,不应手工修改。
18
+ - 优化新建、编辑、导出、预览、保存的固定工作顺序说明,降低保存时覆盖父工作流其他数据流的风险。
19
+ - 调整 create/export 相关行为说明,使新建数据流默认通过 `etl/` 事实源生成。
20
+
21
+ ## @guandata/guanwf 0.1.0 - 2026-05-20
22
+
23
+ - 新增 `@guandata/guanwf` npm 发布包,提供构建、预检、安装同步与跨平台二进制分发流程。
24
+ - 支持 workflow platform 的导入/导出闭环,完善工作流数据流编辑与发布链路。
25
+ - 扩展工作流数据流节点预览能力,并补充节点与 ETL AI 开发参考文档。
26
+ - 调整用户侧命令与文档命名,移除 `-skill` 后缀并清理旧引用。
27
+ - 改进跨平台运行体验,包括 Linux 构建目标与 Windows 控制台 UTF-8 输出兼容。
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # guanwf
2
+
3
+ 观远工作流数据流编辑工具,支持创建、编辑、导出、预览、保存数据流。
4
+
5
+ ## 本地安装(开发/测试阶段)
6
+
7
+ ```bash
8
+ # 编译所有平台 binary(需要 Go 工具链)
9
+ npm run build
10
+
11
+ # 安装到本地 node global
12
+ npm link
13
+ ```
14
+
15
+ 安装后即可在终端使用:
16
+
17
+ ```bash
18
+ guanwf create --name "我的数据流" --parent-dir <dirId>
19
+ guanwf edit <parentWorkflowId>
20
+ guanwf export --dir <workdir>
21
+ guanwf preview --dir <workdir>
22
+ guanwf save --dir <workdir>
23
+ guanwf run --wait --dir <workdir>
24
+ ```
25
+
26
+ 说明:npm 包名为 `@guandata/guanwf`,用户侧 CLI 命令统一为 `guanwf`。
27
+
28
+ 也可以为 AI Coding Assistant 安装 Skill:
29
+
30
+ ```bash
31
+ guanwf install-skill
32
+ ```
33
+
34
+ ## 版本更新
35
+
36
+ ### 0.1.3
37
+
38
+ - 优化试用账号场景下的运行上下文识别,提升命令执行记录与问题排查的一致性。
39
+ - 重新构建发布产物,纳入共享运行上下文处理更新。
40
+
41
+ ## 卸载
42
+
43
+ ```bash
44
+ npm unlink -g @guandata/guanwf
45
+ ```
46
+
47
+ ## 支持平台
48
+
49
+ - macOS (Apple Silicon / Intel)
50
+ - Linux (x64 / arm64)
51
+ - Windows (x64)
52
+
53
+ ## 开发
54
+
55
+ ```bash
56
+ # 编译所有平台 binary(需要 Go 工具链)
57
+ npm run build
58
+
59
+ # 发布到内部 Nexus npm 仓库
60
+ npm publish
61
+ ```
package/bin/run.js ADDED
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env node
2
+
3
+ "use strict";
4
+
5
+ const { execFileSync, execSync, spawnSync } = require("child_process");
6
+ const path = require("path");
7
+ const fs = require("fs");
8
+
9
+ const PLATFORM_MAP = {
10
+ "darwin-arm64": "guanwf-darwin-arm64",
11
+ "darwin-x64": "guanwf-darwin-x64",
12
+ "linux-x64": "guanwf-linux-x64",
13
+ "linux-arm64": "guanwf-linux-arm64",
14
+ "win32-x64": "guanwf-win32-x64.exe",
15
+ };
16
+
17
+ function getBinaryPath() {
18
+ const key = `${process.platform}-${process.arch}`;
19
+ const binaryName = PLATFORM_MAP[key];
20
+ if (!binaryName) {
21
+ console.error(
22
+ `Unsupported platform: ${key}\nSupported: ${Object.keys(PLATFORM_MAP).join(", ")}`
23
+ );
24
+ process.exit(1);
25
+ }
26
+
27
+ const binaryPath = path.join(__dirname, "..", "binaries", binaryName);
28
+ if (!fs.existsSync(binaryPath)) {
29
+ console.error(
30
+ `Binary not found: ${binaryPath}\nRun 'npm run build' to compile binaries.`
31
+ );
32
+ process.exit(1);
33
+ }
34
+
35
+ return binaryPath;
36
+ }
37
+
38
+ if (process.argv[2] === "version" && process.argv.length === 3) {
39
+ const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, "..", "package.json"), "utf8"));
40
+ console.log(pkg.version);
41
+ process.exit(0);
42
+ }
43
+
44
+ if (process.argv[2] === "install-skill") {
45
+ const pkgRoot = path.join(__dirname, "..");
46
+ const extraArgs = process.argv.slice(3);
47
+ const args = [
48
+ "skills",
49
+ "add",
50
+ pkgRoot,
51
+ "--skill",
52
+ "guanwf",
53
+ "-g",
54
+ "-y",
55
+ ...extraArgs,
56
+ ];
57
+ console.log("Installing guanwf to AI coding assistants...");
58
+ const result = spawnSync("npx", args, { stdio: "inherit", env: process.env, shell: true });
59
+ if (result.error) throw result.error;
60
+ process.exit(result.status || 0);
61
+ }
62
+
63
+ const binary = getBinaryPath();
64
+
65
+ try {
66
+ if (process.platform !== "win32") {
67
+ fs.chmodSync(binary, 0o755);
68
+ }
69
+ } catch (_) {
70
+ // chmod may fail in read-only environments
71
+ }
72
+
73
+ if (process.platform === "win32") {
74
+ try { execSync("chcp 65001", { stdio: "ignore" }); } catch (_) {}
75
+ }
76
+
77
+ try {
78
+ execFileSync(binary, process.argv.slice(2), {
79
+ stdio: "inherit",
80
+ env: { ...process.env, GUANWF_PROG_NAME: "guanwf" },
81
+ });
82
+ } catch (err) {
83
+ if (err.status != null) {
84
+ process.exit(err.status);
85
+ }
86
+ throw err;
87
+ }
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@guandata/guanwf",
3
+ "version": "0.1.3",
4
+ "description": "观远工作流数据流编辑工具 - 创建、编辑、导出、预览、保存数据流",
5
+ "bin": {
6
+ "guanwf": "bin/run.js"
7
+ },
8
+ "files": [
9
+ "bin/",
10
+ "binaries/",
11
+ "skills/",
12
+ "CHANGELOG.md",
13
+ "README.md"
14
+ ],
15
+ "keywords": [
16
+ "guandata",
17
+ "workflow",
18
+ "dataflow",
19
+ "cli",
20
+ "agent-skill"
21
+ ],
22
+ "license": "UNLICENSED",
23
+ "os": [
24
+ "darwin",
25
+ "linux",
26
+ "win32"
27
+ ],
28
+ "engines": {
29
+ "node": ">=14"
30
+ },
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }
@@ -0,0 +1,296 @@
1
+ ---
2
+ name: guanwf
3
+ description: 当用户要创建、编辑、保存工作流引擎中的数据流(Dataflow),或需要预览/运行数据流,或需要查询工作流/数据流列表和详情时使用。数据流是 BI ETL 的扩展,运行在独立的工作流引擎中。适用于用户说"创建一个数据流""编辑这个工作流中的数据流""保存数据流""预览数据流节点""运行工作流""列出所有数据流""查看这个工作流的定义"等场景。
4
+ compatibility: "Requires Node.js 14+. Install via npm link (local) or npm install -g @guandata/guanwf (from internal Nexus registry). CLI command: guanwf."
5
+ ---
6
+
7
+ # guanwf
8
+
9
+ 工作流引擎数据流 (Dataflow) 的编辑闭环工具。
10
+
11
+ 这是一个执行型 skill,不是只读分析 skill。
12
+
13
+ - `guancli workflow` 负责查询工作流、数据流、目录和线上结构。
14
+ - `guanwf` 负责把目标数据流拉到本地工作目录,修改 `etl/` 事实源,再完成 `export -> preview -> save-draft/save -> run`。
15
+
16
+ ## 核心概念
17
+
18
+ - **工作流 (Workflow / PROCESS)**: 顶层容器,包含任务节点 DAG
19
+ - **数据流 (Dataflow / DATAFLOW)**: 嵌入在工作流中的 ETL 流程,actions 结构与 BI ETL 相同
20
+ - **数据库数据流 (DB_DATAFLOW)**: 数据流的变体,支持 SQL 下推
21
+ - **父工作流**: 每个数据流必须属于一个父工作流,保存时必须提交完整的父工作流上下文
22
+
23
+ ## 关键不变式
24
+
25
+ **保存数据流时,必须携带完整的父工作流快照(包含所有 dataflowJson)。**
26
+ 如果只提交被编辑的 dataflow,后端会删除父工作流中其他数据流的定义。
27
+
28
+ ## Harness 工作法:源文件驱动,不手写最终 JSON
29
+
30
+ `guanwf` 复用 `guanetl` 的本地 harness 思路:先改可读、可复现的源文件,再用明确命令重新生成派生产物。不要为了让保存请求看起来正确而直接改最终 JSON。
31
+
32
+ **如果任务是生成或修改数据流,但本轮没有修改下面任一事实源,不要汇报完成,也不要直接 save:**
33
+
34
+ - `etl/etl.go`
35
+ - `etl/*.sql`
36
+ - `etl/node_*.json`(仅限被 `etl.go` 里的 `LoadNodeFromFile(...)` 引用的透传节点)
37
+
38
+ 查询、预览、运行、排查已有导出结果时可以不修改源文件;但只要目标是“生成/调整数据流逻辑”,最终变更必须落在 `etl/` 事实源中。
39
+
40
+ ### Artifact contract(本地可测契约)
41
+
42
+ 工作目录中 `etl/` 是可编辑事实源。导入、导出、预览、保存过程产生的根目录 JSON 是系统状态或派生产物。
43
+
44
+ | 路径/产物 | 类型 | 规则 |
45
+ |---|---|---|
46
+ | `etl/etl.go` | 可编辑事实源(默认首选) | 定义 `DefineETL()`,使用 guanetl framework DSL 声明节点、依赖和叶子输出 |
47
+ | `etl/*.sql` | 可编辑事实源 | 仅供 `BasicSqlScript(..., ReadSQLFile(...))` 或数据源 SQL 配置引用;新增 SQL 节点必须同步新增文件 |
48
+ | `etl/node_*.json` | 可编辑事实源(透传节点) | 仅用于 `LoadNodeFromFile(...)` 引用的复杂节点配置;改透传节点时改这里,不改 `_exported.json` |
49
+ | `etl/actions.json` | 历史兼容输入 | 不作为 AI 编辑入口;新建/修改数据流不要写这个文件 |
50
+ | `_wf_state.json` | 系统状态 | 不手改 |
51
+ | `_parent_snapshot.json` | 服务端快照 | 不手改;save 时会重新拉取线上最新版本并合并 |
52
+ | `_input.json` | 服务端快照 | 不手改;仅用于 `edit` 导入排查,不是新建数据流的输入配置入口 |
53
+ | `_exported.json` | 派生产物 | 不手改;由 `export` 从 `etl/` 重新生成 |
54
+
55
+ 默认工作方式:编写 `etl/etl.go`、`etl/*.sql` 和必要的 `etl/node_*.json`,然后执行 `export`。`export` 检测到 `etl.go` 后会通过 `guanetl go2json`(yaegi 解释器)生成 actions JSON,并写入 `_exported.json`。
56
+
57
+ 输入信息写在 `etl/` 事实源中:
58
+
59
+ - 数据集输入:在 `etl/etl.go` 里用 `BasicInputDataset(..., dsId, []Field{...})` 声明数据集 ID 和字段 schema。
60
+ - 数据库直连输入:优先在 `etl/etl.go` 里用 `BasicInputDatasource(...)` 声明账号、连接和 SQL;复杂导入节点才编辑被 `LoadNodeFromFile(...)` 引用的 `etl/node_*.json`。
61
+ - SQL 转换:SQL 放在 `etl/*.sql`,并由 `ReadSQLFile(...)` 引用。
62
+ - `_input.json` 是导入快照,不参与新建数据流配置。
63
+
64
+ ### 禁止的偷懒路径
65
+
66
+ 不要做这些事:
67
+
68
+ - 不要直接编辑 `_exported.json` 来“修好”预览或保存。
69
+ - 不要直接编辑 `_parent_snapshot.json` 或 `_input.json` 来拼保存 payload。
70
+ - 不要把 `guancli workflow get --raw` 的结果复制成最终 JSON 后手改。
71
+ - 不要把新建/修改需求写进 `etl/actions.json`。
72
+ - 不要跳过 `export`,拿上一次的 `_exported.json` 去 `preview` 或 `save`。
73
+
74
+ 如果 `export`、`preview` 或 `save` 失败,修复顺序固定为:读失败输出 -> 判断是 `etl.go`、SQL、透传节点 JSON、认证/网络还是线上任务问题 -> 修改 `etl/` 中最小责任源文件 -> 从失败步骤往后重跑。不要通过手改根目录 JSON 绕过失败。
75
+
76
+ ## 认证
77
+
78
+ 复用 guancli 的认证链路:
79
+ ```bash
80
+ guancli auth login # 先确保已登录
81
+ guancli auth status # 检查连接状态
82
+ ```
83
+
84
+ ## 固定工作方式
85
+
86
+ ### 0. 先分场景
87
+
88
+ - `只读查询`: 用 `guancli workflow`,不要创建工作目录。
89
+ - `编辑已有数据流`: 用 `guanwf edit <parentWorkflowId>` 拉取父工作流和目标数据流。
90
+ - `新建数据流`: 用 `guanwf create --name ... --parent-dir ...` 建工作目录,然后新建 `etl/etl.go`。
91
+ - `修复失败`: 先定位失败发生在 `export`、`preview`、`save` 还是 `run`,只修最小责任源文件。
92
+
93
+ ### 1. 缺上下文先查,不要猜
94
+
95
+ 查询工作流和数据流定义时使用 `guancli workflow`。工作流/数据流由独立的 workflow 引擎管理。
96
+
97
+ ```bash
98
+ guancli workflow tree # 工作流目录树
99
+ guancli workflow list # 列出工作流 + 内嵌数据流(默认)
100
+ guancli workflow list --show-embedded=false # 只列工作流,不展示内嵌数据流
101
+ guancli workflow list 销售 # 按关键词搜索
102
+ guancli workflow list --page-size 50 # 调整每页条数
103
+ guancli workflow list --parent-dir <dirId> # 指定父目录
104
+ guancli workflow get <processDefinitionId> # 查看工作流详情(含数据流节点详情)
105
+ guancli workflow get <id> --raw # 输出原始 JSON
106
+ guancli workflow get <id> -f json # JSON 格式输出
107
+ ```
108
+
109
+ `workflow tree` 调用 `/api/directory/MASTER_FLOW/authorized-tree` 获取工作流目录树。
110
+
111
+ 数据流以 SUB_PROCESS 节点形式内嵌在工作流中(`dataFlowNode=true`),不作为独立记录存在。
112
+ `list` 默认展示内嵌数据流(遍历每个工作流并汇总),使用 `--show-embedded=false` 可关闭。
113
+
114
+ `get` 详情输出包含:工作流节点 DAG(区分 DATAFLOW/DB_DATAFLOW/SUB_PROCESS 节点类型)、数据流摘要(节点数/类型分布/输入输出),以及各节点的详细信息(Sources/UsedBy 依赖关系、字段、SQL 等),按拓扑排序输出(INPUT → 中间处理 → OUTPUT)。
115
+
116
+ ### 2. 真正改文件前先读这些
117
+
118
+ 至少先读:
119
+
120
+ - `etl/etl.go`
121
+ - `etl/*.sql`
122
+ - `etl/node_*.json`
123
+ - `references/ETL_AI_DEVELOP.md`
124
+ - `references/WORKFLOW_NODES.md`
125
+
126
+ 必要时再读:
127
+
128
+ - `_input.json`(只读,用于排查导入前结构)
129
+ - `_exported.json`(只读,用于对比导出结果)
130
+ - `guancli workflow get <parentWorkflowId> -f json` 的线上结构
131
+
132
+ ### 3. 只改 `etl/`
133
+
134
+ 只允许修改这些文件:
135
+
136
+ - `etl/etl.go`
137
+ - `etl/*.sql`
138
+ - `etl/node_*.json`
139
+
140
+ 不要手改这些系统文件:
141
+
142
+ - `_workspace_state.json`
143
+ - `_wf_state.json`
144
+ - `_parent_snapshot.json`
145
+ - `_input.json`
146
+ - `_exported.json`
147
+
148
+ ### 4. 改完永远按这个顺序
149
+
150
+ 1. `guanwf export --dir <workdir>`
151
+ 2. 需要看数据结果时再 `guanwf preview --dir <workdir>` 或 `guanwf preview <nodeId> --dir <workdir>`
152
+ 3. 确认无误后才 `guanwf save-draft --dir <workdir>` 或 `guanwf save --dir <workdir>`
153
+ 4. 需要执行时再 `guanwf run --wait --dir <workdir>`
154
+
155
+ 如果 `export` 没过,不要直接 `preview` 或 `save`。
156
+
157
+ ## 三种默认流程
158
+
159
+ ### 编辑已有数据流
160
+
161
+ ```bash
162
+ # 获取数据流(参数必须是父工作流 ID,自动保存父工作流 snapshot)
163
+ guanwf edit <parentWorkflowId> # 仅含一个数据流时自动选中
164
+ guanwf edit <parentWorkflowId> --dataflow-id <dfId> # 指定数据流
165
+
166
+ # 在 etl/ 下修改 etl.go、*.sql、必要的 node_*.json
167
+ # 不要改 _input.json、_parent_snapshot.json 或 _exported.json
168
+
169
+ # 导出验证(有 etl.go 时自动走 yaegi runner)
170
+ guanwf export --dir <workdir>
171
+
172
+ # 预览节点
173
+ guanwf preview --dir <workdir>
174
+ guanwf preview <nodeId> --dir <workdir>
175
+
176
+ # 保存草稿或发布
177
+ guanwf save-draft --dir <workdir>
178
+ guanwf save --dir <workdir>
179
+ ```
180
+
181
+ `edit` 会把线上 actions 导入为 `etl.go`。复杂工作流节点可能被导入为 `etl/node_*.json` 并在 `etl.go` 中以 `LoadNodeFromFile(...)` 引用;这种 `node_*.json` 是可编辑源文件。
182
+
183
+ ### 新建数据流
184
+
185
+ ```bash
186
+ # 创建(自动生成父工作流包裹)
187
+ guanwf create --name "我的数据流" --parent-dir <dirId>
188
+ guanwf create --name "DB数据流" --type DB_DATAFLOW --parent-dir <dirId>
189
+
190
+ # 新建 etl/etl.go,按 references/ETL_AI_DEVELOP.md 编写 DefineETL()
191
+
192
+ # 导出 -> 保存
193
+ guanwf export --dir <workdir>
194
+ guanwf save-draft --dir <workdir>
195
+ ```
196
+
197
+ 新建场景尤其不要把需求直接写进 `etl/actions.json`。应创建 `etl/etl.go`,把节点、依赖、输入、输出都放进 `DefineETL()`;输入数据集 ID 和字段 schema 写在 `BasicInputDataset(...)`,不是 `_input.json`。
198
+
199
+ ### 修复失败
200
+
201
+ 1. 先确认失败发生在 `export`、`preview`、`save` 还是 `run`。
202
+ 2. 如果失败来自 `export`,只改 `etl.go`、SQL 或被引用的 `node_*.json`。
203
+ 3. 如果失败来自 `preview`,先确认 `_exported.json` 是否由最新 `etl/` 生成;必要时先重跑 `export`。
204
+ 4. 如果失败来自 `save`,不要手写保存 payload;检查父工作流是否能重新拉取、目标 dataflow 是否还存在、以及本地 `_exported.json` 是否来自最新源文件。
205
+ 5. 从失败步骤往后重跑,不要把整个工作目录推倒重来。
206
+
207
+ ### 运行工作流
208
+
209
+ ```bash
210
+ guanwf run --dir <workdir> # 触发运行
211
+ guanwf run --wait --dir <workdir> # 等待完成
212
+ guanwf run --wait --timeout 600 --dir <workdir> # 自定义超时
213
+ ```
214
+
215
+ ## 工作目录结构
216
+
217
+ ```
218
+ <workdir>/
219
+ _workspace_state.json # etlworkspace 通用状态
220
+ _wf_state.json # 工作流特有状态(parentId, dataflowId, isNew 等)
221
+ _parent_snapshot.json # 父工作流完整快照(保存时使用)
222
+ _input.json # 原始输入(runner 格式)
223
+ _exported.json # export 导出结果(派生产物,不手改)
224
+ etl/
225
+ etl.go # 数据流节点定义(推荐,yaegi DSL)
226
+ *.sql # SQL 节点的独立 SQL 文件
227
+ node_*.json # LoadNodeFromFile 透传节点配置
228
+ actions.json # 历史兼容输入;新建/修改不要编辑
229
+ ```
230
+
231
+ ## save 的工作机制
232
+
233
+ `save` / `save-draft` 不是简单把本地 JSON 上传。内部流程是:
234
+
235
+ 1. 读取本地 `_exported.json`(必须由 `export` 从 `etl/` 生成)
236
+ 2. 从服务端重新拉取父工作流最新版本
237
+ 3. 用字段级合并把目标 dataflow actions 替换进父工作流完整上下文
238
+ 4. 调用 `/process/save-draft` 或 `/process/save`
239
+ 5. 保存成功后刷新本地 parent snapshot
240
+
241
+ 因此用户和 AI 都不需要手写服务端保存 payload。只要 `etl/` 事实源正确、`export` 通过、父工作流仍可拉取,保存流程会保留同一父工作流里的其他数据流。
242
+
243
+ ## API 路由
244
+
245
+ 所有 API 通过 `/api/offline-dev/*` 前缀走 BFF 代理到工作流引擎。
246
+
247
+ | 操作 | 方法 | 路径 |
248
+ |------|------|------|
249
+ | 分页查询 | POST | `/api/offline-dev/process/list-paging` |
250
+ | 查询详情 | GET | `/api/offline-dev/process/{id}/select-by-id` |
251
+ | 保存草稿 | POST | `/api/offline-dev/process/save-draft` |
252
+ | 保存发布 | POST | `/api/offline-dev/process/save` |
253
+ | 预览数据流 | POST | `/api/offline-dev/dataflow/preview-async` |
254
+ | 查询预览任务 | GET | `/api/offline-dev/dataflow/task/{taskId}` |
255
+ | 取消预览 | POST | `/api/offline-dev/dataflow/task/{taskId}/cancel` |
256
+ | 运行工作流 | POST | `/api/offline-dev/process/{id}/start-process-instance` |
257
+ | 查询运行实例 | GET | `/api/offline-dev/process/instance/{id}/select-by-id` |
258
+
259
+ ## 与其他 skill 的关系
260
+
261
+ | 任务 | 工具 |
262
+ |------|------|
263
+ | BI ETL 编辑 | guanetl |
264
+ | 工作流/数据流只读查询 | guancli workflow(隐藏子命令,需手动输入) |
265
+ | 数据流编辑闭环 | guanwf(本工具) |
266
+ | 数据集管理 | guands |
267
+ | 通用 ETL 节点函数参考 | guanwf/references/ETL_AI_DEVELOP.md(symlink → guanetl) |
268
+ | 工作流扩展节点参考 | guanwf/references/WORKFLOW_NODES.md |
269
+
270
+ ## 工作流特有节点
271
+
272
+ 工作流数据流是 BI ETL 的超集,额外支持以下节点类型:
273
+
274
+ | 节点类型 | 函数 | 说明 |
275
+ |---|---|---|
276
+ | `INPUT_DATASOURCE` | `LoadNodeFromFile` / `BasicInputDatasource` | 数据库直连输入(无上游) |
277
+ | `OUTPUT_DATASOURCE` | `LoadNodeFromFile` / `BasicOutputDatasource` | 数据回写到数据库 |
278
+ | `INCREMENT_OUTPUT_DATASET` | `LoadNodeFromFile` / `BasicIncrementOutputDataset` | 增量输出数据集 |
279
+ | `TRIM_COLUMNS` | `BasicTrimColumns` | 去空格 |
280
+ | `VALUE_MAPPER` | `LoadNodeFromFile` | 值映射/替换 |
281
+ | `SINGLE_VALUE_MAPPER` | `LoadNodeFromFile` | Null 值替换 |
282
+ | `COMBINE_COLUMNS` | `BasicCombineColumns` | 合并列 |
283
+
284
+ `guanwf edit` 导入时:
285
+ - INPUT_DATASOURCE / OUTPUT_DATASOURCE / INCREMENT_OUTPUT_DATASET / VALUE_MAPPER / SINGLE_VALUE_MAPPER → 保存为 `node_<id>.json` + `LoadNodeFromFile("node_<id>.json")`
286
+ - TRIM_COLUMNS / COMBINE_COLUMNS → 直接生成结构化 Go DSL 代码
287
+
288
+ 详细配置参数见 `references/WORKFLOW_NODES.md`。
289
+
290
+ ## 注意事项
291
+
292
+ - 编辑时自动获取并保存父工作流 snapshot,save 时从服务端重新获取最新版本
293
+ - actions 结构与 BI ETL 相同,节点函数和约束复用 guanetl 的 framework
294
+ - DB_DATAFLOW 涉及 SQL 下推和单账号约束,后续可能需要额外处理
295
+ - `guancli workflow` 子命令在 `guancli --help` 中不显示(隐藏命令),但手动输入可正常使用
296
+ - 透传节点(`LoadNodeFromFile`)和结构化节点可互换使用,框架不限制
@@ -0,0 +1,281 @@
1
+ # GuanETL AI 开发参考
2
+
3
+ 这份参考只保留 AI 真正常用、且最容易写错的部分。
4
+
5
+ ## 先读什么
6
+
7
+ 写 ETL 前,先按这个顺序看:
8
+
9
+ 1. `etl/etl.go`
10
+ 2. `etl/*.sql`
11
+ 3. 必要时 `etl/meta.json`
12
+ 4. 缺上下文时,用 `guancli` 查 ETL / ds / 字段,不要猜
13
+
14
+ ## 不可违反的规则
15
+
16
+ ```go
17
+ import . "guanetl/internal/framework"
18
+
19
+ func DefineETL() []Node
20
+ ```
21
+
22
+ - 不要改点导入和 `DefineETL()` 签名。
23
+ - 返回值必须是叶子节点数组,通常是一个或多个 `OUTPUT_DATASET`。
24
+ - 节点 ID 必须唯一,格式必须是 `id_数字`。
25
+ - 下游节点必须写在上游节点之后。
26
+ - 只改 `etl/` 目录,不要手改 `_base_etl.json`、`_input.json`、`_exported.json`。
27
+ - 对 `edit` 导入出来的 ETL,优先局部修改,尽量保留现有节点顺序和文件命名。
28
+
29
+ ## 节点选择顺序
30
+
31
+ 优先用专用节点,最后才用 SQL:
32
+
33
+ 1. `BasicInputDataset`
34
+ 2. `BasicSelectColumns`
35
+ 3. `BasicFilterRows`
36
+ 4. `BasicJoinData`
37
+ 5. `BasicGroupBy`
38
+ 6. `BasicCalculator`
39
+ 7. `BasicRemoveDuplicates`
40
+ 8. `BasicAppendRows`
41
+ 9. `BasicSqlScript`
42
+
43
+ 特别注意:
44
+
45
+ - 等值 JOIN 不要写成 SQL,优先 `JOIN_DATA`。
46
+ - 只是筛选、选列、聚合、去重时,不要默认上 SQL。
47
+ - SQL 很长或有很多 CTE 时,优先拆成多个节点。
48
+
49
+ ## 输入字段 schema 放哪里
50
+
51
+ 输入字段 schema 以 `BasicInputDataset(..., []Field{...})` 为主。
52
+
53
+ ```go
54
+ orders := BasicInputDataset("id_1001", "订单", "ds_orders", []Field{
55
+ BasicField("user_id", "STRING"),
56
+ BasicField("amount", "DOUBLE"),
57
+ BasicField("order_date", "DATE"),
58
+ }, Position{X: 100, Y: 100})
59
+ ```
60
+
61
+ - `go run ./cmd/guanetl export` 会把这里的字段带到最终 JSON。
62
+ - `meta.json` 主要保留 ETL 名称等元信息。
63
+ - 字段相关报错时,优先检查 `[]Field`,不要先怀疑 `meta.json`。
64
+
65
+ ## 最常用函数
66
+
67
+ ### 输入 / 输出
68
+
69
+ ```go
70
+ BasicInputDataset(id, name, inputDsID string, fields []Field, position Position)
71
+ BasicOutputDataset(id, name string, source Node, outputDsName string, position Position)
72
+ BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
73
+ ```
74
+
75
+ `BasicOutputDatasetInDir` 允许指定输出数据集的父目录 ID,避免落到根目录。目录 ID 可通过 `guancli ds tree` 获取。
76
+
77
+ ### 选列
78
+
79
+ ```go
80
+ BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, position Position)
81
+ ```
82
+
83
+ ```go
84
+ []ColumnSetting{
85
+ BasicColumnSetting("user_id"),
86
+ {Name: "amount", NewName: "pay_amount"},
87
+ }
88
+ ```
89
+
90
+ ### 筛选
91
+
92
+ ```go
93
+ BasicFilterRows(id, name string, source Node, combineType string, conditions []FilterCondition, position Position)
94
+ ```
95
+
96
+ - `combineType` 只能是 `AND` 或 `OR`
97
+ - 高频操作符:`EQ` `NE` `LT` `LE` `GT` `GE` `IN` `BT` `IS_NULL` `NOT_NULL` `CONTAINS` `NOT_CONTAINS` `STARTSWITH` `NOT_STARTSWITH` `ENDSWITH` `NOT_ENDSWITH`
98
+
99
+ ### 等值 JOIN
100
+
101
+ ```go
102
+ BasicJoinData(id, name string, leftSource, rightSource Node, joinType string, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
103
+ ```
104
+
105
+ - `joinType` 只能是 `INNER` `LEFT_OUTER` `RIGHT_OUTER` `FULL_OUTER`
106
+
107
+ ### 聚合
108
+
109
+ ```go
110
+ BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
111
+ ```
112
+
113
+ - 聚合类型只用:`SUM` `COUNT` `COUNT_DISTINCT` `MIN` `MAX` `AVG` `FIRST_NOT_NULL`
114
+
115
+ ### SQL
116
+
117
+ ```go
118
+ BasicSqlScript(id, name string, sources []Node, sql string, position Position)
119
+ ```
120
+
121
+ 推荐把 SQL 放到独立文件:
122
+
123
+ ```go
124
+ node := BasicSqlScript(
125
+ "id_1005",
126
+ "复杂转换",
127
+ []Node{inputA, inputB},
128
+ ReadSQLFile("node_1005.sql"),
129
+ Position{X: 900, Y: 100},
130
+ )
131
+ ```
132
+
133
+ SQL 规则:
134
+
135
+ - 上游节点在 SQL 中用 `input1`、`input2` 这类名字引用。
136
+ - 特殊列名、中文列名、带空格列名,用反引号。
137
+ - 新增 SQL 节点时,要同步新增 `.sql` 文件。
138
+
139
+ ## 推荐骨架
140
+
141
+ ```go
142
+ package main
143
+
144
+ import . "guanetl/internal/framework"
145
+
146
+ func DefineETL() []Node {
147
+ inputA := BasicInputDataset("id_1001", "输入A", "ds_a", []Field{
148
+ BasicField("id", "STRING"),
149
+ }, Position{X: 100, Y: 100})
150
+
151
+ transformed := BasicFilterRows(
152
+ "id_1002",
153
+ "筛选A",
154
+ inputA,
155
+ "AND",
156
+ []FilterCondition{},
157
+ Position{X: 320, Y: 100},
158
+ )
159
+
160
+ output := BasicOutputDataset("id_1003", "输出", transformed, "结果数据集", Position{X: 560, Y: 100})
161
+ return []Node{output}
162
+ }
163
+ ```
164
+
165
+ ## 每次改完都检查
166
+
167
+ 1. `DefineETL()` 返回的是叶子节点吗?
168
+ 2. 新节点 ID 唯一吗?格式对吗?
169
+ 3. 节点顺序符合依赖吗?
170
+ 4. `JOIN_DATA` / `GROUP_BY` / `FILTER_ROWS` 的枚举值合法吗?
171
+ 5. 新增 SQL 文件了吗?文件名和代码引用一致吗?
172
+ 6. 输入字段 schema 足够支撑后续节点吗?
173
+
174
+ ## 验证顺序
175
+
176
+ 始终按这个顺序:
177
+
178
+ 1. `go run ./cmd/guanetl export --dir <work_dir>`
179
+ 2. 需要看结果时:`go run ./cmd/guanetl preview <node_id> --dir <work_dir>`
180
+ 3. 确认无误后:`go run ./cmd/guanetl save --dir <work_dir>`
181
+
182
+ 如果 `export` 没过,不要直接 `save`。
183
+
184
+ ## Appendix: Framework Surface
185
+
186
+ 这部分只列 AI 写 ETL 时最值得记住的公开函数和合法枚举。
187
+ 源码真相在 runner framework 中,但不要把源码实现细节当成日常写法。
188
+
189
+ ### 推荐优先使用的 Basic 函数
190
+
191
+ ```go
192
+ BasicInputDataset(id, name, inputDsID string, fields []Field, position Position)
193
+ BasicOutputDataset(id, name string, source Node, outputDsName string, position Position)
194
+ BasicOutputDatasetInDir(id, name string, source Node, outputDsName, parentDirId string, position Position)
195
+ BasicSelectColumns(id, name string, source Node, columns []ColumnSetting, position Position)
196
+ BasicFilterRows(id, name string, source Node, combineType string, conditions []FilterCondition, position Position)
197
+ BasicJoinData(id, name string, leftSource, rightSource Node, joinType string, joinColumns []JoinColumnPair, outputColumns []JoinOutputColumn, position Position)
198
+ BasicGroupBy(id, name string, source Node, groupByColumns []GroupByColumn, aggregationColumns []AggregationColumn, position Position)
199
+ BasicCalculator(id, name string, source Node, formulas []Formula, position Position)
200
+ BasicRemoveDuplicates(id, name string, source Node, columnNames []string, position Position)
201
+ BasicAppendRows(id, name string, sources []Node, unionType string, schemaSource string, position Position)
202
+ BasicSqlScript(id, name string, sources []Node, sql string, position Position)
203
+ ```
204
+
205
+ ### 只有需要完整配置时再用的 New 函数
206
+
207
+ ```go
208
+ NewInputDataset(id, name string, config InputDatasetConfig)
209
+ NewOutputDataset(id, name string, source Node, config OutputDatasetConfig)
210
+ NewSelectColumns(id, name string, source Node, config SelectColumnsConfig)
211
+ NewFilterRows(id, name string, source Node, config FilterRowsConfig)
212
+ NewJoinData(id, name string, leftSource, rightSource Node, config JoinDataConfig)
213
+ NewGroupBy(id, name string, source Node, config GroupByConfig)
214
+ NewCalculator(id, name string, source Node, config CalculatorConfig)
215
+ NewRemoveDuplicates(id, name string, source Node, config RemoveDuplicatesConfig)
216
+ NewAppendRows(id, name string, sources []Node, config AppendRowsConfig)
217
+ NewSqlScript(id, name string, sources []Node, config SqlScriptConfig)
218
+ ```
219
+
220
+ 默认优先 `Basic*`,除非你明确需要手动控制更底层配置。
221
+
222
+ ### 高频辅助函数
223
+
224
+ ```go
225
+ BasicField(name, fieldType string)
226
+ BasicColumnSetting(name string)
227
+ BasicJoinColumnPair(leftColumn, rightColumn string)
228
+ BasicJoinOutputFromLeft(columnName string)
229
+ BasicJoinOutputFromRight(columnName string)
230
+ BasicJoinOutputFromLeftWithAlias(columnName, alias string)
231
+ BasicJoinOutputFromRightWithAlias(columnName, alias string)
232
+ BasicGroupByColumn(name, columnType string)
233
+ BasicGroupByColumnWithAlias(name, columnType, newName string)
234
+ BasicAggregationColumn(name, columnType, aggregationType string)
235
+ BasicAggregationColumnWithAlias(name, columnType, aggregationType, newName string)
236
+ BasicFilterCondition(columnName, operator string, filterValues []FilterValue)
237
+ BasicFilterValue(value string)
238
+ BasicFilterColumnValue(columnName string)
239
+ ReadSQLFile(filename string)
240
+ ```
241
+
242
+ ### 合法枚举
243
+
244
+ 筛选组合:
245
+
246
+ - `AND`
247
+ - `OR`
248
+
249
+ 筛选操作符:
250
+
251
+ - `EQ` `NE`
252
+ - `LT` `LE` `GT` `GE`
253
+ - `IN`
254
+ - `BT`
255
+ - `IS_NULL` `NOT_NULL`
256
+ - `CONTAINS` `NOT_CONTAINS`
257
+ - `STARTSWITH` `NOT_STARTSWITH`
258
+ - `ENDSWITH` `NOT_ENDSWITH`
259
+
260
+ JOIN 类型:
261
+
262
+ - `INNER`
263
+ - `LEFT_OUTER`
264
+ - `RIGHT_OUTER`
265
+ - `FULL_OUTER`
266
+
267
+ 聚合类型:
268
+
269
+ - `SUM`
270
+ - `COUNT`
271
+ - `COUNT_DISTINCT`
272
+ - `MIN`
273
+ - `MAX`
274
+ - `AVG`
275
+ - `FIRST_NOT_NULL`
276
+
277
+ APPEND_ROWS unionType:
278
+
279
+ - `INCLUDE_SHARED`
280
+ - `INCLUDE_ALL`
281
+ - `INCLUDE_FROM`
@@ -0,0 +1,163 @@
1
+ # 工作流数据流扩展节点参考
2
+
3
+ 本文档描述工作流数据流中相对于 BI ETL 的超集节点类型。
4
+ 通用 ETL 节点(INPUT_DATASET、OUTPUT_DATASET、SQL_SCRIPT 等)的用法参见 `ETL_AI_DEVELOP.md`(symlink 到 guanetl)。
5
+
6
+ ## 透传节点(LoadNodeFromFile 模式)
7
+
8
+ 以下节点在 `guanwf edit` 导入时自动保存为 `node_XXXX.json` 独立文件,`etl.go` 中通过 `LoadNodeFromFile` 引用。
9
+ 这些节点配置复杂且与数据库连接紧密绑定,通常不需要手动修改。
10
+
11
+ ### INPUT_DATASOURCE(数据源输入)
12
+
13
+ 数据库直连输入,无上游 source。从关系型数据库中读取数据到数据流。
14
+
15
+ ```go
16
+ // 透传模式(edit 路径推荐)
17
+ node_1001 := LoadNodeFromFile("node_1001.json")
18
+
19
+ // 结构化模式(create 路径)
20
+ node_1001 := BasicInputDatasource("id_1001", "数据库输入", InputDatasourceConfig{
21
+ BaseConfig: BaseConfig{Position: Position{X: 100, Y: 100}},
22
+ AcId: "ac_xxx",
23
+ CnId: "cn_yyy",
24
+ Sql: ReadSQLFile("input_query.sql"),
25
+ SourceType: "DIRECT_CONNECT",
26
+ })
27
+ ```
28
+
29
+ 配置说明:
30
+ - `AcId`: 数据账号 ID(通过 `guands account list` 获取)
31
+ - `CnId`: 数据连接 ID(通过 `guands account get <acId>` 获取)
32
+ - `Sql`: 查询 SQL(支持 ReadSQLFile 引用独立文件)
33
+ - `SourceType`: `DIRECT_CONNECT`(数据库直连)或 `DATASET`(引用数据集)
34
+
35
+ ### OUTPUT_DATASOURCE(数据源输出)
36
+
37
+ 数据回写到数据库,有上游 source。
38
+
39
+ ```go
40
+ // 透传模式(edit 路径推荐)
41
+ node_1005 := LoadNodeFromFile("node_1005.json", node_1004)
42
+
43
+ // 结构化模式(create 路径)
44
+ node_1005 := BasicOutputDatasource("id_1005", "数据回写", node_1004, OutputDatasourceConfig{
45
+ BaseConfig: BaseConfig{Position: Position{X: 500, Y: 100}},
46
+ AcId: "ac_xxx",
47
+ CnId: "cn_yyy",
48
+ TargetTable: "target_table",
49
+ UpdateMode: "OVERWRITE",
50
+ })
51
+ ```
52
+
53
+ 配置说明:
54
+ - `TargetTable`: 目标表名
55
+ - `UpdateMode`: `OVERWRITE`(覆盖)、`APPEND`(追加)、`UPSERT`(更新插入)
56
+ - `Schema`: 目标 schema(可选)
57
+ - `PrimaryKeys`: UPSERT 模式的主键列
58
+ - `PreSql`/`PostSql`: 写入前/后执行的 SQL(可选)
59
+
60
+ ### INCREMENT_OUTPUT_DATASET(增量输出数据集)
61
+
62
+ 增量方式写入观远数据集(与 OUTPUT_DATASET 的区别是支持增量更新策略)。
63
+
64
+ ```go
65
+ // 透传模式
66
+ node_1006 := LoadNodeFromFile("node_1006.json", node_1005)
67
+ ```
68
+
69
+ 配置说明:
70
+ - `OutputDsId`: 输出数据集 ID
71
+ - `UpdateMode`: `APPEND`(追加)或 `UPSERT`(更新插入)
72
+ - `PrimaryKeys`: UPSERT 模式的主键列
73
+
74
+ ## 原生 DSL 节点
75
+
76
+ 以下节点有结构化的 Go DSL 函数,import 后会生成可直接编辑的代码。
77
+
78
+ ### TRIM_COLUMNS(去空格)
79
+
80
+ 对指定列去除首尾空格。
81
+
82
+ ```go
83
+ node := BasicTrimColumns("id_1002", "去空格", source, []string{"name", "address"}, Position{X: 300, Y: 100})
84
+ ```
85
+
86
+ 参数:
87
+ - `columnNames`: 要处理的列名列表(对应 BI 端 `columnNames` 字段)
88
+
89
+ ### COMBINE_COLUMNS(合并列)
90
+
91
+ 将多个列合并为一个新列。
92
+
93
+ ```go
94
+ node := BasicCombineColumns("id_1005", "合并地址", source, "full_address", []string{"city", "street"}, ", ", false, Position{X: 600, Y: 100})
95
+ ```
96
+
97
+ 参数:
98
+ - `targetFieldName`: 新列名
99
+ - `fields`: 要合并的源列名列表
100
+ - `separator`: 分隔符
101
+ - `removeSelectedFields`: 是否删除源列
102
+
103
+ ## 透传 DSL 节点(复杂 mappings 类)
104
+
105
+ 以下节点因配置结构复杂(含嵌套数组/对象),import 时使用 `LoadNodeFromFile` 透传模式保存完整 JSON,确保往返保真。
106
+
107
+ ### VALUE_MAPPER(值映射/值替换)
108
+
109
+ 将指定列的值按映射表替换。
110
+
111
+ ```go
112
+ // import 自动生成(透传模式)
113
+ node_1003 := LoadNodeFromFile("node_id_1003.json", source)
114
+ ```
115
+
116
+ 配置说明(编辑 `node_*.json` 文件):
117
+ - `column`: 源列名
118
+ - `targetField`: 目标列名(可与源列相同)
119
+ - `mappings`: 映射规则数组
120
+ - `defaultValue`: 不匹配时的默认值
121
+
122
+ ### SINGLE_VALUE_MAPPER(Null 值替换)
123
+
124
+ 将指定列的 null 值替换为固定值。import 时使用 LoadNodeFromFile 透传模式。
125
+
126
+ ```go
127
+ // import 自动生成(透传模式)
128
+ node_1004 := LoadNodeFromFile("node_id_1004.json", source)
129
+ ```
130
+
131
+ 配置说明(编辑 `node_*.json` 文件中的 `mappings` 数组):
132
+ - `mappings`: 列值映射列表,每条包含:
133
+ - `colName`: 目标列名
134
+ - `origValue`: 原始值(`null` 表示匹配 null)
135
+ - `toValue`: 替换值
136
+
137
+ ## SQL Fallback 节点
138
+
139
+ 以下节点类型不做原生支持,import 时由 guanetl-server 生成等价 SQL 转为 `BasicSqlScript`:
140
+
141
+ | 节点类型 | 说明 |
142
+ |---|---|
143
+ | `COLLAPSE_COLUMNS` | 列转行 |
144
+ | `UNCOLLAPSE_COLUMNS` | 行转列 |
145
+ | `BENCHMARKING` | 智能对标 |
146
+ | `FREQ_PATTERN` | 关联性挖掘 |
147
+ | `DATA_INSIGHT` | 智能归因 |
148
+
149
+ ## 不支持的节点类型
150
+
151
+ 以下节点类型在当前版本中不支持 import,遇到时会报错:
152
+
153
+ | 节点类型 | 说明 |
154
+ |---|---|
155
+ | `SAP_ERP_INPUT` | SAP ERP 输入 |
156
+ | `FTP_INPUT` | FTP 输入 |
157
+
158
+ ## LoadNodeFromFile 使用规则
159
+
160
+ 1. **透传与结构化可互换**:用户可以把 `LoadNodeFromFile(...)` 替换为 `BasicInputDatasource(...)`(或反过来),框架不限制。
161
+ 2. **修改透传节点**:直接编辑对应的 `node_XXXX.json` 文件即可。
162
+ 3. **JSON 文件命名**:`node_` + 节点 ID(sanitize 后保留字母/数字/下划线/连字符)+ `.json`。
163
+ 4. **sources 自动处理**:export 时 `LoadedNode.ToMap()` 会根据函数参数中的 sources 自动更新 `sources` 字段。