@shundoo-ai/dsh-cosmic 1.0.21 → 1.0.22

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 (218) hide show
  1. package/package.json +1 -1
  2. package/skills/cosmic-build/LOCAL-PATCHES.md +43 -14
  3. package/skills/cosmic-build/SKILL.md +24 -9
  4. package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +26 -17
  5. package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +96 -2
  6. package/skills/cosmic-build/knowledge/cosmic/arch/security/security_standard.md +4 -4
  7. package/skills/cosmic-build/knowledge/cosmic/platform/allimport.md +2 -1
  8. package/skills/cosmic-build/knowledge/cosmic/platform/core/dynamic-object-collection.md +6 -4
  9. package/skills/cosmic-build/knowledge/cosmic/platform/core/model.md +9 -9
  10. package/skills/cosmic-build/knowledge/cosmic/platform/core/view.md +5 -5
  11. package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +8 -4
  12. package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +5 -3
  13. package/skills/cosmic-build/knowledge/cosmic/platform/print-plugin-guide.md +1 -0
  14. package/skills/cosmic-build/knowledge/cosmic/platform/report-plugin.md +269 -57
  15. package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +130 -23
  16. package/skills/cosmic-build/knowledge/cosmic/platform/reporttreedata-plugin.md +48 -28
  17. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/_index.md +12 -3
  18. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/attachment-name-process.md +17 -13
  19. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/dynamic-update-field.md +4 -4
  20. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/filter-grid/filter-grid-set-value.md +1 -1
  21. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-basedatafield.md +2 -3
  22. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-container-init.md +0 -1
  23. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-popup.md +0 -1
  24. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-value-parse.md +5 -6
  25. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/list-show-form.md +1 -1
  26. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/query-field-dynamic.md +0 -7
  27. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/business-data/delete-service.md +24 -29
  28. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-batch.md +4 -0
  29. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-direct-access.md +4 -0
  30. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/entity-object-access.md +8 -3
  31. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/qfilter-compose.md +6 -5
  32. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/after-bind-data-set-visible.md +5 -1
  33. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/click-return-data.md +2 -2
  34. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/closed-call-back-set-value.md +2 -2
  35. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/register-listener.md +1 -1
  36. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/set-filter-tab-org.md +3 -0
  37. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dispatch-service/microservice-dev.md +18 -11
  38. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dist-lock/dist-lock-basic.md +3 -6
  39. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/mq/mq-subscribe.md +5 -1
  40. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/async-operation.md +10 -6
  41. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/big-task-async.md +4 -2
  42. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/schedule-task.md +9 -7
  43. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-interactive.md +33 -34
  44. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-plugin-events.md +30 -22
  45. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-async-query.md +133 -0
  46. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-batch-query.md +26 -22
  47. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-chart.md +112 -0
  48. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-dataset-assembly.md +200 -0
  49. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-head-filter.md +177 -0
  50. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-hyperlink-drill.md +216 -0
  51. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-list-data-query.md +12 -12
  52. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-merge-column.md +44 -6
  53. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-print-data.md +163 -0
  54. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-row-data-toolbar.md +188 -0
  55. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-subtotal-total.md +139 -0
  56. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-data-query.md +108 -12
  57. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-list.md +181 -0
  58. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/system-service/report-plugin-interface.md +3 -0
  59. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-basedata-cascade-confirm.md +14 -11
  60. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-before-item-validate.md +1 -1
  61. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-build-filter.md +1 -1
  62. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-multi-level-filter.md +11 -11
  63. package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-refresh-node.md +22 -15
  64. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +2 -2
  65. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-entity-model.md +3 -4
  66. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-exception.md +9 -5
  67. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-orm-access.md +4 -0
  68. package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-request-context.md +7 -3
  69. package/skills/cosmic-build/knowledge/cosmic/platform/standards.md +28 -3
  70. package/skills/cosmic-build/references/compile-guide.md +36 -1
  71. package/skills/cosmic-build/references/dcs-code-rules.md +180 -0
  72. package/skills/cosmic-build/references/delivery.md +16 -2
  73. package/skills/cosmic-build/references/execution-contract.md +15 -3
  74. package/skills/cosmic-build/references/java-plugin-delivery.md +21 -11
  75. package/skills/cosmic-build/references/plugin-handoff-input.md +82 -24
  76. package/skills/cosmic-build/references/plugin-runtime-routing.md +23 -6
  77. package/skills/cosmic-build/references/report-filter-contract.md +53 -15
  78. package/skills/cosmic-build/references/sdk-inspection.md +4 -1
  79. package/skills/cosmic-build/references/type-registry.json +26 -17
  80. package/skills/cosmic-build/schemas/data-model-authoring.schema.json +1 -1
  81. package/skills/cosmic-build/scripts/_http_client.py +3 -0
  82. package/skills/cosmic-build/scripts/compile_plugin_source.py +251 -42
  83. package/skills/cosmic-build/scripts/configure_list.py +112 -0
  84. package/skills/cosmic-build/scripts/dcs-rule-catalog.json +799 -0
  85. package/skills/cosmic-build/scripts/dcs_code_gate.py +1976 -0
  86. package/skills/cosmic-build/scripts/execution_binding.py +25 -0
  87. package/skills/cosmic-build/scripts/platform_api_contract.py +352 -7
  88. package/skills/cosmic-build/scripts/plugin_source_gate.py +9 -1
  89. package/skills/cosmic-build/scripts/plugin_target_sdk.py +12 -8
  90. package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +488 -88
  91. package/skills/cosmic-build/scripts/query_plugin_handoff.py +207 -20
  92. package/skills/cosmic-build/scripts/register_plugins.py +27 -10
  93. package/skills/cosmic-build/scripts/render_output_cards.py +57 -20
  94. package/skills/cosmic-build/scripts/resolve_output_path.py +60 -26
  95. package/skills/cosmic-build/scripts/run_java_tests.py +162 -0
  96. package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +28 -0
  97. package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +1 -22
  98. package/skills/cosmic-build/scripts/test/fixtures/list_column_property_declarations.json +887 -0
  99. package/skills/cosmic-build/scripts/test/fixtures/operation-entry-field-prepare.java +78 -0
  100. package/skills/cosmic-build/scripts/test/test_add_field_required_contract.py +12 -0
  101. package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +23 -2
  102. package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +287 -4
  103. package/skills/cosmic-build/scripts/test/test_configure_list_delivery.py +132 -0
  104. package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +58 -1
  105. package/skills/cosmic-build/scripts/test/test_data_model_contract.py +19 -0
  106. package/skills/cosmic-build/scripts/test/test_dcs_code_gate.py +901 -0
  107. package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +1 -1
  108. package/skills/cosmic-build/scripts/test/test_direct_cli_architecture.py +2 -0
  109. package/skills/cosmic-build/scripts/test/test_http_client.py +6 -0
  110. package/skills/cosmic-build/scripts/test/test_java_toolchain_feedback.py +206 -0
  111. package/skills/cosmic-build/scripts/test/test_kd_api.py +21 -0
  112. package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +184 -0
  113. package/skills/cosmic-build/scripts/test/test_mcp_cli.py +139 -2
  114. package/skills/cosmic-build/scripts/test/test_menu_creation_closure.py +16 -0
  115. package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +55 -1
  116. package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +1 -1
  117. package/skills/cosmic-build/scripts/test/test_operation_field_preparation.py +84 -0
  118. package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +322 -1
  119. package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +951 -4
  120. package/skills/cosmic-build/scripts/test/test_plugin_source_gate.py +30 -2
  121. package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +21 -0
  122. package/skills/cosmic-build/scripts/test/test_property_reroute.py +165 -0
  123. package/skills/cosmic-build/scripts/test/test_property_schema_first.py +35 -0
  124. package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +221 -0
  125. package/skills/cosmic-build/scripts/test/test_register_plugins.py +76 -4
  126. package/skills/cosmic-build/scripts/test/test_render_output_cards.py +75 -3
  127. package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -5
  128. package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +103 -11
  129. package/skills/cosmic-build/scripts/test/test_report_plugin_source_gate.py +46 -16
  130. package/skills/cosmic-build/scripts/test/test_report_properties.py +74 -15
  131. package/skills/cosmic-build/scripts/test/test_report_query_data_source.py +3 -1
  132. package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +1 -2
  133. package/skills/cosmic-build/scripts/test/test_test_environment.py +76 -1
  134. package/skills/cosmic-build/scripts/test_environment.py +67 -1
  135. package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +52 -10
  136. package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +16 -3
  137. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +59 -26
  138. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +1 -1
  139. package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +75 -18
  140. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_auto.py +39 -0
  141. package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_client.py +97 -10
  142. package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +10 -12
  143. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +3 -1
  144. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +38 -29
  145. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +400 -233
  146. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +87 -45
  147. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +42 -15
  148. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +135 -5
  149. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +1 -1
  150. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +1 -1
  151. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +17 -11
  152. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_reroute.py +15 -5
  153. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +1429 -238
  154. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/validators.py +56 -28
  155. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +380 -134
  156. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +1 -1
  157. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_export.py +12 -11
  158. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_verifier.py +42 -0
  159. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_add_field_placement_closure.py +8 -8
  160. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_configuration_readback.py +94 -0
  161. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +47 -1
  162. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_metadata_unchanged_fail_closed.py +87 -0
  163. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_dym_evidence.py +23 -23
  164. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py +279 -4
  165. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_field_guard.py +1 -1
  166. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +509 -32
  167. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +178 -1
  168. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_recreate_plan.py +125 -10
  169. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_serve_contract.py +208 -0
  170. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +259 -50
  171. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_inventory_perf.py +29 -0
  172. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +20 -0
  173. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +18 -0
  174. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +35 -0
  175. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_identity_guard.py +25 -0
  176. package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_time_range_rule.py +98 -0
  177. package/skills/cosmic-build/skills/cosmic-meta-knowledge/domain-model/report-model.md +20 -9
  178. package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/report-modeling-guide.md +55 -25
  179. package/skills/cosmic-build/skills/cosmic-meta-knowledge/property-guides/report-column-properties.md +15 -39
  180. package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +19 -17
  181. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +82 -38
  182. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-batch.md +64 -19
  183. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-plugin.md +32 -20
  184. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-tree.md +78 -31
  185. package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/report-artifact-schema.json +2 -1
  186. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-companion-fields.md +3 -0
  187. package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-list-configuration.md +2 -0
  188. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +3 -1
  189. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +27 -4
  190. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +99 -44
  191. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_examples.py +31 -4
  192. package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_validator.py +129 -29
  193. package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +6 -2
  194. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
  195. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +1 -1
  196. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +104 -25
  197. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +9 -0
  198. package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +2 -2
  199. package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +39 -12
  200. package/skills/cosmic-build/skills/mcp-server/SKILL.md +39 -24
  201. package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +20 -6
  202. package/skills/cosmic-dev-best-practices/SKILL.md +1 -1
  203. package/skills/cosmic-dev-best-practices/references/README.md +2 -2
  204. package/skills/cosmic-dev-best-practices/references/basedata-dev-convention.md +2 -2
  205. package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +4 -4
  206. package/skills/cosmic-dev-best-practices/references/design-document-convention.md +2 -2
  207. package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +4 -4
  208. package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +13 -13
  209. package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +1 -1
  210. package/skills/cosmic-dev-best-practices/references/elements/README.md +1 -1
  211. package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +1 -1
  212. package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +109 -13
  213. package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +1 -1
  214. package/skills/erp-analysis/references/delivery-contract.md +2 -1
  215. package/skills/erp-analysis/templates/AGENTS.template.md +3 -1
  216. package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +0 -169
  217. package/skills/cosmic-build/assets/platform-report-filters/platform.patch +0 -98
  218. package/skills/cosmic-build/scripts/apply_report_filter_extension.py +0 -39
