@josephyan/qingflow-cli 1.0.11 → 1.1.2

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.
Files changed (67) hide show
  1. package/README.md +3 -3
  2. package/npm/bin/qingflow.mjs +40 -2
  3. package/npm/lib/runtime.mjs +386 -15
  4. package/npm/scripts/postinstall.mjs +7 -2
  5. package/package.json +1 -1
  6. package/pyproject.toml +1 -1
  7. package/skills/qingflow-cli/SKILL.md +440 -0
  8. package/skills/qingflow-cli/manifest.yaml +10 -0
  9. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ADMIN_CHEATSHEET.md +94 -0
  10. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md +485 -0
  11. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_CHARTS_WORKFLOW.md +237 -0
  12. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_MATCH_RULES.md +137 -0
  13. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_PORTAL_WORKFLOW.md +263 -0
  14. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_VIEWS_WORKFLOW.md +304 -0
  15. package/skills/qingflow-cli/reference/QINGFLOW_CLI_BUILDER_WORKSPACE_ICONS.md +41 -0
  16. package/skills/qingflow-cli/reference/QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md +139 -0
  17. package/skills/qingflow-cli/reference/QINGFLOW_CLI_EXPLORATION_REPORT.md +84 -0
  18. package/skills/qingflow-cli/reference/QINGFLOW_CLI_FIELD_DATA_TYPES.md +129 -0
  19. package/skills/qingflow-cli/reference/QINGFLOW_CLI_MEMBER_CHEATSHEET.md +195 -0
  20. package/skills/qingflow-cli/reference/QINGFLOW_CLI_ONE_SHOT_CHEATSHEET.md +159 -0
  21. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md +20 -0
  22. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_IMPORT_WORKFLOW.md +176 -0
  23. package/skills/qingflow-cli/reference/QINGFLOW_CLI_RECORD_UPDATE_WORKFLOW.md +163 -0
  24. package/skills/qingflow-cli/reference/QINGFLOW_CLI_SCHEMA_APPLY_FIELD_TYPES_AND_SCENARIOS.md +107 -0
  25. package/skills/qingflow-cli/reference/QINGFLOW_CLI_TASK_CONTEXT_WORKFLOW.md +151 -0
  26. package/skills/qingflow-cli/reference/_batch_schema_complex.json +18 -0
  27. package/skills/qingflow-cli/reference/_batch_schema_scalar.json +17 -0
  28. package/skills/qingflow-cli/reference/charts_remove.example.json +1 -0
  29. package/skills/qingflow-cli/reference/charts_reorder.example.json +1 -0
  30. package/skills/qingflow-cli/reference/charts_upsert_bar.example.json +8 -0
  31. package/skills/qingflow-cli/reference/charts_upsert_dashboard_starter.example.json +37 -0
  32. package/skills/qingflow-cli/reference/charts_upsert_minimal.example.json +13 -0
  33. package/skills/qingflow-cli/reference/portal_sections_all_types.example.json +131 -0
  34. package/skills/qingflow-cli/reference/portal_sections_five_types.example.json +126 -0
  35. package/skills/qingflow-cli/reference/portal_sections_standard_workbench.example.json +128 -0
  36. package/skills/qingflow-cli/reference/schema_add_fields_minimal.example.json +7 -0
  37. package/skills/qingflow-cli/reference/schema_apply_add_fields_all_types.json +78 -0
  38. package/skills/qingflow-cli/reference/views_upsert_table_minimal.example.json +7 -0
  39. package/skills/qingflow-cli/scripts/builder-package-from-app-list.py +140 -0
  40. package/skills/qingflow-cli/scripts/find-app-by-keyword.py +132 -0
  41. package/skills/qingflow-cli/scripts/validate_qingflow_output_files.py +87 -0
  42. package/src/qingflow_mcp/__init__.py +1 -1
  43. package/src/qingflow_mcp/builder_facade/models.py +532 -48
  44. package/src/qingflow_mcp/builder_facade/service.py +9194 -2384
  45. package/src/qingflow_mcp/builder_facade/workflow_spec.py +111 -0
  46. package/src/qingflow_mcp/cli/commands/app.py +3 -16
  47. package/src/qingflow_mcp/cli/commands/builder.py +354 -56
  48. package/src/qingflow_mcp/cli/commands/record.py +89 -2
  49. package/src/qingflow_mcp/cli/formatters.py +32 -1
  50. package/src/qingflow_mcp/cli/main.py +245 -3
  51. package/src/qingflow_mcp/public_surface.py +11 -8
  52. package/src/qingflow_mcp/response_trim.py +143 -14
  53. package/src/qingflow_mcp/server.py +15 -12
  54. package/src/qingflow_mcp/server_app_builder.py +108 -30
  55. package/src/qingflow_mcp/server_app_user.py +17 -18
  56. package/src/qingflow_mcp/solution/compiler/__init__.py +1 -3
  57. package/src/qingflow_mcp/solution/compiler/icon_utils.py +294 -0
  58. package/src/qingflow_mcp/solution/executor.py +3 -133
  59. package/src/qingflow_mcp/tools/ai_builder_tools.py +2617 -440
  60. package/src/qingflow_mcp/tools/app_tools.py +53 -8
  61. package/src/qingflow_mcp/tools/package_tools.py +16 -2
  62. package/src/qingflow_mcp/tools/record_tools.py +2095 -176
  63. package/src/qingflow_mcp/tools/resource_read_tools.py +3 -0
  64. package/src/qingflow_mcp/tools/solution_tools.py +30 -2
  65. package/src/qingflow_mcp/tools/workflow_tools.py +3 -31
  66. package/src/qingflow_mcp/version.py +110 -0
  67. package/src/qingflow_mcp/solution/compiler/workflow_compiler.py +0 -173
