weaver-e10-builder 1.6.9 → 1.6.11

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 (212) hide show
  1. package/dist/commands/form-data.d.ts.map +1 -1
  2. package/dist/commands/form-data.js +4 -1
  3. package/dist/commands/form-data.js.map +1 -1
  4. package/dist/commands/report-admin.d.ts.map +1 -1
  5. package/dist/commands/report-admin.js +378 -2
  6. package/dist/commands/report-admin.js.map +1 -1
  7. package/dist/commands/report-validate.d.ts.map +1 -1
  8. package/dist/commands/report-validate.js +1066 -3
  9. package/dist/commands/report-validate.js.map +1 -1
  10. package/dist/commands/report.d.ts.map +1 -1
  11. package/dist/commands/report.js +341 -3
  12. package/dist/commands/report.js.map +1 -1
  13. package/dist/commands/workflow-after-submit-comment.d.ts +3 -0
  14. package/dist/commands/workflow-after-submit-comment.d.ts.map +1 -0
  15. package/dist/commands/workflow-after-submit-comment.js +56 -0
  16. package/dist/commands/workflow-after-submit-comment.js.map +1 -0
  17. package/dist/commands/workflow-setting.d.ts.map +1 -1
  18. package/dist/commands/workflow-setting.js +2 -67
  19. package/dist/commands/workflow-setting.js.map +1 -1
  20. package/dist/services/action-detail.d.ts.map +1 -1
  21. package/dist/services/action-detail.js +1 -17
  22. package/dist/services/action-detail.js.map +1 -1
  23. package/dist/services/actionflow/components/action_eb_update_compiler.d.ts.map +1 -1
  24. package/dist/services/actionflow/components/action_eb_update_compiler.js +6 -2
  25. package/dist/services/actionflow/components/action_eb_update_compiler.js.map +1 -1
  26. package/dist/services/actionflow/components/custom_trigger.js +1 -1
  27. package/dist/services/actionflow/components/custom_trigger.js.map +1 -1
  28. package/dist/services/actionflow/components/dml_execute.js +1 -1
  29. package/dist/services/actionflow/components/dml_execute.js.map +1 -1
  30. package/dist/services/actionflow/components/document_actions.js +1 -2
  31. package/dist/services/actionflow/components/document_actions.js.map +1 -1
  32. package/dist/services/actionflow/components/eb_get_data_by_id.d.ts.map +1 -1
  33. package/dist/services/actionflow/components/eb_get_data_by_id.js +2 -2
  34. package/dist/services/actionflow/components/eb_get_data_by_id.js.map +1 -1
  35. package/dist/services/actionflow/components/form_data_builder.d.ts.map +1 -1
  36. package/dist/services/actionflow/components/form_data_builder.js +18 -10
  37. package/dist/services/actionflow/components/form_data_builder.js.map +1 -1
  38. package/dist/services/actionflow/components/hrm_actions.js +0 -1
  39. package/dist/services/actionflow/components/hrm_actions.js.map +1 -1
  40. package/dist/services/actionflow/components/webhook_trigger.js +3 -3
  41. package/dist/services/actionflow/components/webhook_trigger.js.map +1 -1
  42. package/dist/services/actionflow/components/workflow_actions.js +3 -2
  43. package/dist/services/actionflow/components/workflow_actions.js.map +1 -1
  44. package/dist/services/actionflow/filter_rules.d.ts.map +1 -1
  45. package/dist/services/actionflow/filter_rules.js +15 -13
  46. package/dist/services/actionflow/filter_rules.js.map +1 -1
  47. package/dist/services/actionflow/form_source.d.ts.map +1 -1
  48. package/dist/services/actionflow/form_source.js +59 -11
  49. package/dist/services/actionflow/form_source.js.map +1 -1
  50. package/dist/services/actionflow/formula_service.d.ts.map +1 -1
  51. package/dist/services/actionflow/formula_service.js +4 -1
  52. package/dist/services/actionflow/formula_service.js.map +1 -1
  53. package/dist/services/actionflow/option_field_types.d.ts +5 -0
  54. package/dist/services/actionflow/option_field_types.d.ts.map +1 -0
  55. package/dist/services/actionflow/option_field_types.js +20 -0
  56. package/dist/services/actionflow/option_field_types.js.map +1 -0
  57. package/dist/services/actionflow/param_group_dto.d.ts.map +1 -1
  58. package/dist/services/actionflow/param_group_dto.js +7 -3
  59. package/dist/services/actionflow/param_group_dto.js.map +1 -1
  60. package/dist/services/actionflow/preflight_service.d.ts.map +1 -1
  61. package/dist/services/actionflow/preflight_service.js +42 -18
  62. package/dist/services/actionflow/preflight_service.js.map +1 -1
  63. package/dist/services/actionflow/webhook_source.d.ts.map +1 -1
  64. package/dist/services/actionflow/webhook_source.js +9 -4
  65. package/dist/services/actionflow/webhook_source.js.map +1 -1
  66. package/dist/services/actionflow/workflow_compiler.d.ts.map +1 -1
  67. package/dist/services/actionflow/workflow_compiler.js +11 -8
  68. package/dist/services/actionflow/workflow_compiler.js.map +1 -1
  69. package/dist/services/report/blueprint.d.ts +109 -0
  70. package/dist/services/report/blueprint.d.ts.map +1 -0
  71. package/dist/services/report/blueprint.js +322 -0
  72. package/dist/services/report/blueprint.js.map +1 -0
  73. package/dist/services/report/build.d.ts.map +1 -1
  74. package/dist/services/report/build.js +8 -3
  75. package/dist/services/report/build.js.map +1 -1
  76. package/dist/services/report/card.d.ts +65 -0
  77. package/dist/services/report/card.d.ts.map +1 -0
  78. package/dist/services/report/card.js +151 -0
  79. package/dist/services/report/card.js.map +1 -0
  80. package/dist/services/report/copy-health.d.ts +31 -0
  81. package/dist/services/report/copy-health.d.ts.map +1 -0
  82. package/dist/services/report/copy-health.js +87 -0
  83. package/dist/services/report/copy-health.js.map +1 -0
  84. package/dist/services/report/dataset-heal.d.ts +64 -0
  85. package/dist/services/report/dataset-heal.d.ts.map +1 -0
  86. package/dist/services/report/dataset-heal.js +89 -0
  87. package/dist/services/report/dataset-heal.js.map +1 -0
  88. package/dist/services/report/dataset-ids.d.ts +70 -0
  89. package/dist/services/report/dataset-ids.d.ts.map +1 -0
  90. package/dist/services/report/dataset-ids.js +149 -0
  91. package/dist/services/report/dataset-ids.js.map +1 -0
  92. package/dist/services/report/dataset.d.ts.map +1 -1
  93. package/dist/services/report/dataset.js +3 -1
  94. package/dist/services/report/dataset.js.map +1 -1
  95. package/dist/services/report/empty-dataset.d.ts +51 -0
  96. package/dist/services/report/empty-dataset.d.ts.map +1 -0
  97. package/dist/services/report/empty-dataset.js +213 -0
  98. package/dist/services/report/empty-dataset.js.map +1 -0
  99. package/dist/services/report/field-binding.d.ts +75 -0
  100. package/dist/services/report/field-binding.d.ts.map +1 -0
  101. package/dist/services/report/field-binding.js +160 -0
  102. package/dist/services/report/field-binding.js.map +1 -0
  103. package/dist/services/report/forms.d.ts +32 -0
  104. package/dist/services/report/forms.d.ts.map +1 -1
  105. package/dist/services/report/forms.js +90 -0
  106. package/dist/services/report/forms.js.map +1 -1
  107. package/dist/services/report/formula-parent.d.ts +25 -0
  108. package/dist/services/report/formula-parent.d.ts.map +1 -0
  109. package/dist/services/report/formula-parent.js +116 -0
  110. package/dist/services/report/formula-parent.js.map +1 -0
  111. package/dist/services/report/locate.d.ts +40 -0
  112. package/dist/services/report/locate.d.ts.map +1 -0
  113. package/dist/services/report/locate.js +122 -0
  114. package/dist/services/report/locate.js.map +1 -0
  115. package/dist/services/report/reports.d.ts +4 -0
  116. package/dist/services/report/reports.d.ts.map +1 -1
  117. package/dist/services/report/reports.js +4 -2
  118. package/dist/services/report/reports.js.map +1 -1
  119. package/dist/services/report/scene.d.ts +22 -0
  120. package/dist/services/report/scene.d.ts.map +1 -0
  121. package/dist/services/report/scene.js +145 -0
  122. package/dist/services/report/scene.js.map +1 -0
  123. package/dist/services/report/types.d.ts +28 -0
  124. package/dist/services/report/types.d.ts.map +1 -1
  125. package/dist/services/report/verify.d.ts +24 -0
  126. package/dist/services/report/verify.d.ts.map +1 -1
  127. package/dist/services/report/verify.js +115 -2
  128. package/dist/services/report/verify.js.map +1 -1
  129. package/dist/services/report/workbook.d.ts +12 -1
  130. package/dist/services/report/workbook.d.ts.map +1 -1
  131. package/dist/services/report/workbook.js +107 -8
  132. package/dist/services/report/workbook.js.map +1 -1
  133. package/dist/services/workflow-after-submit-comment.d.ts +20 -0
  134. package/dist/services/workflow-after-submit-comment.d.ts.map +1 -0
  135. package/dist/services/workflow-after-submit-comment.js +81 -0
  136. package/dist/services/workflow-after-submit-comment.js.map +1 -0
  137. package/ecodecmd/src/build.js +36 -12
  138. package/package.json +1 -1
  139. package/skill-docs/SKILL.md +4 -4
  140. package/skill-docs/_catalog.md +10 -1
  141. package/skill-docs/actionflows/SKILL.md +1 -0
  142. package/skill-docs/actionflows/references/components/ebuilder/EnquiryForm.md +2 -2
  143. package/skill-docs/actionflows/references/components/trigger/CustomTrigger.md +2 -0
  144. package/skill-docs/actionflows/references/components/trigger/WebHookTrigger.md +19 -1
  145. package/skill-docs/references/coding/guide-form-crud.md +2 -0
  146. package/skill-docs/references/coding/guide-page-code.md +108 -0
  147. package/skill-docs/references/components/ref-component-common.md +98 -0
  148. package/skill-docs/references/report/add-dataset-registration-guide.md +44 -22
  149. package/skill-docs/references/report/advanced-crosstab-tree-guide.md +82 -0
  150. package/skill-docs/references/report/ai-report-workflow.md +100 -100
  151. package/skill-docs/references/report/api-auth-headers.md +129 -0
  152. package/skill-docs/references/report/api-report-build-fast-path.md +274 -275
  153. package/skill-docs/references/report/app-datasource-lookup-guide.md +230 -230
  154. package/skill-docs/references/report/branch-scene-guide.md +293 -0
  155. package/skill-docs/references/report/build-resilience-guide.md +99 -0
  156. package/skill-docs/references/report/card-report-guide.md +110 -0
  157. package/skill-docs/references/report/cell-config-echo-guide.md +44 -9
  158. package/skill-docs/references/report/cell-transform-guide.md +9 -9
  159. package/skill-docs/references/report/column-split-report-guide.md +96 -0
  160. package/skill-docs/references/report/condition-format-guide.md +49 -24
  161. package/skill-docs/references/report/cross-dataset-filter-guide.md +180 -69
  162. package/skill-docs/references/report/crosstab-raw-workbook-guide.md +265 -0
  163. package/skill-docs/references/report/dataset-query-filter-guide.md +231 -235
  164. package/skill-docs/references/report/dataset-sidebar-ops-guide.md +75 -34
  165. package/skill-docs/references/report/dataset-table-field-contract-guide.md +196 -46
  166. package/skill-docs/references/report/datasource-metadata-guide.md +128 -119
  167. package/skill-docs/references/report/datasource-selection-and-parent-filter-guide.md +183 -148
  168. package/skill-docs/references/report/date-range-query-reference.md +97 -102
  169. package/skill-docs/references/report/default-header-style-guide.md +8 -8
  170. package/skill-docs/references/report/designer-blank-prevention-guide.md +8 -8
  171. package/skill-docs/references/report/dossier-report-guide.md +69 -0
  172. package/skill-docs/references/report/drilldown-report-guide.md +59 -0
  173. package/skill-docs/references/report/event-hreflink-guide.md +53 -47
  174. package/skill-docs/references/report/expansion-block-parent-config-guide.md +208 -0
  175. package/skill-docs/references/report/failure-cleanup-and-nonblocking-guide.md +183 -0
  176. package/skill-docs/references/report/field-bind-exact-match-guide.md +208 -208
  177. package/skill-docs/references/report/financial-stats-report-guide.md +89 -0
  178. package/skill-docs/references/report/formula-cell-api-guide.md +325 -258
  179. package/skill-docs/references/report/golden-case-summary-sheet.md +200 -0
  180. package/skill-docs/references/report/guide-report-build.md +6 -1
  181. package/skill-docs/references/report/hidden-row-and-header-merge-guide.md +10 -10
  182. package/skill-docs/references/report/layout-showlocation-freeze-guide.md +39 -10
  183. package/skill-docs/references/report/multi-report-crosstab-casebook.md +263 -0
  184. package/skill-docs/references/report/paged-group-report-guide.md +68 -0
  185. package/skill-docs/references/report/preaggregate-auto-disable-guide.md +24 -13
  186. package/skill-docs/references/report/query-condition-api-guide.md +145 -125
  187. package/skill-docs/references/report/ref-report-commands.md +41 -3
  188. package/skill-docs/references/report/report-baseinfo-defaults-guide.md +117 -117
  189. package/skill-docs/references/report/report-building-guide.md +52 -52
  190. package/skill-docs/references/report/report-case-prompts.md +104 -0
  191. package/skill-docs/references/report/report-config-schema.md +281 -281
  192. package/skill-docs/references/report/report-create-modes-guide.md +73 -35
  193. package/skill-docs/references/report/report-data-model.md +131 -131
  194. package/skill-docs/references/report/report-delivery-contract.md +23 -3
  195. package/skill-docs/references/report/report-designer-feature-map.md +30 -35
  196. package/skill-docs/references/report/report-help-guide.md +304 -0
  197. package/skill-docs/references/report/report-internal-error-guide.md +71 -0
  198. package/skill-docs/references/report/report-knowledge-base.md +27 -27
  199. package/skill-docs/references/report/report-lifecycle-api-guide.md +126 -94
  200. package/skill-docs/references/report/report-list-page-ops-guide.md +54 -8
  201. package/skill-docs/references/report/report-permission-guide.md +99 -102
  202. package/skill-docs/references/report/report-requirement-analysis-template.md +93 -29
  203. package/skill-docs/references/report/report-self-check-guide.md +164 -164
  204. package/skill-docs/references/report/report-verification-discipline-guide.md +203 -0
  205. package/skill-docs/references/report/safe-partial-save-guide.md +151 -151
  206. package/skill-docs/references/report/shard-freeze-report-guide.md +97 -0
  207. package/skill-docs/references/report/snowflake-id-precision-guide.md +84 -84
  208. package/skill-docs/references/report/spec-build-schema.md +71 -71
  209. package/skill-docs/references/report/targeted-fix-guide.md +155 -97
  210. package/skill-docs/references/report/user-request-scope-only-guide.md +23 -4
  211. package/skill-docs/references/workflow/guide-after-submit-comment.md +58 -0
  212. package/skill-docs/references/workflow/guide-workflow.md +6 -0