@@ -0,0 +1,200 @@
1
+ ---
2
+ version: "1.0.0"
3
+ framework_version: "8.0.1"
4
+ source_doc: "标品报表案例 01、02、08、11"
5
+ last_updated: "2026-10-19"
6
+ status: "approved"
7
+ scene_name: "报表 DataSet 组装"
8
+ related_apis: ["DataSet.select", "DataSet.groupBy", "DataSet.orderBy", "DataSet.union", "DataSet.addField", "DataSet.filter", "DataSet.copy", "DataSet.getRowMeta", "GroupbyDataSet.sum", "GroupbyDataSet.min", "GroupbyDataSet.finish", "RowMeta.getFieldNames", "RowMeta.getField", "Field.setDataType", "Algo.create", "Algo.createDataSet", "FilterInfo.getFilterItems", "FilterInfo.getQFilters", "FilterItemInfo.getPropName", "FilterItemInfo.getCompareType", "FilterItemInfo.getValue", "ReportQueryParam.getCustomParam", "QueryServiceHelper.queryDataSet"]
9
+ topic: "报表数据查询"
10
+ ---
11
+
12
+ # 场景:报表 DataSet 组装
13
+
14
+ ## 适用情况
15
+ 报表的行不是一次查询的原始行:要按期间聚合、要把两个列不同的来源合并、要用打开方传来的值在内存中构造。`reportlist` 插件用一次查询带回所需的全部字段,再用 DataSet 算子加工结果,而不是回头再查数据库。它返回的 DataSet 就是报表:列名就是报表列的 `dateIndex`,区分大小写。
16
+
17
+ ## 事件/方法说明
18
+
19
+ - `QueryServiceHelper.queryDataSet(algoKey, entity, fields, filters, orderBy)`:一次查询;关联字段按路径带回(`currency.id as currency`)
20
+ - `select(exprs)`:投影、改名(`server as servercol`)和表达式(`TO_CHAR(happenddate, 'yyyy.MM') as periodcol`)
21
+ - `groupBy(keys)` + `sum` / `avg` / `min` / `max` + `finish()`:聚合;`finish()` 之前什么都不会执行
22
+ - `orderBy(new String[]{"periodcol desc"})`:排序,用的必须是此时存在的列
23
+ - `union(other)`:合并两个列相同、顺序相同的 DataSet 的行
24
+ - `addField(expression, alias)`:派生列;`filter(expression)`:过滤行
25
+ - `copy()`:得到第二份可消费的副本;每次遍历都会消费 DataSet
26
+ - `Algo.create(algoKey).createDataSet(rows, rowMeta)`:由 `Object[]` 行和带类型 `Field` 的 `RowMeta` 构造 DataSet
27
+
28
+ ## 方法签名
29
+ ```java
30
+ @Override
31
+ public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable
32
+ ```
33
+
34
+ ## 代码示例
35
+ `extracted` `needs-verification`
36
+
37
+ 转发过滤面板条件,查询一次,按期间聚合:
38
+
39
+ ```java
40
+ package kd.bos.plugin.sample.report.bizcase;
41
+
42
+ import java.util.ArrayList;
43
+ import java.util.List;
44
+
45
+ import kd.bos.algo.DataSet;
46
+ import kd.bos.entity.report.AbstractReportListDataPlugin;
47
+ import kd.bos.entity.report.FilterItemInfo;
48
+ import kd.bos.entity.report.ReportQueryParam;
49
+ import kd.bos.orm.query.QCP;
50
+ import kd.bos.orm.query.QFilter;
51
+ import kd.bos.servicehelper.QueryServiceHelper;
52
+
53
+ public class CheckingPayReportListDataPlugin extends AbstractReportListDataPlugin {
54
+
55
+ private static final String ALGO_NAME = "xxx.report.checkingpay";
56
+ private static final String ENTITY_NUMBER = "xxx_orderbill";
57
+
58
+ @Override
59
+ public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable {
60
+ List<QFilter> filters = new ArrayList<>(queryParam.getFilter().getQFilters());
61
+ for (FilterItemInfo item : queryParam.getFilter().getFilterItems()) {
62
+ if (item.getValue() != null && isForwarded(item.getPropName())) {
63
+ filters.add(new QFilter(item.getPropName(), item.getCompareType(), item.getValue()));
64
+ }
65
+ }
66
+ // 固定的业务规则写在这里,写在拥有这次查询的插件里
67
+ filters.add(new QFilter("producttype", QCP.not_equals, "2"));
68
+
69
+ // 一次查询带回全部字段,关联 id 也一起带回
70
+ DataSet bills = QueryServiceHelper.queryDataSet(ALGO_NAME, ENTITY_NUMBER,
71
+ "company.id as company, operationtype, happenddate, totalamount, payamount, currency.id as currency",
72
+ filters.toArray(new QFilter[filters.size()]), null);
73
+ // 投影并计算、聚合,再按聚合后仍存在的列排序
74
+ return bills.select(new String[]{"company as companycol", "operationtype as operationtypecol",
75
+ "TO_CHAR(happenddate, 'yyyy.MM') as periodcol", "totalamount as totalamountcol",
76
+ "payamount as payamountcol", "currency as currencycol"})
77
+ .groupBy(new String[]{"companycol", "operationtypecol", "periodcol"})
78
+ .sum("totalamountcol").sum("payamountcol").min("currencycol")
79
+ .finish()
80
+ .orderBy(new String[]{"periodcol desc"});
81
+ }
82
+
83
+ // 本身就是实体字段标识的面板键,可以原样转发.
84
+ // DCS 禁止静态集合字段(CVE-KD-0056),通过方法返回局部集合。
85
+ private static boolean isForwarded(String key) {
86
+ return "operationtype".equals(key) || "happenddate".equals(key);
87
+ }
88
+ }
89
+ ```
90
+
91
+ 两个列不同的来源:对齐到同一列清单,union,再聚合一次:
92
+
93
+ ```java
94
+ package kd.bos.plugin.sample.report.bizcase;
95
+
96
+ import java.util.ArrayList;
97
+ import java.util.Arrays;
98
+ import java.util.List;
99
+
100
+ import kd.bos.algo.DataSet;
101
+ import kd.bos.algo.GroupbyDataSet;
102
+
103
+ public final class PartnerBalanceMerger {
104
+
105
+ private static final String[] DIMENSIONS = {"org", "partner", "currency"};
106
+ private static final String[] AMOUNTS = {"receivableamt", "payableamt"};
107
+
108
+ private PartnerBalanceMerger() {
109
+ }
110
+
111
+ public static DataSet merge(DataSet arDataSet, DataSet apDataSet) {
112
+ // 两边列名相同、顺序相同;缺少的金额列补 0
113
+ DataSet ar = arDataSet.select(aligned(Arrays.asList(arDataSet.getRowMeta().getFieldNames())));
114
+ DataSet ap = apDataSet.select(aligned(Arrays.asList(apDataSet.getRowMeta().getFieldNames())));
115
+ // 两边都有的往来户现在是两行:再聚合一次
116
+ GroupbyDataSet grouped = ar.union(ap).groupBy(DIMENSIONS);
117
+ for (String amount : AMOUNTS) {
118
+ grouped = grouped.sum(amount);
119
+ }
120
+ // 轧差余额;空值兜底让缺失的一边不参与运算
121
+ grouped = grouped.sum("(case when payableamt is null then 0 else payableamt end)"
122
+ + " - (case when receivableamt is null then 0 else receivableamt end)", "netamt");
123
+ return grouped.finish();
124
+ }
125
+
126
+ private static String[] aligned(List<String> present) {
127
+ List<String> select = new ArrayList<>(Arrays.asList(DIMENSIONS));
128
+ for (String amount : AMOUNTS) {
129
+ select.add(present.contains(amount) ? amount : "0 " + amount);
130
+ }
131
+ return select.toArray(new String[select.size()]);
132
+ }
133
+ }
134
+ ```
135
+
136
+ 用打开方传来的值在内存中构造行;结果为空时仍带有已声明的列:
137
+
138
+ ```java
139
+ package kd.bos.plugin.sample.report.bizcase;
140
+
141
+ import java.math.BigDecimal;
142
+ import java.util.ArrayList;
143
+ import java.util.List;
144
+ import java.util.Map;
145
+
146
+ import kd.bos.algo.Algo;
147
+ import kd.bos.algo.DataSet;
148
+ import kd.bos.algo.DataType;
149
+ import kd.bos.algo.Field;
150
+ import kd.bos.algo.RowMeta;
151
+ import kd.bos.entity.report.AbstractReportListDataPlugin;
152
+ import kd.bos.entity.report.ReportQueryParam;
153
+
154
+ public class DrillDetailReportListDataPlugin extends AbstractReportListDataPlugin {
155
+
156
+ private static final String ALGO_NAME = "xxx.report.drilldetail";
157
+
158
+ @Override
159
+ public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable {
160
+ // 每列一个 Field,按行内顺序排列,类型与列的声明一致
161
+ RowMeta rowMeta = new RowMeta(new Field[]{
162
+ new Field("itemname", DataType.StringType),
163
+ new Field("amount", DataType.BigDecimalType)});
164
+ List<Object[]> rows = new ArrayList<>(16);
165
+ Map<String, Object> custom = queryParam.getCustomParam();
166
+ Object passed = custom == null ? null : custom.get("rows");
167
+ if (passed instanceof List) {
168
+ for (Object value : (List<?>) passed) {
169
+ List<?> row = (List<?>) value;
170
+ rows.add(new Object[]{String.valueOf(row.get(0)), new BigDecimal(String.valueOf(row.get(1)))});
171
+ }
172
+ }
173
+ // 辅助性质的视图显示一张空表,而不是让整个页面报错
174
+ return Algo.create(ALGO_NAME).createDataSet(rows, rowMeta);
175
+ }
176
+ }
177
+ ```
178
+
179
+ 常量文本列在声明类型之前没有类型,在此之前对它排序会失败:
180
+
181
+ ```java
182
+ DataSet rows = QueryServiceHelper.queryDataSet(ALGO_NAME, ENTITY_NUMBER,
183
+ "'' as bizobject, billno, amount", filters.toArray(new QFilter[filters.size()]), null);
184
+ rows.getRowMeta().getField("bizobject").setDataType(kd.bos.algo.DataType.StringType);
185
+ rows = rows.orderBy(new String[]{"bizobject", "billno"});
186
+ ```
187
+
188
+ ## 注意事项
189
+ - 返回的列名就是报表列的 `dateIndex`,区分大小写;`select` 别名(比如统一加 `col` 后缀)让列标识稳定,并与实体字段名区分开
190
+ - 只有面板键本身就是实体字段标识时,才原样转发面板条件;其他键(app-build 的 `flt_<列>` 键、`q_` 前缀、带点的基础资料路径)先映射到源字段——见 `reportlistdata-plugin.md`
191
+ - `groupBy` 及其聚合在 `finish()` 之前什么都不做;`finish()` 之后的 `orderBy` 用聚合后仍存在的列。比率取平均,不要求和
192
+ - 两个共享某个维度的来源 `union` 之后,再聚合一次,让每个维度值只有一行。两边需要列名相同、顺序相同:缺少的列用常量补齐(`select` 里写 `"0 amount"`,或 `addField("0", "amount")`),任何一边加列,另一边都要同步补
193
+ - 在表达式里用 `case when x is null then 0 else x end` 兜底空值:一边补过零的列,在另一边仍可能为空
194
+ - `filter("...")` 和表达式字符串里的列名错误只会在运行期报错;用与 select 列表相同的常量拼接这些字符串
195
+ - 每次遍历——`hasNext()` / `next()`、for-each、画图——都会消费 DataSet。需要再次读取时在第一次遍历之前 `copy()`,并关闭插件自己持有的副本和已遍历的 DataSet;不要关闭它返回的那个 DataSet
196
+ - 手工构造的 `RowMeta` 每列一个 `Field`,按行内顺序排列,类型与声明一致(`DataType.StringType`、`LongType`、`BigDecimalType` 等),每个 `Object[]` 行都按这个顺序
197
+ - `''` 常量列的类型是 `UnknownType`,在声明类型之前对它 `orderBy` 会失败,如最后一段代码所示
198
+ - 很长的 id 列表按每次查询几百个值分批(标品用 400),再把各部分 `union` 起来,不要用一个不受限的 `in`
199
+ - 某一层由其他层计算得出时(期末 = 累计 − 发出),在内存模型上汇集各层,一次性构造行,不要层层 union
200
+ - 插件在两次查询之间不保留状态;算法键是这次查询在性能监控里的名字:用"模块.业务.方法"命名(`er.checkingpay`)
@@ -0,0 +1,177 @@
1
+ ---
2
+ version: "1.0.0"
3
+ framework_version: "8.0.1"
4
+ source_doc: "报表插件示例 示例1;标品报表案例 09"
5
+ last_updated: "2026-10-19"
6
+ status: "approved"
7
+ scene_name: "报表表头过滤与排序"
8
+ related_apis: ["AbstractReportFormPlugin.setSortAndFilter", "AbstractReportFormPlugin.beforeCreateFilterInfo", "SortAndFilterEvent.getColumnName", "SortAndFilterEvent.setSort", "SortAndFilterEvent.setFilter", "CreateFilterInfoEvent.getFieldKey", "CreateFilterInfoEvent.getFilterInfo", "FilterInfo.getTableHeadFilterItems", "FilterInfo.getHeadFilters", "FilterInfo.getQFilters", "FilterItemInfo.getPropName", "FilterItemInfo.getCompareType", "FilterItemInfo.getValue"]
9
+ topic: "报表表头过滤"
10
+ ---
11
+
12
+ # 场景:报表表头过滤与排序
13
+
14
+ ## 适用情况
15
+ 用户在报表列头上过滤或排序(列头的漏斗与排序箭头)。这个功能分两半、落在两个插件里:`reportform` 插件按列开启,并可替换某列的过滤选项;`reportlist` 取数插件应用用户选的条件。只开启不应用,列头会出现漏斗,但一行数据也不会变。
16
+
17
+ ## 事件/方法说明
18
+
19
+ - `setSortAndFilter(List<SortAndFilterEvent>)`(reportform):列表渲染前每列一个事件;`getColumnName()` 是列标识,`setSort(true)` / `setFilter(true)` 开启该列的表头排序 / 表头过滤
20
+ - `beforeCreateFilterInfo(CreateFilterInfoEvent)`(reportform):构建某列的表头过滤之前触发;`getFieldKey()` 是列标识,`getFilterInfo().put("comboItems", items)` 替换它的选项——每个选项是含 `id`、`name`(`LocaleString`)、`value` 的 map
21
+ - `FilterInfo.getTableHeadFilterItems()`(reportlist):用户在列头输入的条件,类型为 `List<FilterItemInfo>`——`getPropName()` 是列标识,`getCompareType()` 是比较符,`getValue()` 是值
22
+ - `FilterInfo.getHeadFilters()`(reportlist):同样的条件,已转换成 `List<QFilter>`,以列标识为字段
23
+ - `ReportList.setFilter2ndMaxRowCount(int)` / `setOrder2ndMaxRowCount(int)`:限制表头过滤 / 排序支持的行数,见 `report-total-type.md`
24
+
25
+ ## 方法签名
26
+ ```java
27
+ // reportform 插件
28
+ @Override
29
+ public void setSortAndFilter(List<SortAndFilterEvent> allColumns)
30
+
31
+ @Override
32
+ public void beforeCreateFilterInfo(CreateFilterInfoEvent event)
33
+
34
+ // reportlist 插件
35
+ @Override
36
+ public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable
37
+ ```
38
+
39
+ ## 代码示例
40
+ `extracted` `needs-verification`
41
+
42
+ 第 1 步——表单插件开启表头排序与过滤,并替换某一列的选项:
43
+
44
+ ```java
45
+ package kd.bos.plugin.sample.report.bizcase;
46
+
47
+ import java.util.ArrayList;
48
+ import java.util.Arrays;
49
+ import java.util.HashMap;
50
+ import java.util.List;
51
+ import java.util.Map;
52
+
53
+ import kd.bos.dataentity.entity.LocaleString;
54
+ import kd.bos.dataentity.resource.ResManager;
55
+ import kd.bos.report.events.CreateFilterInfoEvent;
56
+ import kd.bos.report.events.SortAndFilterEvent;
57
+ import kd.bos.report.plugin.AbstractReportFormPlugin;
58
+
59
+ public class HeadFilterReportFormPlugin extends AbstractReportFormPlugin {
60
+
61
+ // 提供表头过滤的列;所有列都可排序.
62
+ // 使用 static final 数组,避免 DCS 禁止的静态集合(CVE-KD-0056)
63
+ private static final String[] FILTER_COLUMNS = {"org", "billstatus", "amount"};
64
+
65
+ @Override
66
+ public void setSortAndFilter(List<SortAndFilterEvent> allColumns) {
67
+ super.setSortAndFilter(allColumns);
68
+ for (SortAndFilterEvent column : allColumns) {
69
+ column.setSort(true);
70
+ if (Arrays.asList(FILTER_COLUMNS).contains(column.getColumnName())) {
71
+ column.setFilter(true);
72
+ }
73
+ }
74
+ }
75
+
76
+ @Override
77
+ public void beforeCreateFilterInfo(CreateFilterInfoEvent event) {
78
+ super.beforeCreateFilterInfo(event);
79
+ if (!"billstatus".equals(event.getFieldKey())) {
80
+ return;
81
+ }
82
+ // 这些选项替换平台按数据推导出的选项
83
+ List<Map<String, Object>> comboItems = new ArrayList<>(2);
84
+ comboItems.add(comboItem("C", ResManager.loadKDString("已审核", "HeadFilterReportFormPlugin_0", "xxx-report")));
85
+ comboItems.add(comboItem("B", ResManager.loadKDString("已提交", "HeadFilterReportFormPlugin_1", "xxx-report")));
86
+ event.getFilterInfo().put("comboItems", comboItems);
87
+ }
88
+
89
+ private static Map<String, Object> comboItem(String value, String caption) {
90
+ Map<String, Object> item = new HashMap<>(4);
91
+ item.put("id", value);
92
+ item.put("name", new LocaleString(caption));
93
+ // 取数插件用来比较的存储值
94
+ item.put("value", value);
95
+ return item;
96
+ }
97
+ }
98
+ ```
99
+
100
+ 第 2 步——取数插件在同一次查询里应用表头条件与过滤面板条件:
101
+
102
+ ```java
103
+ package kd.bos.plugin.sample.report.bizcase;
104
+
105
+ import java.util.ArrayList;
106
+ import java.util.List;
107
+
108
+ import kd.bos.algo.DataSet;
109
+ import kd.bos.entity.report.AbstractReportListDataPlugin;
110
+ import kd.bos.entity.report.FilterInfo;
111
+ import kd.bos.entity.report.FilterItemInfo;
112
+ import kd.bos.entity.report.ReportQueryParam;
113
+ import kd.bos.orm.query.QFilter;
114
+ import kd.bos.servicehelper.QueryServiceHelper;
115
+
116
+ public class HeadFilterReportListDataPlugin extends AbstractReportListDataPlugin {
117
+
118
+ private static final String ALGO_NAME = "xxx.report.headfilter";
119
+ private static final String ENTITY_NUMBER = "xxx_salesorder";
120
+ private static final String SELECT_FIELDS = "org.id as org, billno, billstatus, amount";
121
+
122
+ @Override
123
+ public DataSet query(ReportQueryParam queryParam, Object selectedObj) throws Throwable {
124
+ FilterInfo filter = queryParam.getFilter();
125
+ List<QFilter> filters = new ArrayList<>(filter.getQFilters());
126
+ // ……过滤面板条件照常在这里追加
127
+
128
+ // 表头条件:把每个列标识映射回它的源字段
129
+ List<FilterItemInfo> headItems = filter.getTableHeadFilterItems();
130
+ if (headItems != null) {
131
+ for (FilterItemInfo item : headItems) {
132
+ String field = sourceField(item.getPropName());
133
+ if (field != null && item.getValue() != null) {
134
+ filters.add(new QFilter(field, item.getCompareType(), item.getValue()));
135
+ }
136
+ }
137
+ }
138
+ return QueryServiceHelper.queryDataSet(ALGO_NAME, ENTITY_NUMBER, SELECT_FIELDS,
139
+ filters.toArray(new QFilter[filters.size()]), queryParam.getSortInfo());
140
+ }
141
+
142
+ // 报表列标识 -> 该列取自的源字段; null for any other key.
143
+ // 使用 switch,避免 DCS 禁止的静态 Map(CVE-KD-0056)
144
+ private static String sourceField(String columnKey) {
145
+ if (columnKey == null) {
146
+ return null;
147
+ }
148
+ switch (columnKey) {
149
+ case "org":
150
+ return "org.id";
151
+ case "billstatus":
152
+ case "amount":
153
+ return columnKey;
154
+ default:
155
+ return null;
156
+ }
157
+ }
158
+ }
159
+ ```
160
+
161
+ 可过滤列的标识都正好是被查实体自己的字段标识时,可以直接追加转换好的条件:
162
+
163
+ ```java
164
+ List<QFilter> headFilters = queryParam.getFilter().getHeadFilters();
165
+ if (headFilters != null) {
166
+ filters.addAll(headFilters);
167
+ }
168
+ ```
169
+
170
+ ## 注意事项
171
+ - 两半缺一不可:`reportform` 插件里的 `setFilter(true)` 只负责显示漏斗;`reportlist` 插件的 `query()` 必须把表头条件加进查询,否则表头过滤不起任何作用。两个插件中后编译的那个会在缺一半时给出警告(`PLUGIN_REPORT_HEAD_FILTER_UNAPPLIED`)
172
+ - `getHeadFilters()` 以报表列标识作为每个条件的字段,所以只有当列标识就是被查实体的字段标识时才能直接用(如 `report-batch-query.md`)。否则从 `getTableHeadFilterItems()` 取条目,把列标识映射到源字段;不要转发任何源字段都不拥有的标识——查询会在运行期失败
173
+ - `query()` 计算出来的列(轧差金额、期间文本)没有源字段:先算出来,再用 `DataSet.filter(expression)` 过滤行
174
+ - 选项 map 含 `id`、`name`(`LocaleString`)、`value`;`value` 是数据里用来比较的存储值,例如单据状态"已审核"的 `"C"`
175
+ - 大报表可用 `setFilter2ndMaxRowCount` / `setOrder2ndMaxRowCount` 限制表头过滤 / 排序支持的行数(`report-total-type.md`)
176
+ - 折叠树形报表列表上,表头过滤默认以树形返回结果。报表示例在 `setTreeReportList` 里用 `event.setCol2ndResultMode(TreeDataMode.FLAT)` 改为平铺;8.0 SDK 里这两个名字都不存在,所以在 8.0 上不要写;目标是更新的版本时,只有 `references/sdk-inspection.md` 在其 SDK 上找到它后才使用
177
+ - 按标识(`getColumnName()` / `getFieldKey()`)匹配列,不要按标题匹配:标题随语言变化
@@ -0,0 +1,216 @@
1
+ ---
2
+ version: "1.0.0"
3
+ framework_version: "8.0.1"
4
+ source_doc: "标品报表案例 03、05、08、09、11"
5
+ last_updated: "2026-10-19"
6
+ status: "approved"
7
+ scene_name: "报表超链接钻取与过滤锁定"
8
+ related_apis: ["HyperLinkClickEvent.getFieldName", "HyperLinkClickEvent.getRowData", "HyperLinkClickEvent.getRowIndex", "BillShowParameter.setPkId", "BillShowParameter.setStatus", "FormShowParameter.setFormId", "FormShowParameter.getOpenStyle", "FormShowParameter.setCustomParams", "FormShowParameter.getCustomParams", "FormShowParameter.setHasRight", "OpenStyle.setShowType", "ReportShowParameter.setQueryParam", "IReportView.getQueryParam", "FilterInfo.clone", "FilterInfo.getFilterItem", "FilterItemInfo.setValue", "ReportQueryParam.setFilter", "FilterContainerInitEvent.getCommonFilterColumns", "CommonFilterColumn.setComboItems", "CommonFilterColumn.setMustInput", "FilterColumn.getFieldName", "FilterColumn.setDefaultValue", "BusinessDataServiceHelper.loadSingle"]
9
+ topic: "报表钻取联查"
10
+ ---
11
+
12
+ # 场景:报表超链接钻取与过滤锁定
13
+
14
+ ## 适用情况
15
+ 报表单元格是一个链接:点单据编号打开单据,点往来户打开该往来户的明细报表,点物料按该物料重新打开报表。发送端报表的 `reportform` 插件监听超链接点击,用打开参数打开目标;接收端报表读取自己被打开时带来的参数,把过滤面板锁定为钻取值,让用户只看到钻取限定的范围。
16
+
17
+ ## 事件/方法说明
18
+
19
+ - 在 `registerListener` 中调用 `ReportList.addHyperClickListener(this)`,插件 `implements HyperLinkClickListener`:把超链接单元格路由到 `hyperLinkClick`。列本身必须在报表元数据里配置为超链接列(代码里构建的列用 `ReportColumn.setHyperlink(true)`)
20
+ - `hyperLinkClick(HyperLinkClickEvent)`:`getFieldName()` 是被点击的列,`getRowData()` 返回被点击的行,`getRowIndex()` 是它的行号
21
+ - 三种目标,三种打开参数:
22
+
23
+ | 目标 | 打开参数 |
24
+ |---|---|
25
+ | 单据 | `BillShowParameter`:`setFormId(单据表单)`、`setPkId(id)`、`setStatus(OperationStatus.VIEW)` |
26
+ | 列表 | `ListShowParameter`:`setFormId("bos_list")`、`setBillFormId(单据表单)`、携带过滤的 `ListFilterParameter`——见 `report-row-data-toolbar.md` |
27
+ | 报表 | `ReportShowParameter`:`setFormId(报表)` 加 `setCustomParams(map)`,或 `setQueryParam(param)` 携带现成的 `FilterInfo` |
28
+
29
+ - 接收端报表的 `filterContainerInit(FilterContainerInitEvent, ReportQueryParam)`:`getView().getFormShowParameter().getCustomParams()` 是发送端传来的参数,`event.getCommonFilterColumns()` 是过滤面板的常用过滤列
30
+ - `CommonFilterColumn`:`getFieldName()`、`setComboItems(items)`、`setDefaultValue(value)`、`setMustInput(true)` 把一个过滤锁定为钻取值
31
+
32
+ ## 方法签名
33
+ ```java
34
+ public class MyReportPlugin extends AbstractReportFormPlugin implements HyperLinkClickListener
35
+
36
+ @Override
37
+ public void registerListener(EventObject e)
38
+
39
+ @Override
40
+ public void hyperLinkClick(HyperLinkClickEvent evt)
41
+
42
+ // 接收端报表
43
+ @Override
44
+ protected void filterContainerInit(FilterContainerInitEvent event, ReportQueryParam queryParam)
45
+ ```
46
+
47
+ ## 代码示例
48
+ `extracted` `needs-verification`
49
+
50
+ 发送端报表:每个超链接列一个方法、各自一个目标:
51
+
52
+ ```java
53
+ package kd.bos.plugin.sample.report.bizcase;
54
+
55
+ import java.util.EventObject;
56
+ import java.util.HashMap;
57
+ import java.util.Map;
58
+
59
+ import kd.bos.bill.BillShowParameter;
60
+ import kd.bos.bill.OperationStatus;
61
+ import kd.bos.dataentity.entity.DynamicObject;
62
+ import kd.bos.entity.report.FilterInfo;
63
+ import kd.bos.entity.report.FilterItemInfo;
64
+ import kd.bos.entity.report.ReportQueryParam;
65
+ import kd.bos.form.ShowType;
66
+ import kd.bos.form.events.HyperLinkClickEvent;
67
+ import kd.bos.form.events.HyperLinkClickListener;
68
+ import kd.bos.report.IReportView;
69
+ import kd.bos.report.ReportList;
70
+ import kd.bos.report.ReportShowParameter;
71
+ import kd.bos.report.plugin.AbstractReportFormPlugin;
72
+
73
+ public class DrillReportFormPlugin extends AbstractReportFormPlugin implements HyperLinkClickListener {
74
+
75
+ private static final String DETAIL_REPORT = "xxx_partner_detail_rpt";
76
+ private static final String THIS_REPORT = "xxx_partner_sum_rpt";
77
+
78
+ @Override
79
+ public void registerListener(EventObject e) {
80
+ super.registerListener(e);
81
+ ReportList reportList = this.getControl("reportlistap");
82
+ reportList.addHyperClickListener(this);
83
+ }
84
+
85
+ @Override
86
+ public void hyperLinkClick(HyperLinkClickEvent evt) {
87
+ String fieldName = evt.getFieldName();
88
+ if ("billno".equals(fieldName)) {
89
+ openBill(evt.getRowData());
90
+ } else if ("partner".equals(fieldName)) {
91
+ openDetailReport(evt.getRowData());
92
+ } else if ("material".equals(fieldName)) {
93
+ reopenForMaterial(evt.getRowData());
94
+ }
95
+ }
96
+
97
+ // 单据:只读,新页签打开
98
+ private void openBill(DynamicObject row) {
99
+ long billId = row.getLong("billid");
100
+ if (billId == 0L) {
101
+ return;
102
+ }
103
+ BillShowParameter parameter = new BillShowParameter();
104
+ parameter.setFormId(row.getString("billformid"));
105
+ parameter.setPkId(billId);
106
+ parameter.setStatus(OperationStatus.VIEW);
107
+ parameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
108
+ this.getView().showForm(parameter);
109
+ }
110
+
111
+ // 另一张报表:用自定义参数告诉它是为什么被打开的
112
+ private void openDetailReport(DynamicObject row) {
113
+ Map<String, Object> params = new HashMap<>(4);
114
+ params.put("isopenbyhyperlink", Boolean.TRUE);
115
+ // 每个值都以接收端报表的过滤字段为键
116
+ params.put("q_partner", row.getDynamicObject("partner").getPkValue());
117
+ params.put("q_org", row.getDynamicObject("org").getPkValue());
118
+ ReportShowParameter parameter = new ReportShowParameter();
119
+ parameter.setFormId(DETAIL_REPORT);
120
+ parameter.setCustomParams(params);
121
+ parameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
122
+ this.getView().showForm(parameter);
123
+ }
124
+
125
+ // 带现成查询打开报表:沿用当前条件,只替换其中一个
126
+ private void reopenForMaterial(DynamicObject row) {
127
+ IReportView view = this.getView();
128
+ FilterInfo filter = view.getQueryParam().getFilter().clone();
129
+ FilterItemInfo material = filter.getFilterItem("material");
130
+ if (material == null) {
131
+ return;
132
+ }
133
+ material.setValue(row.getDynamicObject("material"));
134
+ ReportQueryParam queryParam = new ReportQueryParam();
135
+ queryParam.setFilter(filter);
136
+ ReportShowParameter parameter = new ReportShowParameter();
137
+ parameter.setFormId(THIS_REPORT);
138
+ parameter.setQueryParam(queryParam);
139
+ parameter.getOpenStyle().setShowType(ShowType.MainNewTabPage);
140
+ this.getView().showForm(parameter);
141
+ }
142
+ }
143
+ ```
144
+
145
+ 接收端报表:识别钻取打开,把每个被钻取的过滤锁定为钻取值:
146
+
147
+ ```java
148
+ package kd.bos.plugin.sample.report.bizcase;
149
+
150
+ import java.util.ArrayList;
151
+ import java.util.List;
152
+ import java.util.Map;
153
+
154
+ import kd.bos.dataentity.entity.DynamicObject;
155
+ import kd.bos.dataentity.entity.LocaleString;
156
+ import kd.bos.entity.report.ReportQueryParam;
157
+ import kd.bos.filter.CommonFilterColumn;
158
+ import kd.bos.filter.FilterColumn;
159
+ import kd.bos.form.FormShowParameter;
160
+ import kd.bos.form.control.events.FilterContainerInitEvent;
161
+ import kd.bos.form.field.ComboItem;
162
+ import kd.bos.report.plugin.AbstractReportFormPlugin;
163
+ import kd.bos.servicehelper.BusinessDataServiceHelper;
164
+
165
+ public class DrilledDetailReportFormPlugin extends AbstractReportFormPlugin {
166
+
167
+ @Override
168
+ protected void filterContainerInit(FilterContainerInitEvent event, ReportQueryParam queryParam) {
169
+ super.filterContainerInit(event, queryParam);
170
+ FormShowParameter showParameter = this.getView().getFormShowParameter();
171
+ Map<String, Object> params = showParameter.getCustomParams();
172
+ if (!Boolean.TRUE.equals(params.get("isopenbyhyperlink"))) {
173
+ return;
174
+ }
175
+ for (FilterColumn column : event.getCommonFilterColumns()) {
176
+ if (!(column instanceof CommonFilterColumn)) {
177
+ continue;
178
+ }
179
+ CommonFilterColumn filterColumn = (CommonFilterColumn) column;
180
+ Object pk = params.get(filterColumn.getFieldName());
181
+ if (pk == null) {
182
+ // 没有被钻取:这个过滤保持可选
183
+ continue;
184
+ }
185
+ DynamicObject record = BusinessDataServiceHelper.loadSingle(pk, entityOf(filterColumn.getFieldName()), "name");
186
+ if (record == null) {
187
+ continue;
188
+ }
189
+ // 钻取值成为唯一选项和默认值,并且该过滤必录
190
+ List<ComboItem> items = new ArrayList<>(1);
191
+ items.add(new ComboItem(new LocaleString(record.getString("name")), pk.toString()));
192
+ filterColumn.setComboItems(items);
193
+ filterColumn.setDefaultValue(pk.toString());
194
+ filterColumn.setMustInput(true);
195
+ }
196
+ }
197
+
198
+ // 每个可锁定的过滤字段背后的基础资料实体
199
+ private static String entityOf(String fieldName) {
200
+ return "q_org".equals(fieldName) ? "bos_org" : "bd_customer";
201
+ }
202
+ }
203
+ ```
204
+
205
+ ## 注意事项
206
+ - 不注册监听,超链接点了没反应;元数据里没有超链接列,就没有可点的东西。类里从不调用 `addHyperClickListener` 却覆写了 `hyperLinkClick` 时,编译边界会拒绝它(`PLUGIN_SOURCE_LISTENER_UNREGISTERED`)
207
+ - 一张报表有多个超链接列时,按 `evt.getFieldName()` 分发到每列一个方法,不要写成一个长 `if` 块
208
+ - 点击时从行里读取的每个值——单据 id、单据表单标识、维度——都必须是已声明的报表列,不用于展示时设为隐藏:行数据按已声明的列打包,没有对应列的 DataSet 列读出来什么都没有
209
+ - `setStatus(OperationStatus.VIEW)` 以只读方式打开单据。`setHasRight(true)` 跳过目标的功能权限校验;只有在报表本身已经控制了谁能看到这一行时才使用
210
+ - 中转记录(行上指向真正源单的单据)可能需要在 `hyperLinkClick` 里再查一次:只查一次、只取需要的字段
211
+ - 自定义参数的键是精确匹配的,区分大小写:发送端和接收端对标志和每个维度键的拼写必须完全一致——把它们定义为两个插件共用的常量。结构化的值以 JSON 字符串传递;标品在 `setCustomParam` 之前先把参数对象序列化
212
+ - `setQueryParam(param)` 把一个现成的查询交给接收端。克隆当前的 `FilterInfo`(`getView().getQueryParam().getFilter().clone()`),只改不同的条目,用户的其他条件随之带过去
213
+ - 接收端的 `reportform` 插件读取打开参数;它的 `reportlist` 插件只看得到 `ReportQueryParam`。把钻取值锁定到过滤面板的过滤上(如上),它对取数插件来说就是一个普通条件;其他值在 `verifyQuery` 中复制进 `queryParam.getCustomParam()`
214
+ - 锁定循环里,没有钻取值的过滤列用 `continue` 跳过:在那里 `return` 会让之后所有的列都没被锁定
215
+ - 锁定依赖于该列是常用过滤列(`CommonFilterColumn`,下拉形态);文本和日期过滤的锁定方式不同
216
+ - 标品代码通过给单元格值加 `<nolink>` 前缀来关闭单个单元格的链接;依赖这个约定之前先在目标 SDK 上确认