@@ -0,0 +1,176 @@
1
+ # 记录批量导入 SOP:`app_get`(`import_capability`)→ 模板 → 校验 →(可选)本地修复 → 启动 → 状态
2
+
3
+ 本文与 **MCP 工具命名**一致,并映射到 **`qingflow` CLI**。编写前已 **实跑**:`app get`、`record schema import`、`import template`、`import verify`、`import repair`(未授权分支 + 授权分支遇 **合并单元格** 异常)、`import start`、`import status`(**仅** `--import-id`);环境为当前持久化 CLI 会话、应用 `ead8ims5i401`。
4
+
5
+ ---
6
+
7
+ ## 1. 编排顺序(固定)
8
+
9
+ | 顺序 | 工具名 | CLI | 说明 |
10
+ |------|--------|-----|------|
11
+ | 1 | `app_get` | `qingflow --json app get --app-key <APP_KEY>` | 读 **`data.import_capability`**,预检是否有导入能力 |
12
+ | (可选) | `record_import_schema_get` | `qingflow --json record schema import --app-key <APP_KEY>` | `schema_scope: import_ready`,列 **`columns`**;能力仍以 **`app get`** 为准 |
13
+ | 2 | `record_import_template_get` | `qingflow [--json] import template --app-key <APP_KEY> [--download-to-path <PATH>]` | 官方模板 URL 或 **本地生成的 applicant 模板**(无数据管理权限或导入权限预检未知时可能 fallback) |
14
+ | 3 | `record_import_verify` | `qingflow [--json] import verify --app-key <APP_KEY> --file-path <*.xlsx\|*.xls>` | 产出 **`verification_id`**;**`can_import`** 为真后才应 **`import start`** |
15
+ | (可选) | `record_import_repair_local` | `qingflow [--json] import repair --verification-id <UUID> --authorized-file-modification …` | **必须** 显式授权;**仅 `.xlsx`**;可重复 **`--repair`** |
16
+ | 4 | `record_import_start` | `qingflow [--json] import start --app-key <APP_KEY> --verification-id <UUID> --being-enter-auditing <true\|false> [--view-key …]` | **会真实发起导入** |
17
+ | 5 | `record_import_status_get` | `qingflow [--json] import status (--app-key \| --import-id \| --process-id-str)` **三者择一** | **不要**同时传多个 selector(例如 **`--import-id` 与 `--app-key` 同时**会被 CLI 拒绝) |
18
+
19
+ CLI 注册见全局包 **`qingflow_mcp/cli/commands/imports.py`**(`template` / `verify` / `repair` / `start` / `status` → 上述 `ImportTools` 方法)。
20
+
21
+ ---
22
+
23
+ ## 2. `import_capability`(来自 `app get`)
24
+
25
+ `app get` 在 **`data.import_capability`** 中附带由 `baseInfo` 推导的能力(实现:**`_derive_import_capability`**):
26
+
27
+ | 字段 | 含义 |
28
+ |------|------|
29
+ | **`can_import`** | `true` / `false` / `null`(未知) |
30
+ | **`auth_source`** | `apply_auth`(申请人导入开关)、`data_manage_auth`(数据管理)、`none`、`unknown` |
31
+ | **`applicant_import_enabled`** | 与 `dataImportStatus` 对应 |
32
+ | **`data_manage_status`** | 与 `dataManageStatus` 对应 |
33
+ | **`runtime_checks_required`** | 预检后仍可能受 **`user_disabled`**、**`function_demoted`** 等运行态影响 |
34
+ | **`confidence`** | `preflight`(由元数据推出)或 `unknown` |
35
+
36
+ - **`record_import_schema_get` / `record_import_template_get` / `record_import_verify`**:若 `can_import === false` 且来源非 `unknown`,会直接 **`IMPORT_AUTH_PRECHECK_FAILED`**,不会继续读取导入列、官方模板或进入后端文件校验。
37
+ - **`record_import_template_get`**:当具备申请人导入能力但无数据管理权限时,可能对 **`/app/{appKey}/apply/excelTemplate`** 失败并 **本地生成** applicant 模板(警告 **`IMPORT_TEMPLATE_LOCAL_FALLBACK`**)。
38
+ - 若 **`app get` / `baseInfo`** 权限受限导致 **`import_capability.auth_source: unknown`**,但 applicant 字段 schema 可读,且官方模板接口返回 **40002 / 40027**,`record_import_template_get` 也可能返回 **`status: partial_success`** 并本地生成模板(警告 **`IMPORT_TEMPLATE_LOCAL_FALLBACK_AUTH_UNKNOWN`**)。这只说明“模板准备成功”,**不证明用户有导入权限**;后续仍以 **`record_import_verify` / `record_import_start`** 的 `can_import` 与最终结果为准。
39
+
40
+ ---
41
+
42
+ ## 3. 本地校验仓与运行环境
43
+
44
+ - **`verification_id`** 由 **`record_import_verify`** 生成,并写入本机 **`~/.qingflow-mcp/import-verifications/<id>.json`**(**`ImportVerificationStore`**)。
45
+ - **`record_import_start` / `repair`** 依赖该目录;**自动化/CI 需允许写此目录**,且 **`verify` → `start` 须在** 同一 profile、可达后端的会话内完成。
46
+ - **`record_import_status_get`** 在仅传 **`--import-id`** 时,会结合本地 **`ImportJobStore`** 与后端 **`GET /app/apply/dataImport/record`** 解析 **`app_key` / `process_id_str`**;与 **`import start`** 返回的 **`import_id`**、**`process_id_str`** 对齐使用。
47
+
48
+ ---
49
+
50
+ ## 4. 命令要点
51
+
52
+ ### 4.0 文件形态、CSV 源数据与字段映射
53
+
54
+ - 当前 `import verify/start` 主链路面向官方 Excel 模板(`.xlsx` / `.xls`),不是直接把任意 CSV 交给后端。
55
+ - CSV 可以作为源数据或中间表,但必须先映射到官方模板列名,再写入模板副本;不要改模板表头。
56
+ - 字段映射以 `record schema import` / 模板列为准:源列名可以是用户习惯名,目标列名必须是官方模板 header。
57
+ - 关联字段批量导入优先使用稳定 `record_id` 或 schema 明确支持的唯一搜索值;显示名可能重复时不要猜。
58
+ - 成员/部门字段必须使用 schema/candidate 范围内的值;选项字段优先使用模板里的选项文案。
59
+
60
+ CSV 源数据示例(规划用):
61
+
62
+ ```csv
63
+ 客户名称,关联客户,状态,负责人
64
+ 上海示例客户,CUST_RECORD_ID_001,有效,张三
65
+ ```
66
+
67
+ 映射到模板后的行语义:
68
+
69
+ ```json
70
+ {
71
+ "客户名称": "上海示例客户",
72
+ "关联客户": "CUST_RECORD_ID_001",
73
+ "状态": "有效",
74
+ "负责人": "张三"
75
+ }
76
+ ```
77
+
78
+ ### 4.1 模板
79
+
80
+ ```bash
81
+ qingflow --json import template --app-key "<APP_KEY>" \
82
+ --download-to-path tmp/qingflow_import_template.xlsx \
83
+ > tmp/qingflow_import_template_meta.json
84
+ ```
85
+
86
+ - 关注 **`verification.template_source`**:`official` 或本地 fallback。
87
+ - 若 `verification.import_auth_prechecked: false`,表示模板来自 schema 可读分支但导入权限尚未预检成功;不要把该模板结果当作导入授权结论。
88
+ - **`expected_columns`** 在部分响应负荷中给出;**与校验**一致。
89
+
90
+ ### 4.2 校验
91
+
92
+ ```bash
93
+ qingflow --json import verify --app-key "<APP_KEY>" \
94
+ --file-path tmp/qingflow_import_template.xlsx \
95
+ > tmp/qingflow_import_verify.json
96
+ ```
97
+
98
+ - 始终解析 **`verification_id`**;仅 **`can_import: true`** 时进入后端 multipart **`/upload/verification`**。
99
+ - **标准输出**上 OpenPyXL 可能对某些 xlsx 打出 **`UserWarning`**(**出现在 JSON 前**)。 jq/直解 **`json.load` 失败时,从首个 `{` 起截取再解析**。
100
+
101
+ ### 4.3 本地修复(可选)
102
+
103
+ ```bash
104
+ qingflow --json import repair \
105
+ --verification-id "<VERIFICATION_UUID>" \
106
+ --authorized-file-modification \
107
+ [--output-path tmp/repaired.xlsx] \
108
+ [--repair normalize_headers] [--repair trim_trailing_blank_rows] \
109
+ ... \
110
+ > tmp/qingflow_import_repair.json
111
+ ```
112
+
113
+ - **未加** `--authorized-file-modification` → **`IMPORT_REPAIR_NOT_AUTHORIZED`**。
114
+ - **`--repair`** 可重复;允许集合(实现 **`SAFE_REPAIRS`**):
115
+ **`normalize_headers`**、**`trim_trailing_blank_rows`**、**`normalize_enum_values`**、**`normalize_date_formats`**、**`normalize_number_formats`**、**`normalize_url_cells`**。
116
+ **省略 `--repair`** 时实现默认对 **全集** 尝试。
117
+ - **仅 `.xlsx`**;**合并单元格表格** 在 **`normalize_headers`** 等路径上可能触发 **`MergedCell` 只读** 类异常(实跑曾遇到);此时应改模板或跳过 repair、重新导出模板再 **`verify`**。
118
+
119
+ 成功时返回 **`new_verification_id`**,后续 **`start`** 应用 **新 ID**(若 repair 后重新校验通过)。
120
+
121
+ ### 4.4 启动导入
122
+
123
+ ```bash
124
+ qingflow --json import start \
125
+ --app-key "<APP_KEY>" \
126
+ --verification-id "<VERIFICATION_UUID>" \
127
+ --being-enter-auditing false \
128
+ [--view-key "<VIEW_KEY>"] \
129
+ > tmp/qingflow_import_start.json
130
+ ```
131
+
132
+ - **`--being-enter-auditing`**:**必填**,布尔字面量:`true` / `false` / `1` / `0` / `yes` / `no` 等(见 argparse `parse_bool_text`)。
133
+ - **会校验**:校验记录仍存在、**`can_import`**、**文件 sha256** 未变、**`schema_fingerprint`** 未变;否则 **`IMPORT_VERIFICATION_STALE`** / **`IMPORT_FILE_CHANGED_AFTER_VERIFY`** / **`IMPORT_SCHEMA_CHANGED_AFTER_VERIFY`**。
134
+
135
+ ### 4.5 状态
136
+
137
+ **参数规则(CLI)**:**必须且只能** 提供 **`--app-key`**、**`--import-id`**、**`--process-id-str`** 中的 **一个**。
138
+
139
+ ```bash
140
+ # 已知本次 start 返回的 import_id(不要同时加 --app-key)
141
+ qingflow --json import status --import-id "<IMPORT_UUID>" \
142
+ > tmp/qingflow_import_status.json
143
+
144
+ # 或只看该应用最近一次导入(仅 --app-key)
145
+ qingflow --json import status --app-key "<APP_KEY>" \
146
+ > tmp/qingflow_import_status_latest.json
147
+ ```
148
+
149
+ ---
150
+
151
+ ## 5. 与 `record schema import` 的关系
152
+
153
+ - **`record schema import`**:专注 **导入列 / 指纹**(与校验用的 schema bundle 一致思路),**不替代** **`app get` 的 `import_capability`**。
154
+ - 推荐顺序:**`app get`(能力)** →(可选)**`record schema import`(列清单)** → **`import template`** → …
155
+
156
+ ---
157
+
158
+ ## 6. 实测摘要(编写时)
159
+
160
+ | 步骤 | 结果 |
161
+ |------|------|
162
+ | `app get` | `import_capability.can_import: true`,`auth_source: data_manage_auth` |
163
+ | `record schema import` | `schema_scope: import_ready` |
164
+ | `import template --download-to-path` | 落盘 xlsx 成功 |
165
+ | `import verify` | `can_import: true`,`verification_id` 可用;可能有 **`IMPORT_HEADERS_AUTO_NORMALIZED`** |
166
+ | `import repair --authorized-file-modification` | 特定模板因 **合并单元格** 在 **`normalize_headers`** 崩溃;属 **模板/实现边界** |
167
+ | `import start` | `status: accepted`,返回 **`import_id`**、**`process_id_str`** |
168
+ | `import status --import-id …` | 返回 `app_key`、**`process_id_str`**、**`status`**(后端枚举字符串,如实测 `"3"`) |
169
+ | `import status --import-id … --app-key …` | **CLI 拒绝**:只能选一个 selector |
170
+
171
+ ---
172
+
173
+ ## 7. 交叉引用
174
+
175
+ - 主技能 [SKILL.md](../SKILL.md):**落盘**、导入类权限码 **`IMPORT_*`** 提示。
176
+ - [QINGFLOW_CLI_ADMIN_CHEATSHEET.md](./QINGFLOW_CLI_ADMIN_CHEATSHEET.md):管理侧与 **`IMPORT_*`** 失败排查。
@@ -0,0 +1,163 @@
1
+ # Qingflow CLI:更新记录工作流(详情后直接写入)
2
+
3
+ > **用途**:使用当前 CLI 会话读取目标记录详情后,用 **`record update`** 直接提交差异字段。主链路是 `record get -> record update`;字段范围不清、候选歧义或失败排查时,才追加 **`record schema update`**。
4
+ > **关联**:只读列表/详情见 **[QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md](./QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md)**;**字段 `kind` 与写入值总表**见 **[QINGFLOW_CLI_FIELD_DATA_TYPES.md](./QINGFLOW_CLI_FIELD_DATA_TYPES.md)**;最终统计结论统一使用 **`qingflow-record-analysis` 的 `record_access -> Python/pandas`**;**成员 / 部门等** 的细颗粒写入见 **[QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md](./QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md)**;主技能 **[../SKILL.md](../SKILL.md)**。
5
+
6
+ ---
7
+
8
+ ## 习惯概念名 → CLI
9
+
10
+ | 概念名 | CLI |
11
+ | --- | --- |
12
+ | `app_get`(含 `accessible_views`) | `qingflow --json app get --app-key <APP_KEY>` |
13
+ | `record_schema_get`,`schema_mode=browse`,带 `view_id` | `qingflow --json record schema browse --app-key <APP_KEY> --view-id <VIEW_ID>` |
14
+ | `record_get` | `qingflow --json record get --app-key <APP_KEY> --record-id <RECORD_ID> [--view-id <VIEW_ID>]` |
15
+ | `record_update`(单条) | `qingflow --json record update --app-key <APP_KEY> --record-id <RECORD_ID> --fields-file <FIELDS.json> [--view-id <VIEW_ID>]` |
16
+ | `record_update`(批量) | `qingflow --json record update --app-key <APP_KEY> --items-file <ITEMS.json> [--view-id <VIEW_ID>] [--dry-run]`(见下文) |
17
+
18
+ ---
19
+
20
+ ## 推荐顺序
21
+
22
+ ```text
23
+ record get(得到 RECORD_ID、当前值、fields[].title/field_id/kind;已知前端视图时带 view_id)
24
+ → 智能体按用户要求组 fields key/value
25
+ → record update --fields-file [--view-id](只提交需要改的题;大 JSON 落盘)
26
+ ```
27
+
28
+ **何时先 `app get` / `record list`**:不知道 `view_id` 或 `record_id` 时,先用 `app get` 选视图,再用 `record list` 定位候选。已知 `record_id` 但已知前端/custom view 时,直接 `record get --view-id <VIEW_ID>`。
29
+ **何时追加 `record schema browse`**:只在需要提前看视图表头或搜索字段时使用;`record get` 已经会返回单条详情所需字段信息。
30
+ **何时追加 `record schema update`**:该命令 **必须 `record_id`**,返回 **`writable_fields` / `payload_template` / `available_update_routes` / `recommended_update_route`**,只用于排查「能改什么」和「会走哪条更新路径」。已知前端/custom view 时同样带 `--view-id`,让诊断优先探测同一个详情页上下文;字段明确时不要把它当必经步骤。
31
+
32
+ **更新路径自动选择**:`record update --view-id <VIEW_ID>` 会优先用这个 view 做字段解析和 custom view 写入候选;未传时自动探测可读 route。写入时仍会先尝试 **数据管理员直改**,若后端返回权限拒绝,再 fallback 到 **前端同源 custom view 详情编辑路径**;如果当前用户存在这条记录的唯一待办且目标字段在当前节点可编辑,最后会尝试 **workflow save-only**。成功时响应中的 **`update_route`** 是最终成功路径;失败时再查看失败原因与路线诊断。若诊断里有 `40002`,只代表某条 route 被拒绝,不一定表示用户对记录完全无编辑能力。
33
+
34
+ ---
35
+
36
+ ## 命令模板
37
+
38
+ **1)读取详情**
39
+
40
+ ```bash
41
+ qingflow --json record get \
42
+ --app-key <APP_KEY> \
43
+ --record-id <RECORD_ID> \
44
+ --view-id <VIEW_ID> \
45
+ > tmp/qingflow_record_get.json
46
+ ```
47
+
48
+ 已知用户前端所在视图时传同一个 `--view-id`;未知时 CLI 默认先尝试 `system:all/type=8`,若该详情 route 被权限/不可见类错误拒绝,会继续尝试可访问视图。读取 `fields[]` 中的标题、`field_id`、`kind` 与当前值来组更新 payload。
49
+
50
+ `record_get` 的 `unavailable_context[]` 是辅助上下文提示:`detail_schema` / `audit_info` / 首屏日志等 40002 不等于记录详情读取失败。只要顶层 `status=ok` 且 `fields[]` 中已有目标字段,就按这些字段组 `record update` 的 key/value;只有顶层失败才按失败原因排查。
51
+
52
+ **2)如果缺 `record_id`,先定位候选**
53
+
54
+ ```bash
55
+ qingflow --json record list \
56
+ --app-key <APP_KEY> \
57
+ --view-id <VIEW_ID> \
58
+ > tmp/qingflow_records.json
59
+ # 或
60
+ qingflow --json record get --app-key <APP_KEY> --record-id <RECORD_ID> \
61
+ > tmp/qingflow_record_get.json
62
+ ```
63
+
64
+ **3)单条更新**
65
+
66
+ ```bash
67
+ qingflow --json record update \
68
+ --app-key <APP_KEY> \
69
+ --record-id <RECORD_ID> \
70
+ --fields-file tmp/qingflow_patch_fields.json \
71
+ --view-id <VIEW_ID> \
72
+ > tmp/qingflow_update_result.json
73
+ ```
74
+
75
+ `--view-id` 可省略;已知用户来自某个 `custom:...` 前端详情页时建议传入,CLI 会优先尝试该 view,失败后再按自动路径 fallback。
76
+
77
+ **4)(仅排障)本条记录的更新诊断**
78
+
79
+ ```bash
80
+ qingflow --json record schema update \
81
+ --app-key <APP_KEY> \
82
+ --record-id <RECORD_ID> \
83
+ --view-id <VIEW_ID> \
84
+ > tmp/qingflow_schema_update.json
85
+ ```
86
+
87
+ **`tmp/qingflow_patch_fields.json`**:JSON **对象**,键为 **题目标题**(优先来自 `record get.fields[]`),**只放需要修改的键**;值类型与 **新建** 相同(成员、部门、附件等见 **[QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md](./QINGFLOW_CLI_RECORD_CREATE_WORKFLOW.md)**)。实现要求 **`fields` 为「按标题索引的 map」**(与 `record insert` 一致)。
88
+
89
+ **特殊字段值**:
90
+
91
+ - **成员字段 `kind=member`**:先传自然语言字符串,如 `{"客户成功":"周颖"}`。若返回 `status="needs_confirmation"`,说明尚未写入;从 `confirmation_requests[].candidates[]` 选一项,改传显式对象,如 `{"客户成功":{"uid":1048599,"name":"沈嘉慧Seth","email":"shenjiahui@exiao.tech"}}`。
92
+ - **部门字段 `kind=department`**:先传部门名,如 `{"负责部门":"客户成功部"}`;歧义时同样按候选对象/id 重试。
93
+ - **关联字段 `kind=relation`**:传唯一可解析的自然语言、或 `{"apply_id":"..."}`;多匹配时按候选记录重试,不要猜 `apply_id`。
94
+ - **选项字段 `kind=select`**:单选传一个选项字符串;多选传字符串数组。
95
+ - **引用/公式/自动填充/只读/系统字段**:不要强写目标字段;改写上游驱动字段,或把 blocker 原因反馈给用户。
96
+
97
+ `needs_confirmation` 的语义是 **`write_executed=false`,等待确认候选**;不要把它当成功,也不要把它当最终权限失败。只有用明确候选对象重试并成功后,才报告写入完成。
98
+
99
+ **`--verify-write` / `--no-verify-write`**:见 `qingflow record update -h`;默认倾向 **保留校验**。
100
+
101
+ 更新后优先查看:
102
+
103
+ - **`update_route.route_type`**:`admin_direct`、`view_edit` 或 `task_save_only`。
104
+ - **`tried_routes[]`**:只在失败或 verbose 诊断时需要看;成功时不要把中间路径失败当最终结果。
105
+ - **`write_executed`**:是否已经发出写请求;为 `true` 时不要盲目整单重试。
106
+ - **`verification_status`**:读回校验是否通过。
107
+
108
+ **`record_id` 与写入(排查前置)**:若后端 **`record_id` 超出 JavaScript `Number.MAX_SAFE_INTEGER`(约为 `9007199254740991`)**:在旧 1.0.x 或同类实现分支上,**`record list` / `record get` / `record schema update` 常仍可正常**,而 **`record update` 的实际落库**可能在 **`apply_id` 二次校验**处失败——stdout 常表现为 **`apply_id must be positive`**,根本原因多为 **「整数超过 JS 安全范围」**。**批量 `--dry-run`** 仍可用于覆盖 **预检**流程。处理方式见 **「排查摘要」**末行。
109
+
110
+ ---
111
+
112
+ ## 批量模式(`--items-file`)
113
+
114
+ - **不可**与 **`--record-id` / `--fields-file`** 同时使用。
115
+ - `items` 为 **数组**,每项为对象,至少包含:
116
+ - **`record_id`**:正整数(字符串或可解析数字均可,以 CLI 校验为准)
117
+ - **`fields`**:与单条相同的「标题 → 值」map
118
+
119
+ 示例:
120
+
121
+ ```json
122
+ [
123
+ { "record_id": "518257794791628802", "fields": { "客户名称": "新名称" } },
124
+ { "record_id": "518257794791628803", "fields": { "备注": "批量第 2 条" } }
125
+ ]
126
+ ```
127
+
128
+ - **`--dry-run`**:**仅批量模式**支持(单条模式需走 `items-file` 才能 dry-run);用于预检。详见 `qingflow record update -h` 与 CLI 报错指引。
129
+ - 批量输出优先读顶层语义,不需要再钻进 `data.summary`:
130
+ - `mode="batch"`、`dry_run`
131
+ - `total`、`succeeded`、`failed`、`needs_confirmation`
132
+ - `updated_record_ids`
133
+ - `write_executed`、`safe_to_retry`、`verification_status`
134
+ - `items[].row_number / record_id / status / failed_fields / confirmation_requests`
135
+ - `dry_run=true` 时 `write_executed=false`、`safe_to_retry=true`,只能说明预检通过;真正写入仍需去掉 `--dry-run` 再执行。
136
+ - `write_executed=true` 后不要盲目整批重试;根据 `items[].row_number` 和 `updated_record_ids` 判断是否只补失败行。
137
+
138
+ ---
139
+
140
+ ## 安全与落盘
141
+
142
+ - **`record update` 为写操作**;仅在授权环境、**必要时**对测试应用演练。
143
+ - **`list` / `get` / `schema browse` / `schema update` / 更新结果** 体大量时按主技能 **落盘规则** 重定向。
144
+ - 主技能 **危险操作** 中对 `record update --dry-run`(批量)的说明仍适用。
145
+
146
+ ---
147
+
148
+ ## 排查摘要
149
+
150
+ | 现象 | 处理 |
151
+ | --- | --- |
152
+ | 缺 `record_id` | 先用 **`record list`**(同一 `view_id`)或 **`record get`**。 |
153
+ | 不知道字段标题或当前值 | 先查 **`record get`** 的 `fields[]`。 |
154
+ | 不知道能改哪些题 / 路径失败 | 再查 **`record schema update`** 的 `writable_fields` / `payload_template` / 路径诊断;它不是主链路前置步骤。 |
155
+ | 配置类报错 | 单条更新必须 **`--record-id` + `--fields-file`**;批量必须 **`--items-file`**,且勿混用单条参数。 |
156
+ | `needs_confirmation` | 尚未写入;从 `confirmation_requests[].candidates[]` 选明确对象/id 后重试,再读回确认。 |
157
+ | `40002` | 先看 **`tried_routes`**:如果 `admin_direct` 拒绝但 `view_edit` 或 `task_save_only` 成功,说明某条具体通道不可用但仍有可写路径;如果所有 route 都拒绝,再按权限问题处理。 |
158
+ | 成员/部门格式错误 | 与 **创建记录** 文档同一套结构;必要时用 **`record member-candidates` / `record department-candidates`** 走字段候选范围。不要改用 `builder member search` 做记录字段候选。 |
159
+ | **`apply_id must be positive`**,或内部报错含 **`exceeds JavaScript's safe integer range`**(单条 **`record update`** 或批量 **非 dry-run**) | **`record_id` 典型超出 JavaScript 安全整数上限**(`Number.MAX_SAFE_INTEGER`,约为 `9007199254740991`):读链路与 **`schema update`、批量 `--dry-run`** 可走通;**写入**须在 **新版本 CLI** 重做或向维护方确认。短期仅用 **更小 id 的授权测试应用/记录**演练。 |
160
+
161
+ ---
162
+
163
+ *维护:复核 `qingflow record schema update -h`、`record update -h`;`view_id` 与成员策略以当前 CLI help 与同目录成员速查表为准。*
@@ -0,0 +1,107 @@
1
+ # `app_schema_apply` / `builder schema apply`:字段类型与场景总表
2
+
3
+ > **口径**:`field.type` 枚举以 **`qingflow --json builder contract --tool-name app_schema_apply`**(及包内 `PublicFieldType`)为准;**形状校验**以 `qingflow_mcp` 的 **`FieldPatch`**(`builder_facade/models.py`)与 **`app_schema_apply` 编排**(`builder_facade/service.py`)为准。
4
+ > **CLI**:`qingflow [--profile NAME] builder schema apply [--app-key APP_KEY | 创建模式… | --apps-file 多应用对象] [--publish|--no-publish] [--add-fields-file …] [--update-fields-file …] [--remove-fields-file …]`;**`qingflow` 未在 PATH 时**可用 `npx @qingflow-tech/qingflow-cli` 前缀,并把 `--profile` 放在 `builder` 之前。
5
+ > **新建应用主规则**:`add_fields[]` 里必须有且仅有一个顶层字段标记 `as_data_title: true`;数据封面可选,只能在顶层 `attachment` 字段标记 `as_data_cover: true`。
6
+ > **权限**:包内新建应用按后端 `CreateAppBean` 链路只预检目标包 **AddAppAuth**;已有应用的字段/基础信息变更才走应用自身 **EditAppAuth**。
7
+
8
+ ---
9
+
10
+ ## 1. 全部 `field.type`(19 种)
11
+
12
+ 与契约一致时,搭建侧 **`add_fields[]` / `update_fields[]`** 可用类型为:
13
+
14
+ | `type` | 说明(搭建语义) |
15
+ |--------|------------------|
16
+ | `text` | 单行文本 |
17
+ | `long_text` | 多行文本 |
18
+ | `number` | 数字 |
19
+ | `amount` | 金额 |
20
+ | `date` | 日期 |
21
+ | `datetime` | 日期时间 |
22
+ | `member` | 成员 |
23
+ | `department` | 部门(可带 `department_scope`) |
24
+ | `single_select` | 单选 |
25
+ | `multi_select` | 多选 |
26
+ | `phone` | 电话 |
27
+ | `email` | 邮箱 |
28
+ | `address` | 地址 |
29
+ | `attachment` | 附件 |
30
+ | `boolean` | 是否 |
31
+ | `q_linker` | 远程查询 / 数据获取(**`q_linker_binding`**) |
32
+ | `code_block` | 代码块(**`code_block_binding`** + 编译后的 `code_block_config`) |
33
+ | `relation` | 关联记录(**`relation_mode`**:`single` \| `multiple`) |
34
+ | `subtable` | 子表(**`subfields[]`**) |
35
+
36
+ **`relation_mode`**:仅 **`relation`** 使用,取值为 **`single`** 或 **`multiple`**(多关联字段能力受 **`verification.relation_field_limit_verified`** 等约束,见交付文 §6)。
37
+
38
+ ### 1.1 智能体友好字段类型别名
39
+
40
+ 主路径可以写更符合直觉的字段类型;工具写入前自动归一化,读回和排障时仍使用 canonical 类型:
41
+
42
+ | 智能体写法 | canonical 类型 |
43
+ |------------|----------------|
44
+ | `multiline` / `multiline_text` / `textarea` | `long_text` |
45
+ | `select` / `single_choice` / `dropdown` | `single_select` |
46
+ | `multi_select` / `multi_choice` / `multiple_choice` / `checkbox` | `multi_select` |
47
+
48
+ 完整映射以 `builder contract --tool-name app_schema_apply` 的 `$.contract.allowed_values["field.type_aliases"]` 为准。
49
+
50
+ ---
51
+
52
+ ## 2. 按类型的必填 / 常用可选(写意图)
53
+
54
+ | `type` | 通常必填 | 常见可选 / 约束 |
55
+ |--------|----------|-----------------|
56
+ | 标量与选择器 | `name` + `type`(+ 业务 `required` 等) | `description`、`required`;新建应用时其中一个顶层可读字段须 `as_data_title: true`;**`single_select` / `multi_select` 的 `options` 可用字符串数组或对象数组**,如 `["A", "B"]` 或 `[{"label":"A","value":"a"}]`,工具会归一化为选项文案 |
57
+ | `department` | 同左 | **`department_scope`**:`mode: all \| custom`;`custom` 时 **`departments[]`**(含 `dept_id` 等) |
58
+ | `relation` | **`target_app_key`**、**`display_field`**、**`visible_fields[]`** | **`relation_mode`**;目标应用元数据不可读时可多写 **`name`** 降级 |
59
+ | `subtable` | **`subfields[]` 非空** | 子列递归同父级规则;更新已存在子列优先契约 **`subfield_updates`** |
60
+ | `q_linker` | **`q_linker_binding`**:`inputs`、`request`、**`outputs[]` 每项须含 `target_field`**(能解析到本表单字段) | `request.url` 等须符合**租户/后端远程查询策略**;读回 JSON 可能**缺** `outputs[].target_field`,会导致本地再编译失败(见 §5) |
61
+ | `code_block` | **`code_block_binding`**:`inputs`、`code`、**`outputs[]` + `target_field`**;代码体须能被规范识别 **`qf_output` 赋值** | **`auto_trigger`**、按钮文案;勿与 **`code_block_config.code_content`** 与 **`binding.code`** 冲突 |
62
+
63
+ 别名:契约会把 `title`/`label` 映射到 **`name`** 等;字段类型自然别名见 §1.1。
64
+
65
+ ---
66
+
67
+ ## 3. `builder schema apply` 场景矩阵
68
+
69
+ | 场景 | 必备 CLI 参数 | 说明 |
70
+ |------|----------------|------|
71
+ | 编辑已有应用 | `--app-key`;可选 `--no-publish` | **不要**与 `--package-id`、`--create-if-missing` 同用 |
72
+ | 包内新建应用 | `--package-id`、`--app-name`(或 title 映射)、**`--create-if-missing`**;初始 `add-fields-file` 至少包含一个 `as_data_title: true` 字段 | 创建后可得到新 **`app_key`**(以响应为准) |
73
+ | 多应用一次性新建 | `--package-id`、**`--create-if-missing`**、`--apps-file` | 应用包/系统里有 2 个以上应用,或同批应用之间有 `relation` 时使用;`--apps-file` 主写法为 `{package_id, apps}` 对象,raw apps array / singleton wrapper array 仅兼容;`apps[].client_key` 可被 `target_app_ref` 引用,`apps[].app_name` 可被 `target_app` 引用 |
74
+ | 仅增字段 | `--add-fields-file`(JSON 数组) | 与 **`update_fields`/`remove_fields`** 可同次组合(以契约为准) |
75
+ | 改字段 | `--update-fields-file` | 每项 **`selector`**(`name`/`que_id`/`field_id`)+ **`set`** |
76
+ | 删字段 | `--remove-fields-file` | 每项至少 **`name`/`que_id`/`field_id`** 之一 |
77
+ | 可见性(新建/部分元数据) | `--visibility-file` | `mode` / `external_mode` / `selectors` 见交付文 §3.2 |
78
+ | 发布 | 默认或 `--publish`;探针常用 **`--no-publish`** | 与 **`app_publish_verify`** 配合 |
79
+
80
+ ---
81
+
82
+ ## 4. 仓库示例文件(单一完整清单)
83
+
84
+ | 内容 | 位置 |
85
+ |------|------|
86
+ | **19 种 `type` + `code_block` + `q_linker` + 1 个 `relation`(一次 `add_fields`)** | [schema_apply_add_fields_all_types.json](./schema_apply_add_fields_all_types.json)(首字段已标记 `as_data_title`,附件字段已标记 `as_data_cover`;`relation.target_app_key` 须指向含「单行文字」「多行文字」的目标应用,默认 `ead8ims5i401`) |
87
+ | **`q_linker_example` / `code_block_example`**(契约内置形状) | `ai_builder_tools.py` 中 `app_schema_apply` 合约 |
88
+ | 最小新建字段(1 个 text 标题字段) | [schema_add_fields_minimal.example.json](./schema_add_fields_minimal.example.json) |
89
+ | 历史 scalar / complex 批测(可选) | [_batch_schema_scalar.json](./_batch_schema_scalar.json)、[_batch_schema_complex.json](./_batch_schema_complex.json) |
90
+
91
+ ---
92
+
93
+ ## 5. 实跑结论
94
+
95
+ **一次性新建应用**:`--package-id` + `--app-name` + `--create-if-missing` + [schema_apply_add_fields_all_types.json](./schema_apply_add_fields_all_types.json) 是当前推荐示例;它已包含唯一数据标题和可选数据封面(`q_linker` 使用 `https://httpbin.org/get`,`relation` 指向可读模板应用 `ead8ims5i401`)。
96
+
97
+ **一次性新建多个应用**:用 `builder schema apply --package-id <TAG_ID> --create-if-missing --apps-file tmp/apps.json`,其中 `tmp/apps.json` 主写法为 `{ "package_id": 123, "apps": [...] }`,每个 `apps[]` 写顶层 `icon + color`;`apps[].client_key` 可被同批 relation 字段的 `target_app_ref` 引用,`apps[].app_name` 可被 `target_app` 引用,工具会编译成真实 `target_app_key`。这是应用包/系统搭建的默认路径,尤其适合“商机 + 订单 + 回款”“花名册 + 工时”等互相关联场景;不要为了拿 `app_key` 先逐个创建再二次补关联字段。
98
+
99
+ **限制(平台硬性)**:每应用 **至多一个** `relation`;`--app-key` 向已有应用追加时须 **字段名不重复**、且 **不能**在已有 relation 上再增第二个。**`example.com`** 作 `q_linker` URL 易触发 **`46031`**,本清单改用 httpbin。**读回缺 `outputs[].target_field` 的 `q_linker`** 会拖死后续 `schema apply`,须修复或删除该题。
100
+
101
+ **排障**:`--json`;**`DUPLICATE_FIELD`**;**`MULTIPLE_RELATION_FIELDS_UNSUPPORTED`(49614)**。
102
+
103
+ ## 6. 交叉引用
104
+
105
+ - 交付主流程(读场 / 改场 / 发布):[QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md](./QINGFLOW_CLI_BUILDER_APP_DELIVERY_WORKFLOW.md) **§6**(类型速查与 `relation`/`q_linker`/`code_block` 要点)。
106
+ - 记录读写 `kind`(与搭建 `type` 不同系):[QINGFLOW_CLI_FIELD_DATA_TYPES.md](./QINGFLOW_CLI_FIELD_DATA_TYPES.md)。
107
+ - 主技能:[../SKILL.md](../SKILL.md)。
@@ -0,0 +1,151 @@
1
+ # 任务上下文 SOP:待办列表 → 详情 → 审批历史 → 关联报表 → 执行动作
2
+
3
+ 本文与 **MCP / 服务端公开工具命名**对齐,并给出已安装的 `**qingflow` CLI** 等价命令。编写前已在当前环境对 `**task list` / `task get` / `task log`** 实跑;`**task report`** 在抽样任务上遇到过后端 `**40038` Object not exist**(见文末「实测排障」),属数据/权限/报表生命周期问题,**不否定**标准编排顺序。
4
+
5
+ **必须落盘**:`task` 的 `list`、`get`、`log`、`report` 以及 `**task action` 的响应**若体积大,均应 `> tmp/qingflow_*.json`,与主技能 [SKILL.md](../SKILL.md) 一致。
6
+
7
+ ---
8
+
9
+ ## 1. 编排顺序(固定)
10
+
11
+
12
+ | 步骤 | 工具名(文档/编排常用) | CLI 子命令 | 说明 |
13
+ | --- | ----------------------------------- | ------------------------------- | ----------------------------------------------------------------------------- |
14
+ | 1 | `task_list` | `qingflow [--json] task list` | `--task-box todo` 为待办(可省略则默认 todo) |
15
+ | 2 | `task_get` | `qingflow [--json] task get` | **优先** `--task-id`;从响应中取动作能力、关联报表摘要 |
16
+ | 3 | `task_workflow_log_get` | `qingflow [--json] task log` | 当前任务上下文的流程/审批历史条目(与详情独立接口,勿与 `extras.workflow_log` 混为一谈;记录级全量日志用 `record logs`) |
17
+ | 4 | `task_associated_report_detail_get` | `qingflow [--json] task report` | **必须** `--report-id`;ID 来自 **步骤 2** 的关联报表列表 |
18
+ | 5 | `task_action_execute` | `qingflow [--json] task action` | **无 `--dry-run`**;调用即生效,`**--action` 须落在 `task get` 的 `available_actions` 内** |
19
+
20
+
21
+ 实现侧 CLI 将上述工具映射到 `qingflow_mcp` 的 `TaskContextTools`(`task` 子命令源码:`task list/get/log/report/action` → `task_list`、`task_get`、`task_workflow_log_get`、`task_associated_report_detail_get`、`task_action_execute`)。
22
+
23
+ ---
24
+
25
+ ## 2. 定位方式
26
+
27
+ - **唯一默认路径**:全程使用列表里拿到的 `**task_id`**(字符串十进制;与大雪花 ID 一致时保持 **引号 / JSON 字符串** 习惯,避免其它子系统精度问题)。`task_id` 必须来自 `task_list.data.items[].task_id`,不是列表序号、record_id 或 workflow_node_id。
28
+ - `task_action_execute` / `task action` **只使用 `task_id` 定位**。不要传 `app_key`、`record_id`、`workflow_node_id`,也不要从其它输出里自行拼三键。
29
+ - `task get` / `task log` / `task report` 的底层读接口仍可能保留完整 locator 作为兼容排障能力;主链路不使用。
30
+
31
+ ---
32
+
33
+ ## 3. 步骤说明与命令模板
34
+
35
+ ### 3.1 `task_list`(`task list`)
36
+
37
+ ```bash
38
+ qingflow --json task list \
39
+ --task-box todo \
40
+ --flow-status all \
41
+ --page 1 \
42
+ --page-size 50 \
43
+ > tmp/qingflow_tasks_todo.json
44
+ ```
45
+
46
+ - `**--task-box**`:`todo`(待办)、`done`(已办)、`cc`(抄送)、`initiated`(我发起)等;与 `**--flow-status**`(箱内流程状态)为 **两个独立维度**,可同一次请求带给后端。
47
+ - `**--query`**:优先走后端检索;若后端零条,公开列表可能 **本地回退**匹配应用名、节点名、`app_key`、`record_id`(JSON 中可能出现 `TASK_LIST_QUERY_FALLBACK_APPLIED` 类警告)。
48
+ - `**--app-key`**:列表阶段 **常省略**(不按应用过滤);与下钻阶段不同。
49
+
50
+ 从 `data.items[]` 取 `**task_id`** 进入下一步。
51
+
52
+ ### 3.2 `task_get`(`task get`)
53
+
54
+ ```bash
55
+ qingflow --json task get \
56
+ --task-id "<TASK_ID>" \
57
+ > tmp/qingflow_task_get.json
58
+ ```
59
+
60
+ 可选(默认开启,大响应可关):`--no-include-candidates`、`--no-include-associated-reports`。
61
+
62
+ **解析要点**(以 CLI `--json` 紧凑载荷为准,字段名以你环境实际 JSON 为准):
63
+
64
+ - `**data.task`**:`app_key`、`record_id`、`workflow_node_id`、`workflow_node_name`、`actionable`、`task_id`。
65
+ - `**data.available_actions`**:当前节点 **允许** 的动作子集(**下文 `task action` 的 `--action` 必须落在此列表中**,否则 CLI 报 config)。
66
+ - `**data.editable_fields`**:可编辑字段(与 `**save_only`** 等配合)。
67
+ - `**data.extras**`:摘要信息,例如 `**workflow_log**`(是否可见、条数提示等)、`**associated_reports**`(`count`、`items[]`,每项含 `**report_id**` / `chart_key` / `chart_name` 等)。
68
+
69
+ ### 3.3 `task_workflow_log_get`(`task log`)
70
+
71
+ ```bash
72
+ qingflow --json task log \
73
+ --task-id "<TASK_ID>" \
74
+ > tmp/qingflow_task_log.json
75
+ ```
76
+
77
+ - 典型结构:`data.items[]` 含节点、操作者、`operation`、`operation_time`、备注与附件等(具体字段以后端为准)。
78
+ - `**data.visibility**`:审计/机器人日志可见性等元数据。
79
+ - 这不是记录详情页的全量数据日志工具;如果用户要某条记录的完整数据日志 + 流程日志,先定位 `app_key/record_id/view_id`,再用 `qingflow --json record logs ...`。
80
+
81
+ ### 3.4 `task_associated_report_detail_get`(`task report`)
82
+
83
+ 1. 在 `**task get**` 的 `**data.extras.associated_reports.items**` 中选一条,读取其 `**report_id**`(整数)。
84
+ 2. 若 `**count == 0**`,跳过本步,无 `**--report-id**` 可填。
85
+
86
+ ```bash
87
+ qingflow --json task report \
88
+ --task-id "<TASK_ID>" \
89
+ --report-id <REPORT_ID> \
90
+ --page 1 \
91
+ --page-size 20 \
92
+ > tmp/qingflow_task_report.json
93
+ ```
94
+
95
+ - 定位方式与 `task get` 相同:默认传 `task_list.data.items[].task_id`。
96
+ - 若关联项是 QingBI 报表,详情读取优先走前端同源的 **`/qingbi/charts/data/qflow/{chartId}/detail`**,再按 qflow `asos` 降级;只有 qflow 路由明确不存在/不适用时才退回旧 BI 数据接口。不要把中间 **`CHART_SEE` / 40002** 当成任务关联报表最终不可见。
97
+
98
+ ### 3.5 `task_action_execute`(`task action`)
99
+
100
+ ```bash
101
+ qingflow --json task action \
102
+ --task-id "<TASK_ID>" \
103
+ --action <ACTION> \
104
+ [--payload-file tmp/task_action_payload.json] \
105
+ [--fields-file tmp/task_action_fields.json] \
106
+ > tmp/qingflow_task_action.json
107
+ ```
108
+
109
+ `**--action` 允许字面量**(实现校验,大小写不敏感):
110
+ `approve`、`reject`、`rollback`、`transfer`、`urge`、`save_only`。
111
+
112
+ **约束摘要**(与实现一致):
113
+
114
+ - **定位只认 `task_id`**:来自 `task list` 的 `data.items[].task_id`;`app_key`、`record_id`、`workflow_node_id` 不是动作执行入参。
115
+ - `**approve` / `reject`**:执行上下文以当前待办详情或用户/前端已给出的显式 `formId` 为准;只有缺少 `formId` 时才兜底读取应用 baseInfo。不要把应用 baseInfo 的 `40002` 当作待办动作最终无权结论,最终是否可执行以后端审批动作接口为准。
116
+ - `**save_only`**:必须 提供非空 `**--fields-file`**(或 payload 与 fields 不能同时塞 `answers` 与 fields,具体以报错为准)。以当前待办节点的 `data.editable_fields` / `editableQueIds` 为准;若 `editableQueIds` 被当前权限链路挡住,但任务详情里的 `queAuthSetting` 已暴露可编辑字段,CLI 会按任务详情继续允许 save-only,并由最终保存接口裁决。申请表单 schema 或辅助 `editableQueIds` 的 `40002` 不是待办字段保存的最终无权结论。
117
+ - `**transfer**`:`**payload` 内需要** `target_member_id`(或别名 `uid` / `targetMemberId`);不能转给当前登录人。
118
+ - **意见必填**:若详情里 `**action_constraints.feedback_required_for`** 包含当前动作,需在 `**payload.audit_feedback`**(实现从 payload 抽取审计意见,具体键名以 `--help` / 服务端为准)中提供,否则会 config 错误。
119
+ - `**urge`**:**不支持** 与 `fields` 同时提交。
120
+ - **禁止臆测动作**:只允许 `**data.available_actions`** 中出现的动作;`approve` 不在列表则不可硬放行。
121
+
122
+ `**--payload-file` / `--fields-file`**:UTF-8 JSON 对象,与 `record update` 同类「文件用 JSON」习惯一致。
123
+
124
+ ---
125
+
126
+ ## 4. 与「应用内 `record list`」的边界
127
+
128
+ - **任务中心待办 / 已办 / 抄送**:用 `**task list --task-box …`**,**不要**再用过时的 `record list --view-id system:todo` 等当作任务箱。
129
+ - **业务表读数**:仍在 `app get` 的 `**accessible_views`** 里选 `**custom:`* 等**,走 [QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md](./QINGFLOW_CLI_DATA_RETRIEVAL_WORKFLOW.md)。
130
+
131
+ ---
132
+
133
+ ## 5. 实测与排障(编写时实跑)
134
+
135
+
136
+ | 命令 | 结果 |
137
+ | --------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
138
+ | `task list --task-box todo` | 返回多条,`items[].task_id` 为字符串;列表项可能不含 `app_key`(详情阶段再解析)。 |
139
+ | `task get --task-id …` | `data.task` 含完整定位;`available_actions` 示例含 `approve`/`reject`/`rollback`/`transfer`;`extras.associated_reports` 可为 `count: 0` 或大于 0。 |
140
+ | `task log --task-id …` | 返回 `data.items` 审批/流程历史。 |
141
+ | `task report --task-id … --report-id …` | 某条 `**count>0`** 的抽样上曾返回后端 `**40038` Object not exist**;同一 `task get` 里仍列出 `report_id`。**建议**:换 `report_id`、确认报表未删除、核对轻流 BI 权限与节点关联;仍失败时保留 `request_id` 报障。 |
142
+
143
+
144
+ **说明**:本文编写时仅用读接口抽样校验;`**task action`** 可依照 **第 3.5 节** 在自动化剧本中直接调用,由编排侧控制目标待办与动作,避免误批。
145
+
146
+ ---
147
+
148
+ ## 6. 交叉引用
149
+
150
+ - 主技能 [SKILL.md](../SKILL.md):**落盘规则**、`**task-box` / `flow-status`** 枚举、与 `record` 混用雷区。
151
+ - [QINGFLOW_CLI_MEMBER_CHEATSHEET.md](./QINGFLOW_CLI_MEMBER_CHEATSHEET.md):成员侧最短路径。
@@ -0,0 +1,18 @@
1
+ [
2
+ {
3
+ "name": "BAT_sub",
4
+ "type": "subtable",
5
+ "subfields": [
6
+ {"name": "BAT_sub_a", "type": "text"},
7
+ {"name": "BAT_sub_b", "type": "number"}
8
+ ]
9
+ },
10
+ {
11
+ "name": "BAT_rel",
12
+ "type": "relation",
13
+ "target_app_key": "ead8ims5i401",
14
+ "relation_mode": "single",
15
+ "display_field": {"name": "单行文字"},
16
+ "visible_fields": [{"name": "多行文字"}]
17
+ }
18
+ ]
@@ -0,0 +1,17 @@
1
+ [
2
+ {"name": "BAT_text", "type": "text", "description": "批测"},
3
+ {"name": "BAT_long_text", "type": "long_text"},
4
+ {"name": "BAT_number", "type": "number"},
5
+ {"name": "BAT_amount", "type": "amount"},
6
+ {"name": "BAT_date", "type": "date"},
7
+ {"name": "BAT_datetime", "type": "datetime"},
8
+ {"name": "BAT_member", "type": "member"},
9
+ {"name": "BAT_phone", "type": "phone"},
10
+ {"name": "BAT_email", "type": "email"},
11
+ {"name": "BAT_address", "type": "address"},
12
+ {"name": "BAT_attachment", "type": "attachment"},
13
+ {"name": "BAT_boolean", "type": "boolean"},
14
+ {"name": "BAT_single_select", "type": "single_select", "options": ["A", "B"], "required": false},
15
+ {"name": "BAT_multi_select", "type": "multi_select", "options": ["X", "Y"], "required": false},
16
+ {"name": "BAT_dept", "type": "department", "department_scope": {"mode": "all"}}
17
+ ]
@@ -0,0 +1 @@
1
+ ["CHART_ID_TO_DELETE"]