@@ -1,151 +1,151 @@
1
- # 局部功能保存 · 设计区保护规范
2
-
3
- > **适用场景**:通过 API / 脚本修改**快照、权限、查询条件、数据集筛选、页面布局**等「非布局主体」能力时。
4
- > **事故根因(已验证)**:对 `saveReportDevInfo` 提交**空 workbook** 或 **workbookProps.sheets 无单元格** 的 payload,会**全量替换** `edc_reportsheetfield_dev` 等表,导致设计器字段与单元格配置被清空。
5
- > **硬约束**:除非用户**明确要求删除/清除字段**,否则设计区域内容**不得被置空**。
6
-
7
- ---
8
-
9
- ## 一、核心原则
10
-
11
- | 原则 | 说明 |
12
- |------|------|
13
- | **布局不可丢** | `getReportDevInfo.fields.length > 0` 是保存前最低门槛;保存后必须回读验证 |
14
- | **禁止空 workbook 落库** | `workbookProps.sheets` 为空 **且** `fields` 为空 → **禁止**调用 `saveReportDevInfo`;`fields>0` 但 sheets 空 → **必须从 fields 重建**(含 spans)后再存 |
15
- | **剔除 `(0,0)` 空 Const** | `x=0,y=0` 且文案空的 Const 为异常幽灵格,保存前删除 |
16
- | **workbookProps 不可盲信** | 线上常见 `fields=12` 但 `workbookProps.dataTable` 为空;此时必须从 **fields 重建** dataTable |
17
- | **groupId 原样回传** | `saveReportDevInfo` 的 `groupId`/`nameLabelId` 取自 `baseInfo`,禁止硬编码改分组 |
18
- | **专用接口优先** | 权限 → `savePermissions`;查询 → `saveQueryItem`;数据集筛选 → `queryInfoSave`;版本还原 → `rollbackReport` |
19
- | **快照是唯一例外需写 workbook** | 快照配置挂在 `workbook.snapshot`,必须走 `saveReportDevInfo`,但须套用 **§三 安全合并** |
20
- | **快照 ID 禁 Number()** | `sheetId`/`id`/`jobId` 必须 **String(雪花)**;`Number(reportId)` 截断 → **设计区空白**(见 `designer-blank-prevention-guide.md`) |
21
- | **快照默认名** | 未指定时 **`{报表名}-{date}-{time}`** |
22
- | **showLocation 禁 0** | 合并 workbook 时若为 0 → 改为 **2**(或用户点名居左则 1) |
23
- | **保存前快照版本** | 对已有报表做任意 `saveReportDevInfo` 前,建议 `saveAsVersion --list-only` 确认有版本可还原 |
24
-
25
- ---
26
-
27
- ## 二、接口与布局影响矩阵
28
-
29
- | 操作 | 推荐接口 | 是否触及 layout/fields | 备注 |
30
- |------|----------|------------------------|------|
31
- | 使用/禁用权限 | `permission/table/savePermissions` | **否** | 全量替换权限 JSON,不动 fields |
32
- | 矩阵权限 | `permission/matrix/save` | **否** | |
33
- | 查询条件 | `saveQueryItem` / `saveQueryConfig` | **否** | |
34
- | 数据集筛选 | `queryInfoSave` | **否** | |
35
- | 存为新版本 | `saveAsVersion` | **否**(复制快照) | |
36
- | 还原版本 | `GET rollbackReport` | **是**(整表替换) | 用户显式还原;会重置快照配置 |
37
- | 页面布局/分页 | `allPageSetting` | **否**(pageSetting 独立) | |
38
- | 定时快照 | `saveReportDevInfo` + `workbook.snapshot` | **是(若 workbook 空则灾难)** | 必须 §三 |
39
- | 修改单元格/父格 | `saveReportDevInfo` +(可选)`saveReportWithCacheConfig` | **是** | **新空报表必须先 dev 后 cache**;见 `api-report-build-fast-path.md` |
40
-
41
- ---
42
-
43
- ## 三、saveReportDevInfo 安全合并流程(强制)
44
-
45
- 凡必须调用 `saveReportDevInfo` 且**不是**「从零新建报表」或「用户要求清空」:
46
-
47
- ```
48
- 1. GET getReportDevInfo?reportId=
49
- 2. fields = data.fields || []
50
- 3. IF fields.length === 0 → ABORT(提示先 rollbackReport 或从版本还原)
51
- 4. wb = mergeWorkbookSafe(data, patchFn)
52
- ├─ 若 countCells(workbookProps) > 0 → 深拷贝 workbookProps
53
- └─ 否则 → buildWorkbookFromFields(fields, sheetProps) // 禁止提交空 sheets
54
- 5. patchFn(wb) // 仅改 snapshot / 必要字段,勿删 sheets/dataTable
55
- 6. POST saveReportDevInfo(带当前 designKey + ebBusinessId)
56
- 7. GET getReportDevInfo 回读:
57
- - fields.length 不得小于保存前
58
- - 若用户未要求删除,fields.length === 0 → 视为事故,立即 rollbackReport
59
- ```
60
-
61
- ### 3.1 buildWorkbookFromFields 最低要求
62
-
63
- 从 `fields[]` 重建时,每个 field 须写入 `data.dataTable[x][y].style`(含 `cellType` 关键属性:父格、fieldKey、leftFilterField 等)。
64
- **禁止**只提交 `{ version, sheets: {} }` 或 `{ sheets: { Sheet1: { data: { dataTable: {} } } } }`。
65
-
66
- **Const(强制)**:`style.cellType` 须为完整 `ConstCellType`,至少包含:
67
-
68
- | 属性 | 说明 |
69
- |------|------|
70
- | `cellText` | 有展示文案时必填 |
71
- | `leftX/leftY/topX/topY` | **禁止省略**(省略 → Java int 默认 0 → 伪自定义 A1) |
72
- | `pcHreflink` / `openType` | 超链接 Const 必填;服务端**只认 workbook cellType**,不只认 `fields[]` 顶层 |
73
-
74
- 普通 Const 父格写 `-1`;超链接含 `{L4}` 时左父格指向依赖数据格。见 `report-self-check-guide.md` §2.2。
75
-
76
- **定点修改**:用户只点一格时,语义上只改该格,但仍须整表 fields 原样写回 + 完整 workbook(见 `targeted-fix-guide.md`)。
77
-
78
- ### 3.2 快照配置 patch 示例
79
-
80
- ```javascript
81
- const wb = mergeWorkbookSafe(dev.data, (wb) => {
82
- wb.snapshot = {
83
- enableSnapshot: true,
84
- cron: '0 0 0 * * ?',
85
- // 未指定名称时的平台默认
86
- snapshotName: `${reportName}-{date}-{time}`,
87
- params: JSON.stringify({ searchCondition: {} }),
88
- // 雪花 ID 必须字符串,禁止 Number(reportId)(超 MAX_SAFE_INTEGER 会截断,设计器空白)
89
- sheetId: String(reportId),
90
- // 更新已有配置时可带 id/jobId(同样用 String);rollback 后建议 omit 让服务端重建调度
91
- };
92
- // 禁止 showLocation=0
93
- for (const sh of Object.values(wb.sheets || {})) {
94
- if (!sh.tag) sh.tag = {};
95
- if (!sh.tag.showLocation || sh.tag.showLocation === 0) sh.tag.showLocation = 2;
96
- }
97
- });
98
- ```
99
-
100
- ### 3.3 保存后验证清单
101
-
102
- | 检查项 | 通过标准 |
103
- |--------|----------|
104
- | `fields.length` | ≥ 保存前数量(允许只增不减,除非用户要求删格) |
105
- | 关键单元格抽样 | 如 `(1,0)` 供应商等级 `(1,5)` 进货单编号 仍存在 |
106
- | `snapshot.enableSnapshot` | 若本次任务为开快照,回读为 true |
107
- | 设计器肉眼 | 提示用户 Ctrl+F5 强刷 |
108
-
109
- ---
110
-
111
- ## 四、事故恢复
112
-
113
- | 现象 | 处理 |
114
- |------|------|
115
- | 设计器空白、`fields.length===0` | `GET rollbackReport?sheetId={版本snapshotId}&originSheetId={reportId}` |
116
- | 无版本 | 查 `getVersionWithPage`;或本地 `inspect_*.json` / 脚本备份 |
117
- | 快照误伤后 layout 已还原 | 快照需在设计器 **页面→快照设置** 重配,或 `set_snapshot_schedule.js`(已含保护) |
118
-
119
- **版本 ID 来源**:`save_as_version.js --list-only` → `snapshotId` 列。
120
-
121
- ---
122
-
123
- ## 五、脚本与模块
124
-
125
- | 文件 | 用途 |
126
- |------|------|
127
- | `e10-cli report fix(内部走安全局部保存)` | **安全合并 workbook**、保存前断言、保存后验证 |
128
- | `e10-cli report snapshot set` | 定时快照(默认名 `{报表名}-{date}-{time}`;ID 字符串;依赖 safe_report_save) |
129
- | `e10-cli report verify blank` | 防设计区空白门禁(sheetId/showLocation/scrollPage) |
130
- | `e10-cli report restore` | 从版本 snapshotId 还原 layout |
131
- | `references/designer-blank-prevention-guide.md` | **设计区空白禁令与定位** |
132
- | `已由 CLI 取代` | `merge_workbook_safe` / `assert_layout_not_empty` / `verify_layout_preserved` |
133
-
134
- ---
135
-
136
- ## 六、Agent 硬约束(写入 SKILL 摘要)
137
-
138
- 1. **默认禁止**对已有报表提交空 workbook 或 0 fields 的 `saveReportDevInfo`。
139
- 2. 设置权限 / 查询 / 数据集筛选 / 菜单 / 复制 → **不得**调用 `saveReportDevInfo`。
140
- 3. 设置快照 → **必须** `mergeWorkbookSafe` + 保存前后 `fields.length` 校验。
141
- 4. 用户未说「删除」「清空」「重置布局」→ **任何步骤不得使 fields 归零**。
142
- 5. 保存前:`saveAsVersion` 或确认 `getVersionWithPage` 有可用版本。
143
- 6. 保存后:`getReportDevInfo` 回读失败 → **停止后续操作**,优先 `rollbackReport`。
144
-
145
- ---
146
-
147
- ## 七、源码依据
148
-
149
- - `EdcReportDevDesignServiceImpl.saveReportDevInfoLast`:解压 workbook → `getFieldsFromWorkbook(wb)` → **全量** `saveFields` 覆盖 DB。
150
- - `Workbook.snapshot`:`saveSnapshot(wb, reportId)` 仅在 workbook 含 snapshot 对象时执行;与 fields 同一保存事务。
151
- - `workbookProps` 与 `fields` 分离存储;API 返回的 `workbookProps.sheets` 常不含 dataTable,**不能**作为唯一布局来源。
1
+ # 局部功能保存 · 设计区保护规范
2
+
3
+ > **适用场景**:通过 API / 脚本修改**快照、权限、查询条件、数据集筛选、页面布局**等「非布局主体」能力时。
4
+ > **事故根因(已验证)**:对 `saveReportDevInfo` 提交**空 workbook** 或 **workbookProps.sheets 无单元格** 的 payload,会**全量替换** `edc_reportsheetfield_dev` 等表,导致设计器字段与单元格配置被清空。
5
+ > **硬约束**:除非用户**明确要求删除/清除字段**,否则设计区域内容**不得被置空**。
6
+
7
+ ---
8
+
9
+ ## 一、核心原则
10
+
11
+ | 原则 | 说明 |
12
+ |------|------|
13
+ | **布局不可丢** | `getReportDevInfo.fields.length > 0` 是保存前最低门槛;保存后必须回读验证 |
14
+ | **禁止空 workbook 落库** | `workbookProps.sheets` 为空 **且** `fields` 为空 → **禁止**调用 `saveReportDevInfo`;`fields>0` 但 sheets 空 → **必须从 fields 重建**(含 spans)后再存 |
15
+ | **剔除 `(0,0)` 空 Const** | `x=0,y=0` 且文案空的 Const 为异常幽灵格,保存前删除 |
16
+ | **workbookProps 不可盲信** | 线上常见 `fields=12` 但 `workbookProps.dataTable` 为空;此时必须从 **fields 重建** dataTable |
17
+ | **groupId 原样回传** | `saveReportDevInfo` 的 `groupId`/`nameLabelId` 取自 `baseInfo`,禁止硬编码改分组 |
18
+ | **专用接口优先** | 权限 → `savePermissions`;查询 → `saveQueryItem`;数据集筛选 → `queryInfoSave`;版本还原 → `rollbackReport` |
19
+ | **快照是唯一例外需写 workbook** | 快照配置挂在 `workbook.snapshot`,必须走 `saveReportDevInfo`,但须套用 **§三 安全合并** |
20
+ | **快照 ID 禁 Number()** | `sheetId`/`id`/`jobId` 必须 **String(雪花)**;`Number(reportId)` 截断 → **设计区空白**(见 `designer-blank-prevention-guide.md`) |
21
+ | **快照默认名** | 未指定时 **`{报表名}-{date}-{time}`** |
22
+ | **showLocation 禁 0** | 合并 workbook 时若为 0 → 改为 **2**(或用户点名居左则 1) |
23
+ | **保存前快照版本** | 对已有报表做任意 `saveReportDevInfo` 前,建议 `saveAsVersion --list-only` 确认有版本可还原 |
24
+
25
+ ---
26
+
27
+ ## 二、接口与布局影响矩阵
28
+
29
+ | 操作 | 推荐接口 | 是否触及 layout/fields | 备注 |
30
+ |------|----------|------------------------|------|
31
+ | 使用/禁用权限 | `permission/table/savePermissions` | **否** | 全量替换权限 JSON,不动 fields |
32
+ | 矩阵权限 | `permission/matrix/save` | **否** | |
33
+ | 查询条件 | `saveQueryItem` / `saveQueryConfig` | **否** | |
34
+ | 数据集筛选 | `queryInfoSave` | **否** | |
35
+ | 存为新版本 | `saveAsVersion` | **否**(复制快照) | |
36
+ | 还原版本 | `GET rollbackReport` | **是**(整表替换) | 用户显式还原;会重置快照配置 |
37
+ | 页面布局/分页 | `allPageSetting` | **否**(pageSetting 独立) | |
38
+ | 定时快照 | `saveReportDevInfo` + `workbook.snapshot` | **是(若 workbook 空则灾难)** | 必须 §三 |
39
+ | 修改单元格/父格 | `saveReportDevInfo` +(可选)`saveReportWithCacheConfig` | **是** | **新空报表必须先 dev 后 cache**;见 `api-report-build-fast-path.md` |
40
+
41
+ ---
42
+
43
+ ## 三、saveReportDevInfo 安全合并流程(强制)
44
+
45
+ 凡必须调用 `saveReportDevInfo` 且**不是**「从零新建报表」或「用户要求清空」:
46
+
47
+ ```
48
+ 1. GET getReportDevInfo?reportId=
49
+ 2. fields = data.fields || []
50
+ 3. IF fields.length === 0 → ABORT(提示先 rollbackReport 或从版本还原)
51
+ 4. wb = mergeWorkbookSafe(data, patchFn)
52
+ ├─ 若 countCells(workbookProps) > 0 → 深拷贝 workbookProps
53
+ └─ 否则 → buildWorkbookFromFields(fields, sheetProps) // 禁止提交空 sheets
54
+ 5. patchFn(wb) // 仅改 snapshot / 必要字段,勿删 sheets/dataTable
55
+ 6. POST saveReportDevInfo(带当前 designKey + ebBusinessId)
56
+ 7. GET getReportDevInfo 回读:
57
+ - fields.length 不得小于保存前
58
+ - 若用户未要求删除,fields.length === 0 → 视为事故,立即 rollbackReport
59
+ ```
60
+
61
+ ### 3.1 buildWorkbookFromFields 最低要求
62
+
63
+ 从 `fields[]` 重建时,每个 field 须写入 `data.dataTable[x][y].style`(含 `cellType` 关键属性:父格、fieldKey、leftFilterField 等)。
64
+ **禁止**只提交 `{ version, sheets: {} }` 或 `{ sheets: { Sheet1: { data: { dataTable: {} } } } }`。
65
+
66
+ **Const(强制)**:`style.cellType` 须为完整 `ConstCellType`,至少包含:
67
+
68
+ | 属性 | 说明 |
69
+ |------|------|
70
+ | `cellText` | 有展示文案时必填 |
71
+ | `leftX/leftY/topX/topY` | **禁止省略**(省略 → Java int 默认 0 → 伪自定义 A1) |
72
+ | `pcHreflink` / `openType` | 超链接 Const 必填;服务端**只认 workbook cellType**,不只认 `fields[]` 顶层 |
73
+
74
+ 普通 Const 父格写 `-1`;超链接含 `{L4}` 时左父格指向依赖数据格。见 `report-self-check-guide.md` §2.2。
75
+
76
+ **定点修改**:用户只点一格时,语义上只改该格,但仍须整表 fields 原样写回 + 完整 workbook(见 `targeted-fix-guide.md`)。
77
+
78
+ ### 3.2 快照配置 patch 示例
79
+
80
+ ```javascript
81
+ const wb = mergeWorkbookSafe(dev.data, (wb) => {
82
+ wb.snapshot = {
83
+ enableSnapshot: true,
84
+ cron: '0 0 0 * * ?',
85
+ // 未指定名称时的平台默认
86
+ snapshotName: `${reportName}-{date}-{time}`,
87
+ params: JSON.stringify({ searchCondition: {} }),
88
+ // 雪花 ID 必须字符串,禁止 Number(reportId)(超 MAX_SAFE_INTEGER 会截断,设计器空白)
89
+ sheetId: String(reportId),
90
+ // 更新已有配置时可带 id/jobId(同样用 String);rollback 后建议 omit 让服务端重建调度
91
+ };
92
+ // 禁止 showLocation=0
93
+ for (const sh of Object.values(wb.sheets || {})) {
94
+ if (!sh.tag) sh.tag = {};
95
+ if (!sh.tag.showLocation || sh.tag.showLocation === 0) sh.tag.showLocation = 2;
96
+ }
97
+ });
98
+ ```
99
+
100
+ ### 3.3 保存后验证清单
101
+
102
+ | 检查项 | 通过标准 |
103
+ |--------|----------|
104
+ | `fields.length` | ≥ 保存前数量(允许只增不减,除非用户要求删格) |
105
+ | 关键单元格抽样 | 如 `(1,0)` 供应商等级 `(1,5)` 进货单编号 仍存在 |
106
+ | `snapshot.enableSnapshot` | 若本次任务为开快照,回读为 true |
107
+ | 设计器肉眼 | 提示用户 Ctrl+F5 强刷 |
108
+
109
+ ---
110
+
111
+ ## 四、事故恢复
112
+
113
+ | 现象 | 处理 |
114
+ |------|------|
115
+ | 设计器空白、`fields.length===0` | `GET rollbackReport?sheetId={版本snapshotId}&originSheetId={reportId}` |
116
+ | 无版本 | 查 `getVersionWithPage`;或本地 `inspect_*.json` / 脚本备份 |
117
+ | 快照误伤后 layout 已还原 | 快照需在设计器 **页面→快照设置** 重配,或 `e10-cli report snapshot`(已含保护) |
118
+
119
+ **版本 ID 来源**:`e10-cli report version --list-only` → `snapshotId` 列。
120
+
121
+ ---
122
+
123
+ ## 五、脚本与模块
124
+
125
+ | 文件 | 用途 |
126
+ |------|------|
127
+ | `` | **安全合并 workbook**、保存前断言、保存后验证 |
128
+ | `e10-cli report snapshot` | 定时快照(默认名 `{报表名}-{date}-{time}`;ID 字符串;依赖 safe_report_save) |
129
+ | `e10-cli report validate blank` | 防设计区空白门禁(sheetId/showLocation/scrollPage) |
130
+ | `e10-cli report restore` | 从版本 snapshotId 还原 layout |
131
+ | `references/designer-blank-prevention-guide.md` | **设计区空白禁令与定位** |
132
+ | | `merge_workbook_safe` / `assert_layout_not_empty` / `verify_layout_preserved` |
133
+
134
+ ---
135
+
136
+ ## 六、Agent 硬约束(写入 SKILL 摘要)
137
+
138
+ 1. **默认禁止**对已有报表提交空 workbook 或 0 fields 的 `saveReportDevInfo`。
139
+ 2. 设置权限 / 查询 / 数据集筛选 / 菜单 / 复制 → **不得**调用 `saveReportDevInfo`。
140
+ 3. 设置快照 → **必须** `mergeWorkbookSafe` + 保存前后 `fields.length` 校验。
141
+ 4. 用户未说「删除」「清空」「重置布局」→ **任何步骤不得使 fields 归零**。
142
+ 5. 保存前:`saveAsVersion` 或确认 `getVersionWithPage` 有可用版本。
143
+ 6. 保存后:`getReportDevInfo` 回读失败 → **停止后续操作**,优先 `rollbackReport`。
144
+
145
+ ---
146
+
147
+ ## 七、源码依据
148
+
149
+ - `EdcReportDevDesignServiceImpl.saveReportDevInfoLast`:解压 workbook → `getFieldsFromWorkbook(wb)` → **全量** `saveFields` 覆盖 DB。
150
+ - `Workbook.snapshot`:`saveSnapshot(wb, reportId)` 仅在 workbook 含 snapshot 对象时执行;与 fields 同一保存事务。
151
+ - `workbookProps` 与 `fields` 分离存储;API 返回的 `workbookProps.sheets` 常不含 dataTable,**不能**作为唯一布局来源。
@@ -0,0 +1,97 @@
1
+ # 多源分片与冻结表 · 场景 F(SKILL A0.16)
2
+
3
+ > 典型案例:产品信息统计大宽表 —— 左侧「产品类别+产品名称」行维度,右侧规则分三片
4
+ > (按货主地区 / 按供应商 / 按雇员统计),每片若干数值列+小计,底部总计行;
5
+ > 长宽都大 → **不分页** + **冻结左侧行维度列与上方表头**。
6
+
7
+ ---
8
+
9
+ ## 一、案例解析(特性 → 机制 → CLI 落点)
10
+
11
+ | # | 特性 | 机制 | CLI 落点 |
12
+ |---|---|---|---|
13
+ | ① | 多源分片(三片各挂一个统计维度) | 一张报表挂**多个数据集**(`addDataset` ×N,各返回独立 datasetId);每片 = 一个交叉块,片内数值格 `dataSetId`/`fieldKey` 指向**各自**数据集;片与片之间用普通 Const 列隔开 | 蓝图通道:`e10-cli report rebuild from-blueprint`,spec 用 `datasets[]`(各配 `key`),格上写 `datasetKey` 选片(§6.5 crosstab guide);**datasetId 隔离铁律照常生效**(check-ids / assertNoStaleDatasetIds) |
14
+ | ② | 交叉统计(维度×指标) | 列维度 `Horizontal+Group`,片内数值格 `Count/sum`,**左父格→行维度、上父格→列维度**;小计/总计行公式左父格必须自定义(crosstab guide §2/§4.2) | 同交叉表直构路径(`e10-cli report build` / 蓝图),**禁手写临时 workbook** |
15
+ | ③ | 不分页 | `pageSetting.pagination=false`(第二次保存 `saveReportWithCacheConfig`,同 card_ops 实证通道) | **`build spec` 已内置**:`layout.paginate: false`(A0.16 起;此前只有 pageRowCount 开分页方向) |
16
+ | ④ | 冻结左侧+上方表头 | 唯一权威 = 某格 `tag.fixed=true`(保存侧→`field.fixed=1`);预览引擎取该格 `minCell`:**上方行+左侧列**全部冻结(不含锚点自身) | **`build spec` 已内置**:`layout.freezeRows`(冻到表头)+ `layout.freezeCols`(冻左 N 列),可组合 |
17
+ | ⑤ | 宽表居左 | `sheet.tag.showLocation=1` | `layout.showLocation:'left'`(禁 0,血案见 freeze guide §1.5) |
18
+
19
+ ## 二、冻结锚点语义(★ A0.16 修正版,替换旧 build spec 行为)
20
+
21
+ 预览引擎(`AnalysisParams.analysisFrozenCell`)只认 **fixed=1 那一格的 minCell**:
22
+
23
+ ```
24
+ frozenRowCount = 锚点格.x → 冻结其上方所有行(不含锚点行)
25
+ frozenColumnCount = 锚点格.y → 冻结其左侧所有列(不含锚点列)
26
+ ```
27
+
28
+ 所以锚点**落点决定冻结范围**,`build spec` 自动选点:
29
+
30
+ | 需求 | layout 写法 | 锚点落点(自动) | 效果 |
31
+ |---|---|---|---|
32
+ | 冻标题+表头 | `freezeRows:1` | 首个数据行 `(dataX, 0)` | 上方全部冻住(锚点在数据格是唯一可行做法,freeze guide §2.6 推论1) |
33
+ | 冻标题+表头+左侧 N 列 | `freezeRows:1, freezeCols:N` | `(dataX, N)` | 上方全部 + 左 N 列 |
34
+ | 仅冻左侧 N 列(不冻行) | `freezeCols:N`(不写 freezeRows) | `(0, N)` | 只冻列 |
35
+ | 不冻结 | 不写 | — | — |
36
+
37
+ ⚠️ **坑 F-1(本次修正)**:旧版 build spec 把 fixed 打在**表头格**上 → 引擎只冻「表头上方」:
38
+ 带标题时只冻标题行、无标题时一行都冻不住。已改为锚点落首个数据行。
39
+ `e10-cli report fix freeze --anchor` 手动通道不受影响(用户显式给坐标)。
40
+
41
+ ⚠️ **坑 F-2**:`fixed=1` 全表**必须唯一**(引擎 `findFirst()`);重建/改结构后回读核对。
42
+
43
+ ⚠️ **坑 F-3**:**禁**给锚点格写 `cellText="fixed-cell-tag"` → 服务端清空该格边框+底色。
44
+
45
+ ## 三、最小 spec(分片单源版;真多源走蓝图通道)
46
+
47
+ ```json
48
+ {
49
+ "appId": "<appId>", "sourceId": "<sid>", "sourceName": "<表名>",
50
+ "reportName": "产品信息统计-分片冻结",
51
+ "columns": [
52
+ { "title": "产品类别", "field": "产品类别" },
53
+ { "title": "产品名称", "field": "产品名称" },
54
+ { "title": "数量", "field": "数量", "format": "value" },
55
+ { "title": "订购金额", "field": "订购金额", "format": "huobi" }
56
+ ],
57
+ "layout": {
58
+ "title": "多源分片与冻结", "showLocation": "left",
59
+ "freezeRows": 1, "freezeCols": 2, "paginate": false
60
+ }
61
+ }
62
+ ```
63
+
64
+ 多源分片(三片各一数据集)spec 骨架(`rebuild from-blueprint`):
65
+
66
+ ```json
67
+ {
68
+ "datasets": [
69
+ { "key": "region", "sourceId": "<sid1>", "sourceName": "<表1>" },
70
+ { "key": "supplier", "sourceId": "<sid2>", "sourceName": "<表2>" },
71
+ { "key": "employee", "sourceId": "<sid3>", "sourceName": "<表3>" }
72
+ ],
73
+ "cells": [
74
+ { "r": 2, "c": 0, "datasetKey": "region", "field": "产品名称", "expand": "Vertical" },
75
+ { "r": 2, "c": 3, "datasetKey": "region", "field": "数量", "left": [2,0], "top": [0,3] },
76
+ { "r": 2, "c": 9, "datasetKey": "supplier", "field": "数量", "left": [2,0], "top": [0,9] }
77
+ ]
78
+ }
79
+ ```
80
+
81
+ ## 四、搭建提示语句(用户照此说即可)
82
+
83
+ > 按多源分片与冻结格式搭一张报表:左侧行维度「类别+名称」两列,
84
+ > 右侧三片分别按「地区 / 供应商 / 雇员」统计数量与金额,每片带小计,底部总计行;
85
+ > 数据源 `<sourceId/sourceName 列表>`(appId `<appId>`);
86
+ > 不分页、冻结左侧 2 列和上方表头、报表居左;
87
+ > 用 `build spec`(单源)或 `rebuild from-blueprint`(多源分片)落库,
88
+ > 落库后回读验收:fixed=1 唯一且坐标正确、pagination=false、showLocation=1。
89
+
90
+ ## 五、验证清单
91
+
92
+ 1. 回读 `getReportDevInfo`:`fixed=1` **唯一**且 minCell=锚点坐标;无 `fixed-cell-tag` 文本格
93
+ 2. `allPageSetting`(或设计器「打印与分页」):`pagination=false`
94
+ 3. `sheetProps.showLocation === 1`
95
+ 4. 多源时跑 `e10-cli report check-ids`(exit 3 = 有外来 datasetId,禁止落库)
96
+ 5. 交叉块回读:列维度 `Horizontal`、数值格双父格(left→行维度 / top→列维度)、小计公式左父格自定义
97
+ 6. 预览冻结表现以设计器确认为准(渲染接口取不到展开数据,crosstab guide §5.4)
@@ -1,84 +1,84 @@
1
- # 雪花 ID 精度安全指南
2
-
3
- > **适用场景**:复制报表、权限绑定、添加到菜单、删除/回收站等一切涉及 `reportId` / `pageId` 的 API 链路。
4
- > **事故案例**:`copySheet` 返回 `1311221384923619328`,Node `JSON.parse` 后变为 `1311221384923619300`(末位差 28),导致权限与菜单绑到不存在的 ID。
5
-
6
- ---
7
-
8
- ## 一、根因
9
-
10
- | 项 | 值 |
11
- |----|-----|
12
- | E10 ID 类型 | 64 位雪花 ID(19 位十进制) |
13
- | JS 安全整数上限 | `Number.MAX_SAFE_INTEGER = 9007199254740991`(16 位) |
14
- | 后果 | `JSON.parse('{"id":1311221384923619328}')` → `id === 1311221384923619300` |
15
-
16
- **Python 注意**:`json.loads` 大整数默认可保留,但 `int(report_id)` 传给部分接口仍会溢出;**全程用 `str` 承载 ID**。
17
-
18
- ---
19
-
20
- ## 二、强制规则(Agent)
21
-
22
- 1. **禁止**对含 `id` / `reportId` / `pageId` 的 API 响应裸用 `JSON.parse` / `json.loads` 后直接当数字用。
23
- 2. **必须**使用 Skill 工具:
24
- - Node:`内置雪花安全解析` → `parseJsonSnowflakeSafe` / `extractIdFromRaw`
25
- - Python:`内置雪花安全解析` → 同名函数
26
- 3. **copySheet 后取新 ID 双保险**:
27
- ```javascript
28
- const newId = extractIdFromRaw(copyRes.raw) || stringifyId(copyRes.json.data?.id);
29
- ```
30
- ```python
31
- new_id = extract_id_from_raw(cli.last_raw) or str((result.get("data") or {}).get("id", ""))
32
- ```
33
- 4. **后续 API 一律字符串**:`pageId`、`sourceId`、`reportId`、`sheetId` 参数禁止 `Number(id)`。
34
- 5. **交付给用户/手册的 reportId** 必须来自 raw 提取或 snowflake 安全解析,禁止直接打印 `json.data.id`(未安全解析时)。
35
-
36
- ---
37
-
38
- ## 三、安全解析原理
39
-
40
- 仅对**已知 ID 字段名**的 ≥16 位数字加引号后再 `JSON.parse`,**不**对嵌套 JSON 字符串内数字做全局替换(避免破坏 `tabs` 等字段)。
41
-
42
- ```javascript
43
- // 内置雪花安全解析
44
- parseJsonSnowflakeSafe(rawText)
45
- extractIdFromRaw(rawText, ['id', 'reportId', 'pageId'])
46
- ```
47
-
48
- ```python
49
- # 内置雪花安全解析
50
- parse_json_snowflake_safe(raw_text)
51
- extract_id_from_raw(raw_text, keys=("id", "reportId", "pageId"))
52
- ```
53
-
54
- ---
55
-
56
- ## 四、受影响 API(必用安全 ID)
57
-
58
- | 操作 | API | ID 字段 |
59
- |------|-----|---------|
60
- | 复制 | `GET copySheet?sheetId=` | 响应 `data.id` |
61
- | 权限 | `POST savePermissions` | body key、`sourceId` |
62
- | 添加到菜单 | `POST savePublishMenu` | `page.pageId` |
63
- | 删除 | `GET deleteReport?id=` | query `id` |
64
- | 设计器 | `GET baseInfo?id=` | query `id` |
65
- | 列表 | `GET report/list` | 每项 `id` |
66
-
67
- ---
68
-
69
- ## 五、验证清单
70
-
71
- - [ ] copySheet 后 `newReportId` 与设计器 URL 中 ID 一致(末四位无漂移)
72
- - [ ] `baseInfo?id={newId}` 能返回报表名称
73
- - [ ] 菜单 `allMenus` 中 `pageId` 与报表 ID 字符串完全一致
74
- - [ ] 权限 `getQueryItem` / `permission/table/list/new` 的 `sourceId` 正确
75
-
76
- ---
77
-
78
- ## 六、相关文档
79
-
80
- | 文件 | 用途 |
81
- |------|------|
82
- | `report-lifecycle-api-guide.md` | 复制 / 权限 / 添加到菜单 API |
83
- | `已由 CLI 取代` | Python 客户端(已接入 snowflake 解析) |
84
- | `内置雪花安全解析` / `.py` | 跨语言安全解析 |
1
+ # 雪花 ID 精度安全指南
2
+
3
+ > **适用场景**:复制报表、权限绑定、添加到菜单、删除/回收站等一切涉及 `reportId` / `pageId` 的 API 链路。
4
+ > **事故案例**:`copySheet` 返回 `1311221384923619328`,Node `JSON.parse` 后变为 `1311221384923619300`(末位差 28),导致权限与菜单绑到不存在的 ID。
5
+
6
+ ---
7
+
8
+ ## 一、根因
9
+
10
+ | 项 | 值 |
11
+ |----|-----|
12
+ | E10 ID 类型 | 64 位雪花 ID(19 位十进制) |
13
+ | JS 安全整数上限 | `Number.MAX_SAFE_INTEGER = 9007199254740991`(16 位) |
14
+ | 后果 | `JSON.parse('{"id":1311221384923619328}')` → `id === 1311221384923619300` |
15
+
16
+ **Python 注意**:`json.loads` 大整数默认可保留,但 `int(report_id)` 传给部分接口仍会溢出;**全程用 `str` 承载 ID**。
17
+
18
+ ---
19
+
20
+ ## 二、强制规则(Agent)
21
+
22
+ 1. **禁止**对含 `id` / `reportId` / `pageId` 的 API 响应裸用 `JSON.parse` / `json.loads` 后直接当数字用。
23
+ 2. **必须**使用 Skill 工具:
24
+ - Node:`` → `parseJsonSnowflakeSafe` / `extractIdFromRaw`
25
+ - Python:`` → 同名函数
26
+ 3. **copySheet 后取新 ID 双保险**:
27
+ ```javascript
28
+ const newId = extractIdFromRaw(copyRes.raw) || stringifyId(copyRes.json.data?.id);
29
+ ```
30
+ ```python
31
+ new_id = extract_id_from_raw(cli.last_raw) or str((result.get("data") or {}).get("id", ""))
32
+ ```
33
+ 4. **后续 API 一律字符串**:`pageId`、`sourceId`、`reportId`、`sheetId` 参数禁止 `Number(id)`。
34
+ 5. **交付给用户/手册的 reportId** 必须来自 raw 提取或 snowflake 安全解析,禁止直接打印 `json.data.id`(未安全解析时)。
35
+
36
+ ---
37
+
38
+ ## 三、安全解析原理
39
+
40
+ 仅对**已知 ID 字段名**的 ≥16 位数字加引号后再 `JSON.parse`,**不**对嵌套 JSON 字符串内数字做全局替换(避免破坏 `tabs` 等字段)。
41
+
42
+ ```javascript
43
+ //
44
+ parseJsonSnowflakeSafe(rawText)
45
+ extractIdFromRaw(rawText, ['id', 'reportId', 'pageId'])
46
+ ```
47
+
48
+ ```python
49
+ #
50
+ parse_json_snowflake_safe(raw_text)
51
+ extract_id_from_raw(raw_text, keys=("id", "reportId", "pageId"))
52
+ ```
53
+
54
+ ---
55
+
56
+ ## 四、受影响 API(必用安全 ID)
57
+
58
+ | 操作 | API | ID 字段 |
59
+ |------|-----|---------|
60
+ | 复制 | `GET copySheet?sheetId=` | 响应 `data.id` |
61
+ | 权限 | `POST savePermissions` | body key、`sourceId` |
62
+ | 添加到菜单 | `POST savePublishMenu` | `page.pageId` |
63
+ | 删除 | `GET deleteReport?id=` | query `id` |
64
+ | 设计器 | `GET baseInfo?id=` | query `id` |
65
+ | 列表 | `GET report/list` | 每项 `id` |
66
+
67
+ ---
68
+
69
+ ## 五、验证清单
70
+
71
+ - [ ] copySheet 后 `newReportId` 与设计器 URL 中 ID 一致(末四位无漂移)
72
+ - [ ] `baseInfo?id={newId}` 能返回报表名称
73
+ - [ ] 菜单 `allMenus` 中 `pageId` 与报表 ID 字符串完全一致
74
+ - [ ] 权限 `getQueryItem` / `permission/table/list/new` 的 `sourceId` 正确
75
+
76
+ ---
77
+
78
+ ## 六、相关文档
79
+
80
+ | 文件 | 用途 |
81
+ |------|------|
82
+ | `report-lifecycle-api-guide.md` | 复制 / 权限 / 添加到菜单 API |
83
+ | | Python 客户端(已接入 snowflake 解析) |
84
+ | `` / `.py` | 跨语言安全解析 |