kcode-pi 0.1.0
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/README.md +358 -0
- package/dist/cli/kcode.d.ts +15 -0
- package/dist/cli/kcode.js +153 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +7 -0
- package/docs/KCODE_DISTRIBUTION.md +91 -0
- package/extensions/kingdee-harness.ts +180 -0
- package/extensions/kingdee-header.ts +122 -0
- package/extensions/kingdee-tools.ts +379 -0
- package/knowledge/.backup/v1.0.0/version.json +10 -0
- package/knowledge/cangqiong/product-notes.md +15 -0
- package/knowledge/common/business-flows.md +115 -0
- package/knowledge/common/config-guides.md +110 -0
- package/knowledge/common/error-patterns.md +170 -0
- package/knowledge/common/implementation.md +144 -0
- package/knowledge/cosmic/hard-constraints.md +38 -0
- package/knowledge/cosmic/ksql-datafix.md +34 -0
- package/knowledge/cosmic/platform-baseline.md +32 -0
- package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
- package/knowledge/cosmic/review-checklist.md +40 -0
- package/knowledge/cosmic/unittest.md +35 -0
- package/knowledge/enterprise/api-reference.md +186 -0
- package/knowledge/enterprise/code-patterns.md +217 -0
- package/knowledge/enterprise/plugin-lifecycle.md +188 -0
- package/knowledge/enterprise/tables.json +159 -0
- package/knowledge/flagship/api-reference.md +237 -0
- package/knowledge/flagship/code-patterns.md +246 -0
- package/knowledge/flagship/cosmic-platform-note.md +15 -0
- package/knowledge/flagship/plugin-lifecycle.md +248 -0
- package/knowledge/flagship/tables.json +159 -0
- package/knowledge/version.json +10 -0
- package/knowledge/xinghan/product-notes.md +15 -0
- package/package.json +71 -0
- package/prompts/kd-discuss.md +11 -0
- package/prompts/kd-execute.md +12 -0
- package/prompts/kd-plan.md +12 -0
- package/prompts/kd-ship.md +12 -0
- package/prompts/kd-spec.md +12 -0
- package/prompts/kd-verify.md +12 -0
- package/skills/kd-check/SKILL.md +26 -0
- package/skills/kd-cosmic-dev/SKILL.md +82 -0
- package/skills/kd-cosmic-review/SKILL.md +90 -0
- package/skills/kd-cosmic-unittest/SKILL.md +92 -0
- package/skills/kd-debug/SKILL.md +30 -0
- package/skills/kd-discuss/SKILL.md +24 -0
- package/skills/kd-execute/SKILL.md +22 -0
- package/skills/kd-gen/SKILL.md +34 -0
- package/skills/kd-ksql/SKILL.md +86 -0
- package/skills/kd-plan/SKILL.md +24 -0
- package/skills/kd-ship/SKILL.md +22 -0
- package/skills/kd-spec/SKILL.md +24 -0
- package/skills/kd-verify/SKILL.md +22 -0
- package/themes/kcode-dark.json +81 -0
- package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
- package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
- package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
- package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
- package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
- package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
- package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
- package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
- package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
- package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
- package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
- package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
- package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
- package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
- package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
- package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
- package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
- package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
- package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
- package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
- package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
- package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
- package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
- package/vendor/kingdee-skills/ok-ksql/scripts/ksql_lint.py +363 -0
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* View 生命周期与页面控制示例。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:表单插件、单据插件
|
|
5
|
+
* 优先封装:AbstractFormPluginExt、PageCache
|
|
6
|
+
* 原生兜底:PreOpenFormEventArgs、BeforeClosedEvent、Toolbar
|
|
7
|
+
* 相关 lint 规则:SCENE-006、SCENE-007、SCENE-008
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:
|
|
10
|
+
* 1. preOpenForm 阶段决定是否允许打开页面;
|
|
11
|
+
* 2. afterBindData 阶段按新增/编辑/审核状态控制可见性和锁定性;
|
|
12
|
+
* 3. 局部刷新、工具栏重放、页面关闭、pageCache 暂存;
|
|
13
|
+
* 4. 运行时修改控件标题。
|
|
14
|
+
*/
|
|
15
|
+
package kd.cd.common.snippets.form;
|
|
16
|
+
|
|
17
|
+
import kd.bos.bill.OperationStatus;
|
|
18
|
+
import kd.bos.dataentity.resource.ResManager;
|
|
19
|
+
import kd.bos.form.FormShowParameter;
|
|
20
|
+
import kd.bos.form.control.Toolbar;
|
|
21
|
+
import kd.bos.form.events.BeforeClosedEvent;
|
|
22
|
+
import kd.bos.form.events.PreOpenFormEventArgs;
|
|
23
|
+
import kd.cd.common.plugin.AbstractFormPluginExt;
|
|
24
|
+
|
|
25
|
+
import java.time.DayOfWeek;
|
|
26
|
+
import java.time.LocalDate;
|
|
27
|
+
import java.util.EventObject;
|
|
28
|
+
import java.util.HashMap;
|
|
29
|
+
import java.util.Map;
|
|
30
|
+
|
|
31
|
+
public class ViewControlOpsSample extends AbstractFormPluginExt {
|
|
32
|
+
private static final String FIELD_TOTAL_AMOUNT = "totalamount";
|
|
33
|
+
private static final String FIELD_AUDITOR = "auditor";
|
|
34
|
+
private static final String ENTRY_KEY = "entryentity";
|
|
35
|
+
private static final String ENTRY_DESC = "dealdesc";
|
|
36
|
+
private static final String MAIN_TOOLBAR = "tbmain";
|
|
37
|
+
private static final String PAGE_CACHE_SKIP_DIRTY_CHECK = "skip_dirty_check";
|
|
38
|
+
private static final String RES_APP_ID = "kd-cd-common-snippets";
|
|
39
|
+
|
|
40
|
+
@Override
|
|
41
|
+
public void preOpenForm(PreOpenFormEventArgs e) {
|
|
42
|
+
super.preOpenForm(e);
|
|
43
|
+
if (LocalDate.now().getDayOfWeek() == DayOfWeek.SUNDAY) {
|
|
44
|
+
e.setCancel(true);
|
|
45
|
+
e.setCancelMessage(ResManager.loadKDString("对不起,周日不能制单。", "ViewControlOpsSample_0", RES_APP_ID));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@Override
|
|
50
|
+
public void afterBindData(EventObject e) {
|
|
51
|
+
super.afterBindData(e);
|
|
52
|
+
FormShowParameter showParameter = getView().getFormShowParameter();
|
|
53
|
+
OperationStatus status = showParameter.getStatus();
|
|
54
|
+
if (OperationStatus.ADDNEW.equals(status)) {
|
|
55
|
+
getView().showTipNotification(ResManager.loadKDString("当前是新增页面。", "ViewControlOpsSample_1", RES_APP_ID));
|
|
56
|
+
} else if (OperationStatus.EDIT.equals(status)) {
|
|
57
|
+
getView().showTipNotification(ResManager.loadKDString("当前是编辑页面。", "ViewControlOpsSample_2", RES_APP_ID));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
lockAlwaysReadonlyFields();
|
|
61
|
+
toggleAuditFieldVisibility();
|
|
62
|
+
focusFirstEntryRow();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public void lockAlwaysReadonlyFields() {
|
|
66
|
+
getView().setEnable(false, FIELD_TOTAL_AMOUNT);
|
|
67
|
+
|
|
68
|
+
int rowCount = getModel().getEntryRowCount(ENTRY_KEY);
|
|
69
|
+
for (int row = 0; row < rowCount; row++) {
|
|
70
|
+
getView().setEnable(false, row, ENTRY_DESC);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public void toggleAuditFieldVisibility() {
|
|
75
|
+
String billStatus = String.valueOf(getModel().getValue("billstatus"));
|
|
76
|
+
getView().setVisible("C".equalsIgnoreCase(billStatus), FIELD_AUDITOR);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
public void focusFirstEntryRow() {
|
|
80
|
+
if (getModel().getEntryRowCount(ENTRY_KEY) > 0) {
|
|
81
|
+
getModel().setEntryCurrentRowIndex(ENTRY_KEY, 0);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public void replaySaveToolbar() {
|
|
86
|
+
Toolbar toolbar = getControl(MAIN_TOOLBAR);
|
|
87
|
+
if (toolbar != null) {
|
|
88
|
+
toolbar.itemClick("bar_save", "save");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public void refreshLocally(int rowIndex) {
|
|
93
|
+
getView().updateView(FIELD_TOTAL_AMOUNT);
|
|
94
|
+
if (rowIndex >= 0) {
|
|
95
|
+
getView().updateView(ENTRY_DESC, rowIndex);
|
|
96
|
+
}
|
|
97
|
+
getView().updateView(ENTRY_KEY);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public void refreshFromDatabase() {
|
|
101
|
+
getView().invokeOperation("refresh");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public void renameControlCaption(String controlKey, String caption) {
|
|
105
|
+
Map<String, Object> text = new HashMap<>(1);
|
|
106
|
+
text.put("zh_CN", caption);
|
|
107
|
+
|
|
108
|
+
Map<String, Object> payload = new HashMap<>(1);
|
|
109
|
+
payload.put("text", text);
|
|
110
|
+
getView().updateControlMetadata(controlKey, payload);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public void rememberInPageCache(String key, String value) {
|
|
114
|
+
getPageCache().put(key, value);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public String readFromPageCache(String key) {
|
|
118
|
+
return getPageCache().get(key);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@Override
|
|
122
|
+
public void beforeClosed(BeforeClosedEvent e) {
|
|
123
|
+
super.beforeClosed(e);
|
|
124
|
+
if ("true".equalsIgnoreCase(getPageCache().get(PAGE_CACHE_SKIP_DIRTY_CHECK))) {
|
|
125
|
+
e.setCheckDataChange(false);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public void closeCurrentPage() {
|
|
130
|
+
getView().close();
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 列表插件基础交互示例。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:列表插件
|
|
5
|
+
* 优先封装:ShowParameterUtils、FilterUtils
|
|
6
|
+
* 原生兜底:SetFilterEvent、PackageDataEvent、HyperLinkClickArgs
|
|
7
|
+
* 相关 lint 规则:STYLE-002、STYLE-013
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:
|
|
10
|
+
* 1. 列表界面加载时动态追加过滤条件;
|
|
11
|
+
* 2. 操作列按钮按行状态动态显隐;
|
|
12
|
+
* 3. 列数据格式化显示;
|
|
13
|
+
* 4. 列表操作后打开关联页面。
|
|
14
|
+
*/
|
|
15
|
+
package kd.cd.common.snippets.list;
|
|
16
|
+
|
|
17
|
+
import kd.bos.bill.BillShowParameter;
|
|
18
|
+
import kd.bos.bill.OperationStatus;
|
|
19
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
20
|
+
import kd.bos.dataentity.resource.ResManager;
|
|
21
|
+
import kd.bos.entity.datamodel.ListSelectedRow;
|
|
22
|
+
import kd.bos.entity.datamodel.ListSelectedRowCollection;
|
|
23
|
+
import kd.bos.entity.datamodel.events.PackageDataEvent;
|
|
24
|
+
import kd.bos.entity.list.column.ColumnDesc;
|
|
25
|
+
import kd.bos.form.IPageCache;
|
|
26
|
+
import kd.bos.form.ShowType;
|
|
27
|
+
import kd.bos.form.events.AfterDoOperationEventArgs;
|
|
28
|
+
import kd.bos.form.events.HyperLinkClickArgs;
|
|
29
|
+
import kd.bos.form.events.SetFilterEvent;
|
|
30
|
+
import kd.bos.form.operatecol.OperationColItem;
|
|
31
|
+
import kd.bos.list.BillList;
|
|
32
|
+
import kd.bos.list.ListShowParameter;
|
|
33
|
+
import kd.bos.list.column.ListOperationColumnDesc;
|
|
34
|
+
import kd.bos.orm.query.QCP;
|
|
35
|
+
import kd.bos.orm.query.QFilter;
|
|
36
|
+
import kd.cd.common.form.ShowParameterUtils;
|
|
37
|
+
import kd.cd.common.plugin.AbstractListPluginExt;
|
|
38
|
+
import kd.cd.common.util.DynamicObjectUtils;
|
|
39
|
+
import kd.cd.common.util.FilterUtils;
|
|
40
|
+
import kd.cd.core.util.CollectionUtils;
|
|
41
|
+
|
|
42
|
+
import java.util.List;
|
|
43
|
+
import java.util.Objects;
|
|
44
|
+
|
|
45
|
+
public class ListPluginBasicSample extends AbstractListPluginExt {
|
|
46
|
+
private static final String LOG_FORM_ID = "outapilog";
|
|
47
|
+
private static final String SHOW_LOG_OP = "showlog";
|
|
48
|
+
private static final String STATUS = "status";
|
|
49
|
+
private static final String ENABLE = "enable";
|
|
50
|
+
private static final String FUZZY_CACHE_KEY = "fuzzyfilterkey";
|
|
51
|
+
|
|
52
|
+
// --- setFilter: 列表加载时追加过滤条件 ---
|
|
53
|
+
@Override
|
|
54
|
+
public void setFilter(SetFilterEvent e) {
|
|
55
|
+
super.setFilter(e);
|
|
56
|
+
IPageCache cache = getView().getPageCache();
|
|
57
|
+
String fuzzyFilterStr = cache.get(FUZZY_CACHE_KEY);
|
|
58
|
+
if (fuzzyFilterStr != null) {
|
|
59
|
+
e.setCustomQFilters(FilterUtils.deSerializeQFilter(fuzzyFilterStr));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 特殊权限过滤:非管理员只能看未标记的数据
|
|
63
|
+
QFilter permFilter = new QFilter("require_super_auth", QCP.equals, false);
|
|
64
|
+
QFilter specialDataPermFilter = e.getSpecialDataPermQFilter();
|
|
65
|
+
if (specialDataPermFilter != null) {
|
|
66
|
+
specialDataPermFilter.and(permFilter);
|
|
67
|
+
} else {
|
|
68
|
+
e.setSpecialDataPermQFilter(permFilter);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// --- packageData: 操作列按钮按行状态动态显隐、列数据格式化 ---
|
|
73
|
+
@SuppressWarnings("unchecked")
|
|
74
|
+
@Override
|
|
75
|
+
public void packageData(PackageDataEvent e) {
|
|
76
|
+
Object source = e.getSource();
|
|
77
|
+
|
|
78
|
+
// 操作列按钮显隐控制
|
|
79
|
+
if (source instanceof ListOperationColumnDesc) {
|
|
80
|
+
DynamicObject rowData = e.getRowData();
|
|
81
|
+
boolean isAudited = "C".equals(DynamicObjectUtils.nullSafeGet(rowData, STATUS));
|
|
82
|
+
boolean isEnabled = "1".equals(DynamicObjectUtils.nullSafeGet(rowData, ENABLE));
|
|
83
|
+
|
|
84
|
+
List<OperationColItem> items = (List<OperationColItem>) e.getFormatValue();
|
|
85
|
+
for (OperationColItem item : items) {
|
|
86
|
+
if (SHOW_LOG_OP.equals(item.getOperationKey())) {
|
|
87
|
+
item.setVisible(isAudited && isEnabled);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// 列数据格式化
|
|
94
|
+
if (source instanceof ColumnDesc) {
|
|
95
|
+
String fieldKey = ((ColumnDesc) source).getFieldKey();
|
|
96
|
+
if ("port".equals(fieldKey)) {
|
|
97
|
+
Integer port = (Integer) e.getRowData().get("port");
|
|
98
|
+
e.setFormatValue(port == null || port == 0 ? "-" : String.valueOf(port));
|
|
99
|
+
} else if ("connectstate".equals(fieldKey)) {
|
|
100
|
+
DynamicObject rowData = e.getRowData();
|
|
101
|
+
if (!"C".equals(DynamicObjectUtils.nullSafeGet(rowData, STATUS))) {
|
|
102
|
+
e.setFormatValue("");
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// --- afterDoOperation: 操作完成后打开关联页面 ---
|
|
109
|
+
@Override
|
|
110
|
+
public void afterDoOperation(AfterDoOperationEventArgs args) {
|
|
111
|
+
super.afterDoOperation(args);
|
|
112
|
+
String opKey = getOpKey(args);
|
|
113
|
+
if (!SHOW_LOG_OP.equals(opKey)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
BillList billList = getBillList();
|
|
118
|
+
ListSelectedRow rowInfo = billList.getCurrentSelectedRowInfo();
|
|
119
|
+
if (rowInfo == null) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
String number = rowInfo.getNumber();
|
|
124
|
+
String name = rowInfo.getName();
|
|
125
|
+
|
|
126
|
+
ListShowParameter lsp = ShowParameterUtils.getList(LOG_FORM_ID, ShowType.MainNewTabPage, true);
|
|
127
|
+
lsp.getListFilterParameter().getQFilters().add(new QFilter("opname", QCP.equals, number));
|
|
128
|
+
if (name != null) {
|
|
129
|
+
lsp.setCaption(String.format(
|
|
130
|
+
ResManager.loadKDString("%s-日志", "ListPluginBasicSample_0", "kd-cd-common-snippets"),
|
|
131
|
+
name
|
|
132
|
+
));
|
|
133
|
+
}
|
|
134
|
+
billList.clearSelection();
|
|
135
|
+
getView().showForm(lsp);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// --- billListHyperLinkClick: 拦截默认打开逻辑,改成自定义弹窗 ---
|
|
139
|
+
@Override
|
|
140
|
+
public void billListHyperLinkClick(HyperLinkClickArgs args) {
|
|
141
|
+
if (!Objects.equals("billno", args.getHyperLinkClickEvent().getFieldName())) {
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
args.setCancel(true);
|
|
145
|
+
|
|
146
|
+
ListSelectedRowCollection rows = getCurrentListAllRowCollection();
|
|
147
|
+
int rowIndex = args.getRowIndex();
|
|
148
|
+
if (rows == null || rowIndex < 0 || rowIndex >= rows.size()) {
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
ListSelectedRow rowInfo = rows.get(rowIndex);
|
|
153
|
+
BillShowParameter showParameter = ShowParameterUtils.getBill(
|
|
154
|
+
rowInfo.getFormID(),
|
|
155
|
+
rowInfo.getPrimaryKeyValue(),
|
|
156
|
+
OperationStatus.EDIT,
|
|
157
|
+
ShowType.Modal
|
|
158
|
+
);
|
|
159
|
+
getView().showForm(showParameter);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
public ListSelectedRowCollection currentSelections() {
|
|
163
|
+
ListSelectedRowCollection selectedRows = getSelectedRows();
|
|
164
|
+
if (CollectionUtils.isNotEmpty(selectedRows)) {
|
|
165
|
+
return selectedRows;
|
|
166
|
+
}
|
|
167
|
+
BillList billList = getBillList();
|
|
168
|
+
return billList == null ? selectedRows : billList.getSelectedRows();
|
|
169
|
+
}
|
|
170
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 列表 preOpenForm 按父页面字段动态过滤示例。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:列表插件
|
|
5
|
+
* 优先封装:FormUtils、AbstractListPluginExt
|
|
6
|
+
* 原生兜底:PreOpenFormEventArgs、ListShowParameter、QFilter
|
|
7
|
+
* 相关 lint 规则:当前无专门规则,可参考 SCENE-003、STYLE-015
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:从父单据打开 F7/列表时,
|
|
10
|
+
* 根据父页面表头或当前分录字段给列表追加过滤条件。
|
|
11
|
+
*/
|
|
12
|
+
package kd.cd.common.snippets.list;
|
|
13
|
+
|
|
14
|
+
import kd.bos.entity.datamodel.IDataModel;
|
|
15
|
+
import kd.bos.form.IFormView;
|
|
16
|
+
import kd.bos.form.events.PreOpenFormEventArgs;
|
|
17
|
+
import kd.bos.list.ListShowParameter;
|
|
18
|
+
import kd.bos.orm.query.QCP;
|
|
19
|
+
import kd.bos.orm.query.QFilter;
|
|
20
|
+
import kd.cd.common.form.FormUtils;
|
|
21
|
+
import kd.cd.common.plugin.AbstractListPluginExt;
|
|
22
|
+
import kd.cd.core.util.CharSequenceUtils;
|
|
23
|
+
|
|
24
|
+
import java.util.List;
|
|
25
|
+
|
|
26
|
+
public class ListPreOpenFilterSample extends AbstractListPluginExt {
|
|
27
|
+
private static final String PARENT_FORM_ID = "cas_claimbill";
|
|
28
|
+
private static final String PARENT_ENTRY_KEY = "entryentity";
|
|
29
|
+
private static final String FIELD_CORE_BILL_TYPE = "e_corebilltype";
|
|
30
|
+
private static final String FIELD_PAYMENT_TYPE = "paymenttype";
|
|
31
|
+
private static final String FIELD_PAYER_NO = "recbasepayer.number";
|
|
32
|
+
|
|
33
|
+
// --- 从父页面打开列表前,动态追加过滤 ---
|
|
34
|
+
@Override
|
|
35
|
+
public void preOpenForm(PreOpenFormEventArgs e) {
|
|
36
|
+
super.preOpenForm(e);
|
|
37
|
+
ListShowParameter showParameter = (ListShowParameter) e.getFormShowParameter();
|
|
38
|
+
if (!PARENT_FORM_ID.equals(showParameter.getParentFormId())) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// 获取noPlugin的parentView,防止跨节点报错
|
|
42
|
+
IFormView parentView = FormUtils.getViewByPageId(showParameter.getParentPageId(), true);
|
|
43
|
+
if (parentView == null) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
IDataModel parentModel = parentView.getModel();
|
|
48
|
+
int rowIndex = parentModel.getEntryCurrentRowIndex(PARENT_ENTRY_KEY);
|
|
49
|
+
if (rowIndex < 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
String coreBillType = (String) parentModel.getValue(FIELD_CORE_BILL_TYPE, rowIndex);
|
|
54
|
+
String paymentType = String.valueOf(parentModel.getValue(FIELD_PAYMENT_TYPE));
|
|
55
|
+
if (!"ar_finarbill".equals(coreBillType) || !"bd_customer".equals(paymentType)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
String payerNumber = parentModel.getDataEntity().getString(FIELD_PAYER_NO);
|
|
60
|
+
if (CharSequenceUtils.isBlank(payerNumber)) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
List<QFilter> qFilters = showParameter.getListFilterParameter().getQFilters();
|
|
65
|
+
qFilters.add(new QFilter("asstact.number", QCP.equals, payerNumber));
|
|
66
|
+
qFilters.add(new QFilter("billstatus", QCP.equals, "C"));
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 消息通知示例。
|
|
3
|
+
* <p>
|
|
4
|
+
* 适用插件:操作插件、服务层、后台任务
|
|
5
|
+
* 优先封装:当前仓库无专门消息封装,保持最小原生写法
|
|
6
|
+
* 原生兜底:MessageCenterServiceHelper、MessageInfo
|
|
7
|
+
* 相关 lint 规则:当前无专门规则,可参考 STYLE-013、STYLE-015
|
|
8
|
+
* <p>
|
|
9
|
+
* 使用场景:
|
|
10
|
+
* 1. 系统内消息通知;
|
|
11
|
+
* 2. 短信通知;
|
|
12
|
+
* 3. 云之家机器人通知;
|
|
13
|
+
* 4. 邮件通知。
|
|
14
|
+
*/
|
|
15
|
+
package kd.cd.common.snippets.message;
|
|
16
|
+
|
|
17
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
18
|
+
import kd.bos.dataentity.entity.LocaleString;
|
|
19
|
+
import kd.bos.dataentity.resource.ResManager;
|
|
20
|
+
import kd.bos.logging.Log;
|
|
21
|
+
import kd.bos.logging.LogFactory;
|
|
22
|
+
import kd.bos.servicehelper.workflow.MessageCenterServiceHelper;
|
|
23
|
+
import kd.bos.workflow.engine.msg.info.MessageInfo;
|
|
24
|
+
import kd.cd.core.util.CharSequenceUtils;
|
|
25
|
+
import kd.cd.core.util.CollectionUtils;
|
|
26
|
+
|
|
27
|
+
import java.util.Collections;
|
|
28
|
+
import java.util.List;
|
|
29
|
+
import java.util.Objects;
|
|
30
|
+
import java.util.stream.Collectors;
|
|
31
|
+
|
|
32
|
+
public class MessageNotifySample {
|
|
33
|
+
private static final Log log = LogFactory.getLog(MessageNotifySample.class);
|
|
34
|
+
|
|
35
|
+
// --- 系统内消息通知 ---
|
|
36
|
+
public static boolean sendSystemMessage(List<Long> userIds, String title, String content) {
|
|
37
|
+
if (CollectionUtils.isEmpty(userIds)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
MessageInfo messageInfo = new MessageInfo();
|
|
43
|
+
LocaleString titleLocale = new LocaleString();
|
|
44
|
+
titleLocale.setLocaleValue_zh_CN(title);
|
|
45
|
+
messageInfo.setMessageTitle(titleLocale);
|
|
46
|
+
|
|
47
|
+
LocaleString contentLocale = new LocaleString();
|
|
48
|
+
contentLocale.setLocaleValue_zh_CN(content);
|
|
49
|
+
messageInfo.setMessageContent(contentLocale);
|
|
50
|
+
|
|
51
|
+
messageInfo.setUserIds(userIds);
|
|
52
|
+
messageInfo.setType(MessageInfo.TYPE_ALARM);
|
|
53
|
+
messageInfo.setNotifyType("mcenter,yunzhijia");
|
|
54
|
+
|
|
55
|
+
MessageCenterServiceHelper.batchSendMessages(Collections.singletonList(messageInfo));
|
|
56
|
+
return true;
|
|
57
|
+
} catch (Exception e) {
|
|
58
|
+
log.warn("系统内消息发送失败", e);
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// --- 构建预警消息内容 ---
|
|
64
|
+
public static String buildAlarmMessage(String apiNum, String apiName, int successCount, int failCount, String failRate) {
|
|
65
|
+
return String.format(
|
|
66
|
+
ResManager.loadKDString(
|
|
67
|
+
"接口异常预警%n接口编号:%s%n接口名称:%s%n成功次数:%d%n失败次数:%d%n失败率:%s%%",
|
|
68
|
+
"MessageNotifySample_0",
|
|
69
|
+
"kd-cd-common-snippets"
|
|
70
|
+
),
|
|
71
|
+
apiNum, apiName, successCount, failCount, failRate
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// --- 从分录获取通知用户ID列表 ---
|
|
76
|
+
public static List<Long> getUserIdsFromEntry(DynamicObject bill, String entryKey, String userFieldKey) {
|
|
77
|
+
return bill.getDynamicObjectCollection(entryKey).stream()
|
|
78
|
+
.map(row -> (DynamicObject) row.get(userFieldKey))
|
|
79
|
+
.filter(Objects::nonNull)
|
|
80
|
+
.map(obj -> obj.getLong("id"))
|
|
81
|
+
.distinct()
|
|
82
|
+
.collect(Collectors.toList());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --- 从分录获取手机号列表 ---
|
|
86
|
+
public static List<String> getPhonesFromEntry(DynamicObject bill, String entryKey, String userFieldKey) {
|
|
87
|
+
return bill.getDynamicObjectCollection(entryKey).stream()
|
|
88
|
+
.map(row -> (DynamicObject) row.get(userFieldKey))
|
|
89
|
+
.filter(Objects::nonNull)
|
|
90
|
+
.map(obj -> obj.getString("phone"))
|
|
91
|
+
.filter(CharSequenceUtils::isNotBlank)
|
|
92
|
+
.distinct()
|
|
93
|
+
.collect(Collectors.toList());
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
package kd.cd.common.snippets;
|
|
2
|
+
|
|
3
|
+
import kd.bos.dataentity.OperateOption;
|
|
4
|
+
import kd.bos.dataentity.entity.DynamicObject;
|
|
5
|
+
import kd.bos.dlock.DLock;
|
|
6
|
+
import kd.bos.entity.operate.result.OperationResult;
|
|
7
|
+
import kd.bos.logging.Log;
|
|
8
|
+
import kd.bos.logging.LogFactory;
|
|
9
|
+
import kd.bos.mq.MessageAcker;
|
|
10
|
+
import kd.bos.mq.MessageConsumer;
|
|
11
|
+
import kd.bos.servicehelper.BusinessDataServiceHelper;
|
|
12
|
+
import kd.bos.servicehelper.operation.OperationServiceHelper;
|
|
13
|
+
import kd.bos.servicehelper.operation.SaveServiceHelper;
|
|
14
|
+
import kd.cd.common.operate.OpUtils;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* MQ 消息消费者示例 —— 苍穹消息队列消费端全场景。
|
|
18
|
+
* <p>
|
|
19
|
+
* 适用插件:MQ 消费者(实现 MessageConsumer 接口)
|
|
20
|
+
* 优先封装:(暂无 commons 封装)
|
|
21
|
+
* 原生兜底:MessageConsumer、MessageAcker、DLock、OperationServiceHelper
|
|
22
|
+
* 相关 lint 规则:(暂无)
|
|
23
|
+
* <p>
|
|
24
|
+
* 使用场景:
|
|
25
|
+
* 1. 简单消费模式:收到消息 → 执行操作 → ack/discard;
|
|
26
|
+
* 2. 分布式锁防重复消费:DLock + tryLock 保证同一消息不会并发处理;
|
|
27
|
+
* 3. MessageAcker 三种应答:ack(确认)、deny(拒绝回队列)、discard(废弃不重试)。
|
|
28
|
+
* <p>
|
|
29
|
+
* <b>配套配置:需在 resources 下创建 MQ XML 配置文件,并在启动类中声明。</b>
|
|
30
|
+
* XML 结构示例见本文件末尾注释。
|
|
31
|
+
*/
|
|
32
|
+
public class SampleMQConsumer implements MessageConsumer {
|
|
33
|
+
private static final Log log = LogFactory.getLog(SampleMQConsumer.class);
|
|
34
|
+
|
|
35
|
+
// ===================================================================
|
|
36
|
+
// 一、标准消费模式(执行操作 + 结果应答)
|
|
37
|
+
// ===================================================================
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* MQ 消费入口。
|
|
41
|
+
*
|
|
42
|
+
* @param message 消息体(类型取决于发送方,常见 String / String[] / JSON)
|
|
43
|
+
* @param messageId 消息唯一标识
|
|
44
|
+
* @param resend 是否为重发消息
|
|
45
|
+
* @param acker 消息应答器
|
|
46
|
+
*/
|
|
47
|
+
@Override
|
|
48
|
+
public void onMessage(Object message, String messageId, boolean resend, MessageAcker acker) {
|
|
49
|
+
log.info("MQ 消费开始:messageId={}, message={}", messageId, message);
|
|
50
|
+
if (message == null) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// ---------- 场景 A:消息体为 String[](常见于操作类消息) ----------
|
|
55
|
+
// 发送方约定:message[0] = 单据标识, message[1] = 主键
|
|
56
|
+
// consumeOperationMessage((String[]) message, messageId, acker);
|
|
57
|
+
|
|
58
|
+
// ---------- 场景 B:消息体为单据 PK + 分布式锁防重 ----------
|
|
59
|
+
consumeWithDLock(message, messageId, acker);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ===================================================================
|
|
63
|
+
// 场景 A:操作类消息(如异步入账、复核、反复核)
|
|
64
|
+
// ===================================================================
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 收到消息后调用平台操作。
|
|
68
|
+
* 典型用途:异步入账、异步审批、凭证复核等。
|
|
69
|
+
*/
|
|
70
|
+
private void consumeOperationMessage(String[] message, String messageId, MessageAcker acker) {
|
|
71
|
+
String entityId = message[0]; // 单据标识
|
|
72
|
+
String pkValue = message[1]; // 单据主键
|
|
73
|
+
log.info("操作类消息:entityId={}, pk={}", entityId, pkValue);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
// 调用平台操作(如 "audit"、"submit"、"kdcd_dorule" 等)
|
|
77
|
+
OperationResult result = OperationServiceHelper.executeOperate(
|
|
78
|
+
"audit", entityId, new Long[]{Long.valueOf(pkValue)}, OperateOption.create());
|
|
79
|
+
|
|
80
|
+
if (result.isSuccess()) {
|
|
81
|
+
log.info("操作执行成功,确认消息");
|
|
82
|
+
acker.ack(messageId); // 确认:消费完成
|
|
83
|
+
} else {
|
|
84
|
+
String errMsg = OpUtils.getCompleteFailMsg(result);
|
|
85
|
+
log.info("操作执行失败:{}", errMsg);
|
|
86
|
+
// 更新单据的错误信息字段
|
|
87
|
+
DynamicObject entity = BusinessDataServiceHelper.loadSingle(pkValue, "kdcd_errmsg", entityId);
|
|
88
|
+
entity.set("kdcd_errmsg", errMsg);
|
|
89
|
+
SaveServiceHelper.update(entity);
|
|
90
|
+
acker.discard(messageId); // 废弃:不再重试
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
} catch (Exception e) {
|
|
94
|
+
log.error("消费异常:{}", e.getMessage(), e);
|
|
95
|
+
acker.discard(messageId); // 异常也废弃,避免无限重试
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ===================================================================
|
|
100
|
+
// 场景 B:分布式锁防重复消费(适用于外部接口调用等幂等场景)
|
|
101
|
+
// ===================================================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* 使用 DLock 分布式锁防止同一消息被多个消费者实例并发处理。
|
|
105
|
+
* 典型用途:调用外部接口、同步第三方数据。
|
|
106
|
+
*/
|
|
107
|
+
private void consumeWithDLock(Object message, String messageId, MessageAcker acker) {
|
|
108
|
+
// 以消息内容(通常是单据 PK)作为锁的 key
|
|
109
|
+
try (DLock dLock = DLock.create(messageId)) {
|
|
110
|
+
if (dLock.tryLock()) {
|
|
111
|
+
// ---------- 加锁成功,处理消息 ----------
|
|
112
|
+
processMessageBody(message, messageId, acker);
|
|
113
|
+
} else {
|
|
114
|
+
// ---------- 加锁失败,返回队列等待重新消费 ----------
|
|
115
|
+
log.info("获取分布式锁失败,消息回队列:{}", messageId);
|
|
116
|
+
acker.deny(messageId);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 消息处理核心逻辑。
|
|
123
|
+
*/
|
|
124
|
+
private void processMessageBody(Object message, String messageId, MessageAcker acker) {
|
|
125
|
+
String formId = "kdcd_api_message_pool";
|
|
126
|
+
DynamicObject bill = BusinessDataServiceHelper.loadSingle(message, "kdcd_status", formId);
|
|
127
|
+
|
|
128
|
+
// 幂等判断:已处理成功的跳过
|
|
129
|
+
String status = bill.getString("kdcd_status");
|
|
130
|
+
if ("1".equals(status)) {
|
|
131
|
+
log.info("消息已成功处理过,跳过:{}", messageId);
|
|
132
|
+
acker.discard(messageId);
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
// ====== 执行业务逻辑(如调用外部 API) ======
|
|
138
|
+
boolean success = callExternalApi(bill);
|
|
139
|
+
|
|
140
|
+
if (success) {
|
|
141
|
+
bill.set("kdcd_status", "1"); // 成功
|
|
142
|
+
SaveServiceHelper.update(bill);
|
|
143
|
+
acker.ack(messageId); // 确认消费完成
|
|
144
|
+
} else {
|
|
145
|
+
bill.set("kdcd_status", "-1"); // 失败
|
|
146
|
+
SaveServiceHelper.update(bill);
|
|
147
|
+
acker.deny(messageId); // 拒绝:返回队列稍后重试
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
} catch (Exception e) {
|
|
151
|
+
log.error("消息处理异常:{}", messageId, e);
|
|
152
|
+
bill.set("kdcd_status", "-1");
|
|
153
|
+
SaveServiceHelper.update(bill);
|
|
154
|
+
acker.discard(messageId); // 异常废弃
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
private boolean callExternalApi(DynamicObject bill) {
|
|
159
|
+
// 模拟外部接口调用
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// ===================================================================
|
|
164
|
+
// MessageAcker 应答方式速查
|
|
165
|
+
// ===================================================================
|
|
166
|
+
//
|
|
167
|
+
// acker.ack(messageId) → 确认:消息已成功处理,从队列移除
|
|
168
|
+
// acker.deny(messageId) → 拒绝:消息返回队列,稍后重新投递消费
|
|
169
|
+
// acker.discard(messageId) → 废弃:消息从队列移除,不再重试
|
|
170
|
+
//
|
|
171
|
+
// 选择原则:
|
|
172
|
+
// - 处理成功 → ack
|
|
173
|
+
// - 临时故障(如外部接口超时)→ deny(会重试)
|
|
174
|
+
// - 业务逻辑失败或异常(不可能通过重试修复)→ discard
|
|
175
|
+
|
|
176
|
+
// ===================================================================
|
|
177
|
+
// MQ XML 配置示例(放在 resources 根目录下)
|
|
178
|
+
// ===================================================================
|
|
179
|
+
//
|
|
180
|
+
// 文件名如:kdcd_sample_mq.xml
|
|
181
|
+
//
|
|
182
|
+
// <root>
|
|
183
|
+
// <!--所属云:如 fi=财务云、tmc=资金云、sys=系统-->
|
|
184
|
+
// <region name="fi">
|
|
185
|
+
// <!--name:队列名称(全局唯一) appid:所属应用-->
|
|
186
|
+
// <queue name="kdcd_sample_queue" appid="gl">
|
|
187
|
+
// <!--class:消费者实现类全路径 concurrency:消费者线程数-->
|
|
188
|
+
// <consumer class="kd.cd.xxx.SampleMQConsumer" concurrency="5"/>
|
|
189
|
+
// </queue>
|
|
190
|
+
// </region>
|
|
191
|
+
// </root>
|
|
192
|
+
//
|
|
193
|
+
// 启动类配置:
|
|
194
|
+
// System.setProperty("mqConfigFiles.config", "kdcd_sample_mq.xml");
|
|
195
|
+
//
|
|
196
|
+
// 发送消息:
|
|
197
|
+
// MQFactory.get().send("kdcd_sample_queue", messageObj);
|
|
198
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<root>
|
|
2
|
+
<!--所属云:fi=财务云、tmc=资金云、sys=系统、scm=供应链云 等-->
|
|
3
|
+
<region name="fi">
|
|
4
|
+
<!--name:队列名称(全局唯一) appid:所属应用(如 gl=总账、bei=银企互联)-->
|
|
5
|
+
<queue name="kdcd_sample_queue" appid="gl">
|
|
6
|
+
<!--class:消费者实现类全路径 concurrency:消费者线程数(一般 3~10)-->
|
|
7
|
+
<consumer class="kd.cd.xxx.biz.mq.SampleMQConsumer" concurrency="5"/>
|
|
8
|
+
</queue>
|
|
9
|
+
|
|
10
|
+
<!--同一 region 下可配置多个队列-->
|
|
11
|
+
<queue name="kdcd_another_queue" appid="gl">
|
|
12
|
+
<consumer class="kd.cd.xxx.biz.mq.AnotherMQConsumer" concurrency="3"/>
|
|
13
|
+
</queue>
|
|
14
|
+
</region>
|
|
15
|
+
</root>
|