@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.
- package/package.json +1 -1
- package/skills/cosmic-build/LOCAL-PATCHES.md +43 -14
- package/skills/cosmic-build/SKILL.md +24 -9
- package/skills/cosmic-build/examples/java-rules/CosmicReportAggregation.java +26 -17
- package/skills/cosmic-build/examples/java-rules/CosmicReportFilters.java +96 -2
- package/skills/cosmic-build/knowledge/cosmic/arch/security/security_standard.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/allimport.md +2 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/core/dynamic-object-collection.md +6 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/core/model.md +9 -9
- package/skills/cosmic-build/knowledge/cosmic/platform/core/view.md +5 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/form-plugin.md +8 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/list-plugin.md +5 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/print-plugin-guide.md +1 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/report-plugin.md +269 -57
- package/skills/cosmic-build/knowledge/cosmic/platform/reportlistdata-plugin.md +130 -23
- package/skills/cosmic-build/knowledge/cosmic/platform/reporttreedata-plugin.md +48 -28
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/_index.md +12 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/attachment-name-process.md +17 -13
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/common/dynamic-update-field.md +4 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/filter-grid/filter-grid-set-value.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-basedatafield.md +2 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-container-init.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-popup.md +0 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/filter-value-parse.md +5 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/list-show-form.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/control/list/query-field-dynamic.md +0 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/business-data/delete-service.md +24 -29
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-batch.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/ksql/db-direct-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/entity-object-access.md +8 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/data-access/qfilter/qfilter-compose.md +6 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/after-bind-data-set-visible.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/click-return-data.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/closed-call-back-set-value.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/register-listener.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/event/set-filter-tab-org.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dispatch-service/microservice-dev.md +18 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/dist-lock/dist-lock-basic.md +3 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/mq/mq-subscribe.md +5 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/async-operation.md +10 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/big-task-async.md +4 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/infra/thread-pool/schedule-task.md +9 -7
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-interactive.md +33 -34
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/operation/operation-plugin-events.md +30 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-async-query.md +133 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-batch-query.md +26 -22
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-chart.md +112 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-dataset-assembly.md +200 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-head-filter.md +177 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-hyperlink-drill.md +216 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-list-data-query.md +12 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-merge-column.md +44 -6
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-print-data.md +163 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-row-data-toolbar.md +188 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-subtotal-total.md +139 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-data-query.md +108 -12
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/report/report-tree-list.md +181 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/system-service/report-plugin-interface.md +3 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-basedata-cascade-confirm.md +14 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-before-item-validate.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-build-filter.md +1 -1
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-multi-level-filter.md +11 -11
- package/skills/cosmic-build/knowledge/cosmic/platform/scenes/tree/tree-list-refresh-node.md +22 -15
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-algo.md +2 -2
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-entity-model.md +3 -4
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-exception.md +9 -5
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-orm-access.md +4 -0
- package/skills/cosmic-build/knowledge/cosmic/platform/sdk/sdk-request-context.md +7 -3
- package/skills/cosmic-build/knowledge/cosmic/platform/standards.md +28 -3
- package/skills/cosmic-build/references/compile-guide.md +36 -1
- package/skills/cosmic-build/references/dcs-code-rules.md +180 -0
- package/skills/cosmic-build/references/delivery.md +16 -2
- package/skills/cosmic-build/references/execution-contract.md +15 -3
- package/skills/cosmic-build/references/java-plugin-delivery.md +21 -11
- package/skills/cosmic-build/references/plugin-handoff-input.md +82 -24
- package/skills/cosmic-build/references/plugin-runtime-routing.md +23 -6
- package/skills/cosmic-build/references/report-filter-contract.md +53 -15
- package/skills/cosmic-build/references/sdk-inspection.md +4 -1
- package/skills/cosmic-build/references/type-registry.json +26 -17
- package/skills/cosmic-build/schemas/data-model-authoring.schema.json +1 -1
- package/skills/cosmic-build/scripts/_http_client.py +3 -0
- package/skills/cosmic-build/scripts/compile_plugin_source.py +251 -42
- package/skills/cosmic-build/scripts/configure_list.py +112 -0
- package/skills/cosmic-build/scripts/dcs-rule-catalog.json +799 -0
- package/skills/cosmic-build/scripts/dcs_code_gate.py +1976 -0
- package/skills/cosmic-build/scripts/execution_binding.py +25 -0
- package/skills/cosmic-build/scripts/platform_api_contract.py +352 -7
- package/skills/cosmic-build/scripts/plugin_source_gate.py +9 -1
- package/skills/cosmic-build/scripts/plugin_target_sdk.py +12 -8
- package/skills/cosmic-build/scripts/prepare_plugin_handoff.py +488 -88
- package/skills/cosmic-build/scripts/query_plugin_handoff.py +207 -20
- package/skills/cosmic-build/scripts/register_plugins.py +27 -10
- package/skills/cosmic-build/scripts/render_output_cards.py +57 -20
- package/skills/cosmic-build/scripts/resolve_output_path.py +60 -26
- package/skills/cosmic-build/scripts/run_java_tests.py +162 -0
- package/skills/cosmic-build/scripts/sync_generated_locale_assets.py +28 -0
- package/skills/cosmic-build/scripts/test/fixtures/ReportSdkCheck.java +1 -22
- package/skills/cosmic-build/scripts/test/fixtures/list_column_property_declarations.json +887 -0
- package/skills/cosmic-build/scripts/test/fixtures/operation-entry-field-prepare.java +78 -0
- package/skills/cosmic-build/scripts/test/test_add_field_required_contract.py +12 -0
- package/skills/cosmic-build/scripts/test/test_batch5_drift_governance.py +23 -2
- package/skills/cosmic-build/scripts/test/test_compile_plugin_source.py +287 -4
- package/skills/cosmic-build/scripts/test/test_configure_list_delivery.py +132 -0
- package/skills/cosmic-build/scripts/test/test_cosmic_sdk_examples.py +58 -1
- package/skills/cosmic-build/scripts/test/test_data_model_contract.py +19 -0
- package/skills/cosmic-build/scripts/test/test_dcs_code_gate.py +901 -0
- package/skills/cosmic-build/scripts/test/test_delivery_stage_boundary.py +1 -1
- package/skills/cosmic-build/scripts/test/test_direct_cli_architecture.py +2 -0
- package/skills/cosmic-build/scripts/test/test_http_client.py +6 -0
- package/skills/cosmic-build/scripts/test/test_java_toolchain_feedback.py +206 -0
- package/skills/cosmic-build/scripts/test/test_kd_api.py +21 -0
- package/skills/cosmic-build/scripts/test/test_list_configuration_closure.py +184 -0
- package/skills/cosmic-build/scripts/test/test_mcp_cli.py +139 -2
- package/skills/cosmic-build/scripts/test/test_menu_creation_closure.py +16 -0
- package/skills/cosmic-build/scripts/test/test_modeling_input_recovery.py +55 -1
- package/skills/cosmic-build/scripts/test/test_open_world_cli_catalog.py +1 -1
- package/skills/cosmic-build/scripts/test/test_operation_field_preparation.py +84 -0
- package/skills/cosmic-build/scripts/test/test_platform_api_contract.py +322 -1
- package/skills/cosmic-build/scripts/test/test_plugin_handoff.py +951 -4
- package/skills/cosmic-build/scripts/test/test_plugin_source_gate.py +30 -2
- package/skills/cosmic-build/scripts/test/test_plugin_target_sdk.py +21 -0
- package/skills/cosmic-build/scripts/test/test_property_reroute.py +165 -0
- package/skills/cosmic-build/scripts/test/test_property_schema_first.py +35 -0
- package/skills/cosmic-build/scripts/test/test_query_plugin_handoff.py +221 -0
- package/skills/cosmic-build/scripts/test/test_register_plugins.py +76 -4
- package/skills/cosmic-build/scripts/test/test_render_output_cards.py +75 -3
- package/skills/cosmic-build/scripts/test/test_report_business_contract.py +27 -5
- package/skills/cosmic-build/scripts/test/test_report_doc_contract_sync.py +103 -11
- package/skills/cosmic-build/scripts/test/test_report_plugin_source_gate.py +46 -16
- package/skills/cosmic-build/scripts/test/test_report_properties.py +74 -15
- package/skills/cosmic-build/scripts/test/test_report_query_data_source.py +3 -1
- package/skills/cosmic-build/scripts/test/test_report_sdk_optimization.py +1 -2
- package/skills/cosmic-build/scripts/test/test_test_environment.py +76 -1
- package/skills/cosmic-build/scripts/test_environment.py +67 -1
- package/skills/cosmic-build/skills/cosmic-coding-impl/SKILL.md +52 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/apis/ai_meta.md +16 -3
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/report-write-contracts.json +59 -26
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/cli/rule-domain.json +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json +75 -18
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_auto.py +39 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/pdm/scripts/pdm_client.py +97 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/modules/verify/SKILL.md +10 -12
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_apps.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_formactions.py +38 -29
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_listui.py +400 -233
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_metaops.py +87 -45
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_properties.py +42 -15
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/cmd_rules.py +135 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/config.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/execution_errors.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/formatters.py +17 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/property_reroute.py +15 -5
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/report_ops.py +1429 -238
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/_core/validators.py +56 -28
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/cosmic_api.py +380 -134
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/field_decorator.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_export.py +12 -11
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/meta_verifier.py +42 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_add_field_placement_closure.py +8 -8
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_configuration_readback.py +94 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_build_receipt_resume.py +47 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_metadata_unchanged_fail_closed.py +87 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_dym_evidence.py +23 -23
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_entity_integrity.py +279 -4
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_field_guard.py +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_plugin_contract.py +509 -32
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_readback_derivation.py +178 -1
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_recreate_plan.py +125 -10
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_serve_contract.py +208 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_report_typed_filters.py +259 -50
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_inventory_perf.py +29 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_rule_write_closure.py +20 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_post_build_recovery.py +18 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_batch_verification_evidence.py +35 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_run_identity_guard.py +25 -0
- package/skills/cosmic-build/skills/cosmic-meta-api/scripts/test/test_time_range_rule.py +98 -0
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/domain-model/report-model.md +20 -9
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/generation/report-modeling-guide.md +55 -25
- package/skills/cosmic-build/skills/cosmic-meta-knowledge/property-guides/report-column-properties.md +15 -39
- package/skills/cosmic-build/skills/cosmic-meta-modeling/SKILL.md +19 -17
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/SKILL.md +82 -38
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-batch.md +64 -19
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-plugin.md +32 -20
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/examples/golden-example-report-tree.md +78 -31
- package/skills/cosmic-build/skills/cosmic-meta-modeling/modules/report/report-artifact-schema.json +2 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-companion-fields.md +3 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/references/authoring-list-configuration.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_contract.py +3 -1
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/data_model_emit.py +27 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/report_validator.py +99 -44
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_examples.py +31 -4
- package/skills/cosmic-build/skills/cosmic-meta-modeling/scripts/test_report_validator.py +129 -29
- package/skills/cosmic-build/skills/cosmic-meta-modifier/SKILL.md +6 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/operation/validation.md +2 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/property/SKILL.md +1 -1
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/report-op/SKILL.md +104 -25
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/rule/SKILL.md +9 -0
- package/skills/cosmic-build/skills/cosmic-meta-modifier/modules/verify/usage-guide.md +2 -2
- package/skills/cosmic-build/skills/cosmic-meta-modifier/references/list-filter-modification.md +39 -12
- package/skills/cosmic-build/skills/mcp-server/SKILL.md +39 -24
- package/skills/cosmic-build/skills/mcp-server/scripts/mcp_cli.py +20 -6
- package/skills/cosmic-dev-best-practices/SKILL.md +1 -1
- package/skills/cosmic-dev-best-practices/references/README.md +2 -2
- package/skills/cosmic-dev-best-practices/references/basedata-dev-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/bill-dev-convention.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-document-convention.md +2 -2
- package/skills/cosmic-dev-best-practices/references/design-examples/basedata-design-example.md +4 -4
- package/skills/cosmic-dev-best-practices/references/design-examples/bill-design-example.md +13 -13
- package/skills/cosmic-dev-best-practices/references/elements/CustomerField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/README.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/SupplierField.md +1 -1
- package/skills/cosmic-dev-best-practices/references/elements/common-field-property-conventions.md +109 -13
- package/skills/cosmic-dev-best-practices/references/layout-control-convention.md +1 -1
- package/skills/erp-analysis/references/delivery-contract.md +2 -1
- package/skills/erp-analysis/templates/AGENTS.template.md +3 -1
- package/skills/cosmic-build/assets/platform-report-filters/TypedReportFilterFields.java +0 -169
- package/skills/cosmic-build/assets/platform-report-filters/platform.patch +0 -98
- package/skills/cosmic-build/scripts/apply_report_filter_extension.py +0 -39
package/skills/cosmic-build/skills/cosmic-meta-api/contracts/schema/report-write.schema.json
CHANGED
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
"pattern": "^[A-Za-z_][A-Za-z0-9_]*$",
|
|
9
9
|
"minLength": 1
|
|
10
10
|
},
|
|
11
|
+
"columnRemoval": {
|
|
12
|
+
"type": "object",
|
|
13
|
+
"additionalProperties": false,
|
|
14
|
+
"required": ["fieldKey"],
|
|
15
|
+
"properties": {
|
|
16
|
+
"fieldKey": {"$ref": "#/$defs/identifier", "description": "Exact leaf column key; groups are not supported. Backend ReportColumns.remove patch required."}
|
|
17
|
+
}
|
|
18
|
+
},
|
|
11
19
|
"sort": {
|
|
12
20
|
"type": "object",
|
|
13
21
|
"additionalProperties": false,
|
|
@@ -30,7 +38,7 @@
|
|
|
30
38
|
},
|
|
31
39
|
"fieldType": {
|
|
32
40
|
"enum": ["text", "integer", "bigint", "decimal", "date", "datetime", "boolean", "picture", "combofield", "formula"],
|
|
33
|
-
"description": "
|
|
41
|
+
"description": "仅这些列类型具有已验证写入/回读对称性。decimal 表示标量存储,不能证明单位/币别动态精度;amount/qty/price 的 presentation 当前写前拒绝,需求须保留至平台引用链路验证。基础资料呈现使用 text 或 bigint,并编译为插件要求,不代表运行验收。"
|
|
34
42
|
},
|
|
35
43
|
"caption": {"type": "string", "minLength": 1},
|
|
36
44
|
"width": {"type": "string", "minLength": 1},
|
|
@@ -44,7 +52,8 @@
|
|
|
44
52
|
"type": "object",
|
|
45
53
|
"additionalProperties": false,
|
|
46
54
|
"required": ["kind"],
|
|
47
|
-
"description": "
|
|
55
|
+
"description": "运行呈现不直接写入平台,支持的类型编译为插件契约。amount/qty/price 的动态精度暂被 REPORT_PRECISION_REFERENCE_UNVERIFIED 写前阻断;隐藏 bigint、相同名称及 getColumns() 工厂签名不证明 DynamicObject 引用。原始或历史需求编译保留 precisionSource,但必须先验证平台引用字段、旧字段迁移和格式化前批量物化;不能固定精度降级。",
|
|
56
|
+
"not": {"required": ["kind"], "properties": {"kind": {"enum": ["amount", "qty", "price"]}}},
|
|
48
57
|
"properties": {
|
|
49
58
|
"kind": {
|
|
50
59
|
"enum": ["amount", "qty", "price", "basedata", "decimal"],
|
|
@@ -52,11 +61,11 @@
|
|
|
52
61
|
},
|
|
53
62
|
"currencyField": {
|
|
54
63
|
"$ref": "#/$defs/identifier",
|
|
55
|
-
"description": "
|
|
64
|
+
"description": "amount/price 的币别引用字段要求,仅保留历史需求形状,当前写入拒绝。fieldKey == dateIndex 只证明名称一致,隐藏 bigint 仍是 Long,不能作为 DynamicObject 精度引用。平台引用字段及批量物化验证后才能开放。"
|
|
56
65
|
},
|
|
57
66
|
"measureUnitField": {
|
|
58
67
|
"$ref": "#/$defs/identifier",
|
|
59
|
-
"description": "
|
|
68
|
+
"description": "qty 的单位引用字段要求;与 currencyField 同样被写前阻断。Long 主键不是 DynamicObject 引用,不能靠 hide 或工厂转换证明运行正确。"
|
|
60
69
|
},
|
|
61
70
|
"entityId": {"type": "string", "minLength": 1, "description": "Required for basedata, e.g. bd_supplier."},
|
|
62
71
|
"displayProp": {"type": "string", "minLength": 1, "description": "Required for basedata, e.g. name."},
|
|
@@ -86,6 +95,28 @@
|
|
|
86
95
|
"items": {"$ref": "#/$defs/columnOrGroup"},
|
|
87
96
|
"examples": [[{"kind": "column", "fieldKey": "contract_no", "dateIndex": "contract_no", "fieldType": "text", "caption": "合同号"}]]
|
|
88
97
|
},
|
|
98
|
+
"filterFieldType": {
|
|
99
|
+
"enum": ["TextField", "IntegerField", "BigIntField", "DecimalField", "DateField", "DateTimeField", "DateRangeField", "TimeField", "TimeRangeField", "CheckBoxField", "ComboField", "MulComboField", "CheckBoxGroupField", "BasedataField", "MulBasedataField", "OrgField", "UserField", "CurrencyField", "UnitField", "MaterielField", "CustomerField", "SupplierField", "AssistantField", "ItemClassTypeField", "ItemClassField"],
|
|
100
|
+
"description": "The filter's native editor, built by the platform's typed report filter API (a platform patch). The CLI also accepts the platform's lower-case aliases (basedata, mulbasedata, combo, date, org, ...) and normalizes them to these class names. With compare=between, DateField / DateTimeField become DateRangeField and TimeField becomes TimeRangeField; a range editor needs compare=between. A platform without the patch ignores fieldType and renders its legacy control (TextField, or DateRangeField for between); the write reports such a filter as degraded instead of failing, and the read-back confirms which editor rendered."
|
|
101
|
+
},
|
|
102
|
+
"filterComboItem": {
|
|
103
|
+
"type": "object",
|
|
104
|
+
"additionalProperties": false,
|
|
105
|
+
"required": ["value", "caption"],
|
|
106
|
+
"properties": {
|
|
107
|
+
"value": {"type": ["string", "number"], "description": "The stored value query() compares against, kept exactly: 0 and leading zeros are values."},
|
|
108
|
+
"caption": {"type": "string", "minLength": 1}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"filterItemClassType": {
|
|
112
|
+
"type": "object",
|
|
113
|
+
"additionalProperties": false,
|
|
114
|
+
"required": ["basedataNumber"],
|
|
115
|
+
"properties": {
|
|
116
|
+
"basedataNumber": {"type": "string", "minLength": 1},
|
|
117
|
+
"caption": {"type": "string", "minLength": 1}
|
|
118
|
+
}
|
|
119
|
+
},
|
|
89
120
|
"filter": {
|
|
90
121
|
"type": "object",
|
|
91
122
|
"additionalProperties": false,
|
|
@@ -93,27 +124,47 @@
|
|
|
93
124
|
"properties": {
|
|
94
125
|
"fieldPath": {
|
|
95
126
|
"$ref": "#/$defs/identifier",
|
|
96
|
-
"description": "Filter key, not a dotted entity path. setReportFilters creates the control under this key and the plugin reads its value through ReportQueryParam.getFilter()
|
|
127
|
+
"description": "Filter key, not a dotted entity path. setReportFilters creates the control under this key and the plugin reads its value through ReportQueryParam.getFilter() under this key. It must not repeat any reportColumns fieldKey, group keys included: control and column would share one element key, the write and every API read-back would still succeed, and only the designer would reject the duplicated identifier. Filtering on a displayed column means a distinct key such as flt_<column>, which the plugin applies to that column."
|
|
97
128
|
},
|
|
98
129
|
"displayName": {"type": "string", "minLength": 1},
|
|
99
130
|
"compare": {
|
|
100
131
|
"enum": ["=", "in", "between"],
|
|
101
|
-
"description": "
|
|
132
|
+
"description": "业务查询比较(=、in、between),必须在所属 query() 显式落实。新 API 可从 ReportFilterFieldConfig 投影实际比较配置,但不代表 setReportFilters 自动绑定业务查询;回读保留 compareConfig 来源,不猜 compareTypeId。编辑器由 fieldType 决定。"
|
|
102
133
|
},
|
|
103
|
-
"
|
|
104
|
-
"
|
|
134
|
+
"fieldType": {"$ref": "#/$defs/filterFieldType"},
|
|
135
|
+
"basedataNumber": {"type": "string", "minLength": 1, "description": "Required for BasedataField and MulBasedataField: the base data form number, e.g. bd_supplier. Only the number is accepted; the platform resolves and validates it."},
|
|
136
|
+
"comboItems": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/filterComboItem"}, "description": "Required for ComboField, MulComboField and CheckBoxGroupField: the options, with unique values."},
|
|
137
|
+
"itemClassTypes": {"type": "array", "minItems": 1, "items": {"$ref": "#/$defs/filterItemClassType"}, "description": "Required for ItemClassTypeField: the base data types the user chooses between."},
|
|
138
|
+
"typeFieldKey": {"$ref": "#/$defs/identifier", "description": "Required for ItemClassField: the fieldPath of the ItemClassTypeField filter in the same request that selects its base data type."},
|
|
139
|
+
"asstTypeId": {"type": "string", "pattern": "^\\d+$", "description": "Required for AssistantField: the numeric id of the assistant data group."},
|
|
140
|
+
"orgFuncs": {"type": "string", "minLength": 1, "description": "Optional for OrgField: the organization function code the picker is scoped to; the platform refuses a code it does not list."},
|
|
141
|
+
"showStyle": {"type": "integer", "minimum": 0, "maximum": 3, "description": "Optional for CheckBoxField: the platform's checkbox display style."},
|
|
142
|
+
"startDateFieldKey": {"$ref": "#/$defs/identifier", "description": "Optional for DateRangeField / TimeRangeField: the runtime key of the range's start item. Default <fieldPath>_startdate, or <fieldPath>_starttime for a time range. A platform without the patch always uses the default."},
|
|
143
|
+
"endDateFieldKey": {"$ref": "#/$defs/identifier", "description": "Optional for DateRangeField / TimeRangeField: the runtime key of the range's end item. Default <fieldPath>_enddate, or <fieldPath>_endtime for a time range."},
|
|
144
|
+
"defaultValue": {"description": "Not accepted on an F7 editor (the platform refuses it). CheckBoxField takes true, false, 1 or 0. Compiled into the plugin contract as a requirement either way."},
|
|
105
145
|
"mustInput": {
|
|
106
146
|
"type": "boolean",
|
|
107
|
-
"description": "
|
|
147
|
+
"description": "类型化过滤 API 设置实体字段必录;新投影从实际字段 Boolean 回读,旧平台可由新鲜 DYM 补证据。null/unknown 不是 false;保存标志不能替代 verifyQuery/query 在取数前的共用校验。不能与 visible:false 合用。"
|
|
108
148
|
},
|
|
109
|
-
"visible": {"type": "boolean", "description": "
|
|
110
|
-
}
|
|
149
|
+
"visible": {"type": "boolean", "description": "visible:false 声明初始隐藏。新投影使用 visibleEffective 的实际 Boolean,控件阶段字符串不是 Boolean,null/unknown 不算已验证;旧平台保留 DYM 回读。"}
|
|
150
|
+
},
|
|
151
|
+
"allOf": [
|
|
152
|
+
{"if": {"required": ["fieldType"], "properties": {"fieldType": {"enum": ["BasedataField", "MulBasedataField"]}}}, "then": {"required": ["basedataNumber"]}},
|
|
153
|
+
{"if": {"required": ["fieldType"], "properties": {"fieldType": {"enum": ["ComboField", "MulComboField", "CheckBoxGroupField"]}}}, "then": {"required": ["comboItems"]}},
|
|
154
|
+
{"if": {"required": ["fieldType"], "properties": {"fieldType": {"const": "ItemClassTypeField"}}}, "then": {"required": ["itemClassTypes"]}},
|
|
155
|
+
{"if": {"required": ["fieldType"], "properties": {"fieldType": {"const": "ItemClassField"}}}, "then": {"required": ["typeFieldKey"]}},
|
|
156
|
+
{"if": {"required": ["fieldType"], "properties": {"fieldType": {"const": "AssistantField"}}}, "then": {"required": ["asstTypeId"]}}
|
|
157
|
+
]
|
|
111
158
|
},
|
|
112
159
|
"filters": {
|
|
113
160
|
"type": "array",
|
|
114
161
|
"minItems": 1,
|
|
115
162
|
"items": {"$ref": "#/$defs/filter"},
|
|
116
|
-
"examples": [[
|
|
163
|
+
"examples": [[
|
|
164
|
+
{"fieldPath": "flt_sign_date", "displayName": "签约日期", "compare": "between", "fieldType": "DateRangeField", "mustInput": true},
|
|
165
|
+
{"fieldPath": "flt_supplier", "displayName": "供应商", "compare": "in", "fieldType": "MulBasedataField", "basedataNumber": "bd_supplier"},
|
|
166
|
+
{"fieldPath": "flt_status", "displayName": "状态", "compare": "=", "fieldType": "ComboField", "comboItems": [{"value": "A", "caption": "暂存"}, {"value": "C", "caption": "已审核"}]}
|
|
167
|
+
]]
|
|
117
168
|
},
|
|
118
169
|
"sorts": {
|
|
119
170
|
"type": "array",
|
|
@@ -159,7 +210,7 @@
|
|
|
159
210
|
},
|
|
160
211
|
"reportPropertyTarget": {
|
|
161
212
|
"enum": ["list", "filterPanel", "moreFilterPanel", "form", "tree", "column"],
|
|
162
|
-
"description": "Which host control the property map is applied to. list -> ReportListAp (reportlistap); filterPanel -> ReportFilterAp (reportfilterap); moreFilterPanel -> ReportMoreFilterPanelAp (reportmorefilterpanelap); form -> ReportFormAp, whose element key IS the form number; tree -> ReportTreeAp
|
|
213
|
+
"description": "Which host control the property map is applied to. list -> ReportListAp (reportlistap); filterPanel -> ReportFilterAp (reportfilterap); moreFilterPanel -> ReportMoreFilterPanelAp (reportmorefilterpanelap); form -> ReportFormAp, whose element key IS the form number; tree -> the report's one ReportTreeAp, found by element type because a user adds it in the designer under any key; column -> one EntryFieldAp or EntryFieldGroupAp named by --element. 'column' needs an --element and 'tree' takes one only to pick among several trees; every other target resolves its host from the form number."
|
|
163
214
|
},
|
|
164
215
|
"reportListProperties": {
|
|
165
216
|
"type": "object",
|
|
@@ -200,7 +251,7 @@
|
|
|
200
251
|
"type": "array",
|
|
201
252
|
"minItems": 1,
|
|
202
253
|
"items": {"$ref": "#/$defs/identifier"},
|
|
203
|
-
"description": "A list of filter keys, which the CLI serialises to the JSON array string the runtime parses. setReportFilters writes the same property as a comma-joined string the runtime cannot read, so this is also the repair for a report
|
|
254
|
+
"description": "A list of filter keys, which the CLI serialises to the JSON array string the runtime parses. On a platform without the typed report filter patch, setReportFilters writes the same property as a comma-joined string the runtime cannot read, so this is also the repair for a report that platform wrote; the command contract carries the measurement."
|
|
204
255
|
}
|
|
205
256
|
},
|
|
206
257
|
"examples": [{"UserConfiguration": true, "ShowFilterFields": ["flt_biztime", "flt_supplier"]}]
|
|
@@ -229,7 +280,7 @@
|
|
|
229
280
|
"description": "ReportTreeAp scalars, so they apply only to a report that actually has a left tree; the CLI refuses the write before sending when the element resolves none of them. BaseEntityId is refused here because it has its own candidate-backed route (setComplexProperty --complexType baseEntityId, values from listBaseEntityTypes).",
|
|
230
281
|
"properties": {
|
|
231
282
|
"LeafNodeQuery": {"type": "boolean"},
|
|
232
|
-
"ReportPlugin": {"type": "string", "minLength": 1, "description": "The left tree's own data plugin class name. Writing it records the name; it does not create the class."}
|
|
283
|
+
"ReportPlugin": {"type": "string", "minLength": 1, "description": "The left tree's own data plugin class name: an AbstractReportTreeDataPlugin returning List<TreeNode>, registered here and never as a reportPlugins entry (that slot is the report list's data-fetch plugin). Writing it records the name; it does not create the class, which Java delivery provides as type reporttreelist."}
|
|
233
284
|
}
|
|
234
285
|
},
|
|
235
286
|
"reportColumnLeafProperties": {
|
|
@@ -317,7 +368,7 @@
|
|
|
317
368
|
"type": "object",
|
|
318
369
|
"additionalProperties": false,
|
|
319
370
|
"minProperties": 1,
|
|
320
|
-
"description": "The report Artifact's `reportProperties` section: the same properties the modifier writes one host at a time, declared up front so one `run` creates a report that is already configured. It is applied LAST in the pipeline, after the filters, which is what leaves ShowFilterFields in the JSON-array form the runtime parses rather than the comma-joined form the filter write produces. Each column key is resolved against the artifact's own reportColumns to decide whether it is an EntryFieldAp or an EntryFieldGroupAp, because the two accept different property sets and the platform silently ignores a key sent to the wrong one.",
|
|
371
|
+
"description": "The report Artifact's `reportProperties` section: the same properties the modifier writes one host at a time, declared up front so one `run` creates a report that is already configured. It is applied LAST in the pipeline, after the filters, which is what leaves ShowFilterFields in the JSON-array form the runtime parses rather than the comma-joined form the filter write produces on a platform without the typed report filter patch, and lets a declared FilterMustInput or defaultHiddenFields outlast a patched filter write, which resets both. Each column key is resolved against the artifact's own reportColumns to decide whether it is an EntryFieldAp or an EntryFieldGroupAp, because the two accept different property sets and the platform silently ignores a key sent to the wrong one.",
|
|
321
372
|
"properties": {
|
|
322
373
|
"list": {"$ref": "#/$defs/reportListProperties"},
|
|
323
374
|
"filterPanel": {"$ref": "#/$defs/reportFilterPanelProperties"},
|
|
@@ -325,7 +376,7 @@
|
|
|
325
376
|
"form": {"$ref": "#/$defs/reportFormProperties"},
|
|
326
377
|
"tree": {
|
|
327
378
|
"$ref": "#/$defs/reportTreeProperties",
|
|
328
|
-
"description": "Only a report
|
|
379
|
+
"description": "Only a report that carries a ReportTreeAp has these, and no public report template presets one: bos_grouprpttpl is the grouped report (group panel + group list), not a left tree. A left tree exists only after a user adds the report tree control to the report in the designer. On any other report the write is refused before it is sent, so declaring them in a creation Artifact fails the run after createPage; the validator refuses it as R-property-tree."
|
|
329
380
|
},
|
|
330
381
|
"columns": {
|
|
331
382
|
"type": "object",
|
|
@@ -381,6 +432,11 @@
|
|
|
381
432
|
{"command": "setReportColumns", "input": [{"kind": "column", "fieldKey": "sup", "dateIndex": "sup", "fieldType": "decimal", "caption": "Supplier", "presentation": {"kind": "basedata", "entityId": "bd_supplier", "displayProp": "name"}}], "error": "presentation.kind='basedata' needs fieldType in ['bigint', 'text']"},
|
|
382
433
|
{"command": "addReportColumns", "input": [{"kind": "column", "fieldKey": "creator_email", "dateIndex": "creator_email", "fieldType": "text"}], "error": "caption is required; addReportColumns shares the columns contract with setReportColumns and only differs in write semantics"},
|
|
383
434
|
{"command": "setReportFilters", "input": [{"fieldPath": "biz_date", "displayName": "Business Date", "compare": "between", "enableFuzzy": true}], "error": "filters[0] contains unsupported properties ['enableFuzzy']"},
|
|
435
|
+
{"command": "setReportFilters", "input": [{"fieldPath": "flt_supplier", "displayName": "Supplier", "compare": "in", "fieldType": "MulBasedataField"}], "error": "filters[0].basedataNumber is required for fieldType MulBasedataField"},
|
|
436
|
+
{"command": "setReportFilters", "input": [{"fieldPath": "flt_supplier", "displayName": "Supplier", "compare": "between", "fieldType": "BasedataField", "basedataNumber": "bd_supplier"}], "error": "filters[0]: compare=between needs a range editor (DateRangeField or TimeRangeField), and BasedataField holds one value; declare a from/to pair of filters instead"},
|
|
437
|
+
{"command": "setReportFilters", "input": [{"fieldPath": "flt_status", "displayName": "Status", "compare": "=", "comboItems": [{"value": "A", "caption": "Draft"}]}], "error": "filters[0].comboItems needs fieldType: it configures a typed filter editor"},
|
|
438
|
+
{"command": "setReportFilters", "input": [{"fieldPath": "flt_org", "displayName": "Org", "compare": "=", "fieldType": "OrgField", "basedataNumber": "bos_org"}], "error": "filters[0].basedataNumber does not apply to fieldType OrgField; it configures ['BasedataField', 'MulBasedataField']"},
|
|
439
|
+
{"command": "setReportFilters", "input": [{"fieldPath": "flt_supplier", "displayName": "Supplier", "compare": "=", "source": {"formNumber": "pm_purorderbill", "fieldKey": "supplier"}}], "error": "filters[0].source is retired: declare fieldType and its parameters instead (an F7 filter is fieldType plus basedataNumber); read the source field's type and reference from that form's getFormSchema"},
|
|
384
440
|
{"command": "setReportSorts", "input": [{"fieldKey": "amount", "direction": "descending"}], "error": "direction must be asc or desc"},
|
|
385
441
|
{"command": "registerReportPlugin", "input": {"className": "kd.example.report.P", "generateCode": true}, "error": "plugin contains unsupported properties ['generateCode']"},
|
|
386
442
|
{"command": "setReportProperties", "input": {"target": "list", "input": {"ShowSeq": 1}}, "error": "input.ShowSeq must be a JSON boolean, got int"},
|
|
@@ -395,7 +451,8 @@
|
|
|
395
451
|
{"command": "setReportProperties", "input": {"target": "column", "element": "amt_group", "input": {"Summary": 1}}, "error": "Summary is not a writable property of target column (EntryFieldGroupAp); it accepts ['Freeze', 'GroupCollapsible', 'GroupDefaultCollapse', 'Hidden', 'Hide', 'Merge', 'TextAlign']"},
|
|
396
452
|
{"command": "setReportProperties", "input": {"target": "column", "element": "total_amount", "input": {"Merge": true}}, "error": "Merge is not a writable property of target column (EntryFieldAp)"},
|
|
397
453
|
{"command": "setReportProperties", "input": {"target": "list", "element": "reportlistap", "input": {"ShowSeq": false}}, "error": "target 'list' writes reportlistap and takes no --element"},
|
|
398
|
-
{"command": "setReportProperties", "input": {"target": "tree", "input": {"LeafNodeQuery": false}}, "error": "REPORT_PROPERTY_ELEMENT_MISMATCH: '
|
|
454
|
+
{"command": "setReportProperties", "input": {"target": "tree", "input": {"LeafNodeQuery": false}}, "error": "REPORT_PROPERTY_ELEMENT_MISMATCH: 'kdtest_cost_analysis_rpt' has no ReportTreeAp control, so there is nothing to write to — no public report template presets one; a user adds it in the designer first"},
|
|
455
|
+
{"command": "setReportProperties", "input": {"target": "tree", "input": {"LeafNodeQuery": false}}, "error": "'kdtest_cost_analysis_rpt' has several ReportTreeAp controls ['tree_a', 'tree_b']; name one with --element"},
|
|
399
456
|
{"command": "setReportRowBackColor", "input": {"rules": []}, "error": "rules must be a non-empty JSON array of rule objects"},
|
|
400
457
|
{"command": "setReportFilterFieldConfig", "input": {"fields": [{"fieldName": "amount"}]}, "error": "fields[0].key is required"},
|
|
401
458
|
{"command": "setReportDefaultHiddenFields", "input": {"fields": [{"fieldKey": "flt_a", "userHide": false}]}, "error": "every entry has userHide false, and the platform stores only userHide true, so this write would change nothing"}
|
|
@@ -1239,6 +1239,8 @@ def _run_patch_remove_index(patch_args):
|
|
|
1239
1239
|
"operation": "remove-index",
|
|
1240
1240
|
"table": target_table.get("tableName", table_name),
|
|
1241
1241
|
"removedIndex": removed.get("indexName", index_name),
|
|
1242
|
+
"note": ("Only takes effect for indexes not yet on the platform: save merges indexes "
|
|
1243
|
+
"additively and never drops an existing one, so --apply rejects removing it"),
|
|
1242
1244
|
}
|
|
1243
1245
|
print(json.dumps(result, ensure_ascii=False, indent=2))
|
|
1244
1246
|
sys.exit(0)
|
|
@@ -1248,6 +1250,26 @@ def _run_patch_remove_index(patch_args):
|
|
|
1248
1250
|
# apply mode
|
|
1249
1251
|
# ---------------------------------------------------------------------------
|
|
1250
1252
|
|
|
1253
|
+
def find_dropped_indexes(confirmed_tables, current_tables):
|
|
1254
|
+
"""Return [(tableName, indexName)] present on the platform but missing from confirmed_tables.
|
|
1255
|
+
|
|
1256
|
+
The platform save merges indexes additively (an index absent from the request is kept),
|
|
1257
|
+
so such a removal would be reported as done while the index stays in place.
|
|
1258
|
+
"""
|
|
1259
|
+
confirmed_map = {(t.get("tableName") or "").strip().upper(): t for t in confirmed_tables}
|
|
1260
|
+
dropped = []
|
|
1261
|
+
for cur in current_tables or []:
|
|
1262
|
+
conf = confirmed_map.get((cur.get("tableName") or "").strip().upper())
|
|
1263
|
+
if conf is None:
|
|
1264
|
+
continue
|
|
1265
|
+
kept = {_idx_key(i) for i in (conf.get("indexes") or [])}
|
|
1266
|
+
for idx in cur.get("indexes") or []:
|
|
1267
|
+
key = _idx_key(idx)
|
|
1268
|
+
if key and key not in kept:
|
|
1269
|
+
dropped.append((cur.get("tableName", ""), idx.get("indexName", "")))
|
|
1270
|
+
return dropped
|
|
1271
|
+
|
|
1272
|
+
|
|
1251
1273
|
def run_apply(args):
|
|
1252
1274
|
"""Execution mode: read confirmed tableAps, execute save -> submit -> write files."""
|
|
1253
1275
|
lf = sys.stderr # Progress output to stderr
|
|
@@ -1316,6 +1338,23 @@ def run_apply(args):
|
|
|
1316
1338
|
model_id = args.model_id
|
|
1317
1339
|
_log(f" -> Model: mdlCode={mdl_code}, modelId={model_id}", file=lf)
|
|
1318
1340
|
|
|
1341
|
+
# -- Guard: save cannot drop existing indexes --
|
|
1342
|
+
table_names = [t.get("tableName", "") for t in confirmed_tables if t.get("tableName")]
|
|
1343
|
+
if table_names:
|
|
1344
|
+
try:
|
|
1345
|
+
current_tables = client.get_current_table_aps(model_id, table_names).get("tableAps", [])
|
|
1346
|
+
except Exception as e:
|
|
1347
|
+
_log(f"⚠️ Failed to fetch existing table structure, index removal check skipped: {e}", file=lf)
|
|
1348
|
+
current_tables = []
|
|
1349
|
+
dropped = find_dropped_indexes(confirmed_tables, current_tables)
|
|
1350
|
+
if dropped:
|
|
1351
|
+
_log("❌ TableAps removes indexes that already exist on the platform: "
|
|
1352
|
+
+ ", ".join(f"{t}.{i}" for t, i in dropped), file=lf)
|
|
1353
|
+
_log(" Platform save merges indexes additively and keeps them, so the removal would "
|
|
1354
|
+
"silently not happen. Restore these indexes in the file and drop them in the "
|
|
1355
|
+
"designer data model instead.", file=lf)
|
|
1356
|
+
sys.exit(1)
|
|
1357
|
+
|
|
1319
1358
|
# -- Step 6: Save to D table --
|
|
1320
1359
|
_log(f"\n[Save] Saving to D table...", file=lf)
|
|
1321
1360
|
try:
|
|
@@ -43,7 +43,7 @@ if str(_PROJECT_ROOT_SCRIPTS_DIR) not in sys.path:
|
|
|
43
43
|
sys.path.insert(0, str(_PROJECT_ROOT_SCRIPTS_DIR))
|
|
44
44
|
|
|
45
45
|
import cosmic_api # noqa: E402 — 复用 do_auth / load_config / _post / _get
|
|
46
|
-
from kd_auth import get_token as _kd_get_token # noqa: E402
|
|
46
|
+
from kd_auth import get_token as _kd_get_token, make_auth_headers # noqa: E402
|
|
47
47
|
from kd_env_loader import get_kd_config_path, load_effective_skill_config # noqa: E402
|
|
48
48
|
from _http_client import DEFAULT_USER_AGENT, get_ssl_context # noqa: E402
|
|
49
49
|
|
|
@@ -144,6 +144,9 @@ def resolve_credentials(cfg_raw: dict, env: str) -> dict:
|
|
|
144
144
|
if env == "server":
|
|
145
145
|
s = cfg_raw.get("server", {})
|
|
146
146
|
creds = {
|
|
147
|
+
# 整节透传:kd_auth 依据 normalAccessToken 路由认证模式,并读取 loginUser /
|
|
148
|
+
# userType / acgwIdentity;任一字段丢失都会静默改变认证模式、getToken 用户名或网关头
|
|
149
|
+
**s,
|
|
147
150
|
# cosmic_api.do_auth() 所需字段
|
|
148
151
|
"base_url": (s.get("baseUrl", "") or "").strip().rstrip("/"),
|
|
149
152
|
"login_user": (s.get("user", "") or "").strip(),
|
|
@@ -151,7 +154,8 @@ def resolve_credentials(cfg_raw: dict, env: str) -> dict:
|
|
|
151
154
|
"app_secret": (s.get("appSecret", "") or "").strip(),
|
|
152
155
|
"account_id": (s.get("accountId", "") or "").strip(),
|
|
153
156
|
"tenant_id": (s.get("tenantId", "") or "").strip(),
|
|
154
|
-
"user_type": "Mobile",
|
|
157
|
+
"user_type": (s.get("userType", "") or "Mobile").strip(),
|
|
158
|
+
"normalAccessToken": s.get("normalAccessToken") is True,
|
|
155
159
|
"language": "zh_CN",
|
|
156
160
|
# 硬编码 API 基路径
|
|
157
161
|
"api_base_path": "/kapi/v2/devportal/ai-meta",
|
|
@@ -165,13 +169,15 @@ def resolve_credentials(cfg_raw: dict, env: str) -> dict:
|
|
|
165
169
|
elif env == "datamodel":
|
|
166
170
|
p = cfg_raw.get("pdm", {})
|
|
167
171
|
creds = {
|
|
172
|
+
**p, # 与 server 分支相同的整节透传
|
|
168
173
|
"base_url": (p.get("baseUrl", "") or "").strip().rstrip("/"),
|
|
169
174
|
"login_user": (p.get("user", "") or "").strip(),
|
|
170
175
|
"app_id": (p.get("appId", "") or "").strip(),
|
|
171
176
|
"app_secret": (p.get("appSecret", "") or "").strip(),
|
|
172
177
|
"account_id": (p.get("accountId", "") or "").strip(),
|
|
173
178
|
"tenant_id": (p.get("tenantId", "") or "").strip(),
|
|
174
|
-
"user_type": "Mobile",
|
|
179
|
+
"user_type": (p.get("userType", "") or "Mobile").strip(),
|
|
180
|
+
"normalAccessToken": p.get("normalAccessToken") is True,
|
|
175
181
|
"language": "zh_CN",
|
|
176
182
|
"api_base_path": "/kapi/v2/devportal/ai-meta",
|
|
177
183
|
# PDM 专用
|
|
@@ -185,6 +191,73 @@ def resolve_credentials(cfg_raw: dict, env: str) -> dict:
|
|
|
185
191
|
raise ValueError(f"未知 env 类型: {env!r},应为 'server' 或 'datamodel'")
|
|
186
192
|
|
|
187
193
|
|
|
194
|
+
def _same_index(a, b):
|
|
195
|
+
"""索引定义相同:unique / cluster 标志与有序列列表一致(不区分大小写)。"""
|
|
196
|
+
return (bool(a.get("unique")) == bool(b.get("unique"))
|
|
197
|
+
and bool(a.get("cluster")) == bool(b.get("cluster"))
|
|
198
|
+
and [str(c).upper() for c in a.get("columns") or []]
|
|
199
|
+
== [str(c).upper() for c in b.get("columns") or []])
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def columnless_index_tables(table_aps: list) -> list:
|
|
203
|
+
"""返回带索引但未带 columns 的表名列表,这些索引 save 无法解析。"""
|
|
204
|
+
return [t.get("tableName", "") for t in table_aps
|
|
205
|
+
if t.get("indexes") and not t.get("columns")]
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def align_index_columns(table_aps: list, current_tables=None) -> list:
|
|
209
|
+
"""使索引 / 主键列能被 save 解析,在未打补丁的平台上同样成立。
|
|
210
|
+
|
|
211
|
+
save 接口按 columnName 精确(区分大小写)对照请求中的 columns 解析这些列,解析不到的列
|
|
212
|
+
被静默丢弃,随后用缩短后的列表覆盖已有索引。convert 输出 "FLocaleID",而 M 表索引读回为
|
|
213
|
+
"FLOCALEID",于是 (FID, FLOCALEID) 索引在 submit 后退化为 (FID)。
|
|
214
|
+
|
|
215
|
+
- 可解析的列一律改写为请求 columns 中 columnName 的精确拼写。
|
|
216
|
+
- 未带 columns 的表会保留已有列,因此与 current_tables 相比未变化的索引从请求中省略:
|
|
217
|
+
save 会原样保留请求中未出现的已有索引。
|
|
218
|
+
- 否则抛 ValueError,而不是交给接口静默丢弃该列。带 columns 时 save 还会删除请求中
|
|
219
|
+
未出现的已有列,索引同样无法原样保留。
|
|
220
|
+
|
|
221
|
+
返回被省略的索引 [(tableName, indexName)]。
|
|
222
|
+
"""
|
|
223
|
+
current_idx = {}
|
|
224
|
+
for cur in current_tables or []:
|
|
225
|
+
tn = (cur.get("tableName") or "").upper()
|
|
226
|
+
for idx in cur.get("indexes") or []:
|
|
227
|
+
current_idx[(tn, (idx.get("indexName") or "").upper())] = idx
|
|
228
|
+
omitted = []
|
|
229
|
+
for table in table_aps:
|
|
230
|
+
table_name = table.get("tableName") or ""
|
|
231
|
+
actual_names = {}
|
|
232
|
+
for col in table.get("columns") or []:
|
|
233
|
+
name = col.get("columnName")
|
|
234
|
+
if name:
|
|
235
|
+
actual_names.setdefault(name.upper(), name)
|
|
236
|
+
pk = table.get("primaryKey")
|
|
237
|
+
if isinstance(pk, dict) and pk.get("columns"):
|
|
238
|
+
pk["columns"] = [actual_names.get(str(n).upper(), n) for n in pk["columns"]]
|
|
239
|
+
if not table.get("indexes"):
|
|
240
|
+
continue
|
|
241
|
+
kept = []
|
|
242
|
+
for idx in table["indexes"]:
|
|
243
|
+
cols = idx.get("columns") or []
|
|
244
|
+
aligned = [actual_names.get(str(n).upper()) for n in cols]
|
|
245
|
+
if None not in aligned:
|
|
246
|
+
idx["columns"] = aligned
|
|
247
|
+
kept.append(idx)
|
|
248
|
+
continue
|
|
249
|
+
cur = current_idx.get((table_name.upper(), (idx.get("indexName") or "").upper()))
|
|
250
|
+
if not table.get("columns") and cur is not None and _same_index(idx, cur):
|
|
251
|
+
omitted.append((table_name, idx.get("indexName", "")))
|
|
252
|
+
continue
|
|
253
|
+
missing = ", ".join(str(n) for n, a in zip(cols, aligned) if a is None)
|
|
254
|
+
raise ValueError(
|
|
255
|
+
f"索引 {idx.get('indexName')} 引用的列 {missing} 不在表 "
|
|
256
|
+
f"{table_name} 的 columns 中,save 会静默丢弃该列")
|
|
257
|
+
table["indexes"] = kept
|
|
258
|
+
return omitted
|
|
259
|
+
|
|
260
|
+
|
|
188
261
|
# ---------------------------------------------------------------------------
|
|
189
262
|
# PDMClient
|
|
190
263
|
# ---------------------------------------------------------------------------
|
|
@@ -233,7 +306,7 @@ class PDMClient:
|
|
|
233
306
|
# ------------------------------------------------------------------
|
|
234
307
|
|
|
235
308
|
def login(self, env="server", force=False):
|
|
236
|
-
"""
|
|
309
|
+
"""经 kd_auth.get_token 认证(自动路由 normal 两步 / enhanced getToken)
|
|
237
310
|
|
|
238
311
|
Args:
|
|
239
312
|
env: "server" 或 "datamodel"
|
|
@@ -245,9 +318,7 @@ class PDMClient:
|
|
|
245
318
|
if env == "server":
|
|
246
319
|
if not self._server_cfg:
|
|
247
320
|
raise ValueError("server 凭据未配置,请检查 ~/.kd/config.json 默认环境")
|
|
248
|
-
|
|
249
|
-
# 因此这里直接走 force=True 避免环境混淆
|
|
250
|
-
token = self._do_two_step_auth(self._server_cfg)
|
|
321
|
+
token = self._get_oauth_token(self._server_cfg)
|
|
251
322
|
self._server_token = token
|
|
252
323
|
print(f" [AUTH] server 环境认证成功: {token[:40]}...")
|
|
253
324
|
return token
|
|
@@ -293,7 +364,10 @@ class PDMClient:
|
|
|
293
364
|
return self._server_token if env == "server" else self._pdm_token
|
|
294
365
|
|
|
295
366
|
def _auth_headers(self, env):
|
|
296
|
-
|
|
367
|
+
# legacy_api:access_token + 无下划线的 accesstoken(经 nginx 不被丢弃)+ api:true,
|
|
368
|
+
# 环境位于网关后时另带 x-acgw-identity
|
|
369
|
+
cfg = self._server_cfg if env == "server" else self._pdm_cfg
|
|
370
|
+
return make_auth_headers(cfg, self._get_token(env), style="legacy_api")
|
|
297
371
|
|
|
298
372
|
def _api_call(self, env, method, path, data=None, retry_on_401=True):
|
|
299
373
|
"""通用 API 调用,自动处理 token 过期重试
|
|
@@ -432,8 +506,21 @@ class PDMClient:
|
|
|
432
506
|
{"modelId": ..., "validations": [...], "message": ...}
|
|
433
507
|
"""
|
|
434
508
|
path = "/kapi/v2/devportal/ai-meta/pdm/save"
|
|
435
|
-
if isinstance(table_aps_json,
|
|
436
|
-
|
|
509
|
+
table_aps = (json.loads(table_aps_json) if isinstance(table_aps_json, str)
|
|
510
|
+
else table_aps_json)
|
|
511
|
+
if isinstance(table_aps, list):
|
|
512
|
+
current = None
|
|
513
|
+
unresolved = columnless_index_tables(table_aps)
|
|
514
|
+
if unresolved:
|
|
515
|
+
try:
|
|
516
|
+
current = self.get_current_table_aps(model_id, unresolved).get("tableAps", [])
|
|
517
|
+
except Exception as e:
|
|
518
|
+
print(f" [WARN] 获取 {', '.join(unresolved)} 的现有索引失败: {e}",
|
|
519
|
+
file=sys.stderr)
|
|
520
|
+
for tn, iname in align_index_columns(table_aps, current):
|
|
521
|
+
print(f" [SAVE] 索引 {tn}.{iname} 未变化且所属表未带 columns,"
|
|
522
|
+
f"已从请求中省略,由平台原样保留", file=sys.stderr)
|
|
523
|
+
table_aps_json = json.dumps(table_aps, ensure_ascii=False)
|
|
437
524
|
body = {
|
|
438
525
|
"modelId": model_id,
|
|
439
526
|
"tableApsJson": table_aps_json,
|
|
@@ -90,8 +90,8 @@
|
|
|
90
90
|
| 状态 | 含义 | AI Agent 行动 |
|
|
91
91
|
|------|------|--------------|
|
|
92
92
|
| **PASS** | 断言通过,实际数据与预期一致 | 无需处理,继续后续流程 |
|
|
93
|
-
| **FAIL** | 断言失败,实际数据与预期不一致 |
|
|
94
|
-
| **WARN** |
|
|
93
|
+
| **FAIL** | 断言失败,实际数据与预期不一致 | 先核对当前状态,仅修复已确认差异,不重放整个写入 |
|
|
94
|
+
| **WARN** | 命中候选平台限制,是否生效仍需独立回读 | 确认差异后仅对未完成项选替代 API;状态未知不写入 |
|
|
95
95
|
| **ERROR** | API 回读调用失败(网络/鉴权问题等),不代表写入操作失败 | 重试验证,或检查网络/鉴权状态 |
|
|
96
96
|
| **SKIP** | 无法通过现有 API 验证的项 | 需人工在苍穹设计器中确认 |
|
|
97
97
|
|
|
@@ -117,23 +117,21 @@
|
|
|
117
117
|
|
|
118
118
|
### FAIL — 断言失败
|
|
119
119
|
|
|
120
|
-
1.
|
|
121
|
-
2.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
- **删除未生效**:确认 remove op 的 target 是否正确
|
|
125
|
-
3. 用正确的 API 参数重新执行操作,再次调用验证确认修复
|
|
120
|
+
1. 解析 `message` 和 `actual` / `expected` 差异;先独立回读当前状态及已有成功回执。
|
|
121
|
+
2. 只修复已确认的差异:字段/实体缺失先确认目标与创建状态;属性不符核对 schema 和参数;删除未生效核对 target。
|
|
122
|
+
3. 对尚未完成的差异选择正确 API,再次回读;不得重放整个 artifact / ops 数组,不得用 remove+add 替代属性修复。
|
|
123
|
+
4. 回读失败、部分成功或写后状态未知时停止后续写入,遵循 `reconcile_before_retry`;只重试读取,不重试写入。
|
|
126
124
|
|
|
127
125
|
### WARN — 已知缺陷
|
|
128
126
|
|
|
129
|
-
|
|
127
|
+
已知缺陷库记录候选平台限制。WARN 不能证明本次写入未发生;需核对实际部署、目标类型和独立回读,再决定是否使用替代方案:
|
|
130
128
|
|
|
131
129
|
| 缺陷场景 | 现象 | 替代方案 |
|
|
132
130
|
|----------|------|---------|
|
|
133
131
|
| modifyMeta 设置 MulBasedataField.tableName | modifyMeta 无法生效 | 改用 `setProperties` API 设置 tableName |
|
|
134
|
-
| modifyMeta modify mustInput | mustInput 可能被静默忽略 |
|
|
132
|
+
| modifyMeta modify mustInput | mustInput 可能被静默忽略 | 独立验证创建后,改用带回读的 `setProperties`;不要混合结构和属性事务 |
|
|
135
133
|
|
|
136
|
-
>
|
|
134
|
+
> WARN 是需要核对的候选限制,不是写入未发生的证明。先独立回读已完成和未完成项,再仅对已确认差异调用建议 API;状态未知时禁止重放写入或删除重建。
|
|
137
135
|
|
|
138
136
|
### ERROR — 回读失败
|
|
139
137
|
|
|
@@ -142,7 +140,7 @@
|
|
|
142
140
|
3. 重试验证命令(最多 2 次)
|
|
143
141
|
4. 仍然失败则提示用户检查环境
|
|
144
142
|
|
|
145
|
-
> **注意**:ERROR
|
|
143
|
+
> **注意**:ERROR 仅表示回读失败,前序写入状态仍未知;只重试验证读取,禁止重放写入。仍无法确认时停止依赖写入并交代未完成验证。
|
|
146
144
|
|
|
147
145
|
---
|
|
148
146
|
|
|
@@ -222,7 +222,8 @@ def do_add_menu(cfg, token, biz_app_id, name, parent_menu_id=None,
|
|
|
222
222
|
"linkUrl": link_url,
|
|
223
223
|
"openType": open_type,
|
|
224
224
|
"visible": visible,
|
|
225
|
-
|
|
225
|
+
# 旧平台缺省不初始化 LocaleString,菜单列表读取描述时可能空指针。
|
|
226
|
+
"description": name if description is None else description,
|
|
226
227
|
"caption": caption,
|
|
227
228
|
"parameter": parameter,
|
|
228
229
|
"parameterType": parameter_type,
|
|
@@ -261,6 +262,7 @@ def do_add_menu(cfg, token, biz_app_id, name, parent_menu_id=None,
|
|
|
261
262
|
else:
|
|
262
263
|
expected = {
|
|
263
264
|
"name": name,
|
|
265
|
+
"description": payload["description"],
|
|
264
266
|
"menuType": platform_menu_type,
|
|
265
267
|
"openType": open_type,
|
|
266
268
|
}
|