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,320 @@
|
|
|
1
|
+
# UI 交互与性能陷阱检查表
|
|
2
|
+
|
|
3
|
+
## 🟠 P1 级问题
|
|
4
|
+
|
|
5
|
+
### 1. 循环内刷新视图
|
|
6
|
+
**检查点**:
|
|
7
|
+
- 循环内是否调用了 `updateView()`?
|
|
8
|
+
- 是否频繁触发界面刷新?
|
|
9
|
+
|
|
10
|
+
**风险**: 界面卡顿,CPU 飙升
|
|
11
|
+
|
|
12
|
+
**修正方案**:
|
|
13
|
+
```java
|
|
14
|
+
// ❌ 错误写法
|
|
15
|
+
for (int i = 0; i < 100; i++) {
|
|
16
|
+
this.getModel().setValue("field" + i, value);
|
|
17
|
+
this.getView().updateView(); // 每次循环都刷新!
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// ✅ 正确写法 - 使用 beginInit/endInit 包裹
|
|
21
|
+
this.getView().beginInit();
|
|
22
|
+
try {
|
|
23
|
+
for (int i = 0; i < 100; i++) {
|
|
24
|
+
this.getModel().setValue("field" + i, value);
|
|
25
|
+
}
|
|
26
|
+
} finally {
|
|
27
|
+
this.getView().endInit();
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
### 2. 错误的行号获取
|
|
34
|
+
**检查点**:
|
|
35
|
+
- 是否直接使用 `e.getInsertRow()` 获取行号?
|
|
36
|
+
|
|
37
|
+
**风险**: 可能获取到错误的行号
|
|
38
|
+
|
|
39
|
+
**修正方案**:
|
|
40
|
+
```java
|
|
41
|
+
// ❌ 错误写法
|
|
42
|
+
int row = e.getInsertRow();
|
|
43
|
+
|
|
44
|
+
// ✅ 正确写法
|
|
45
|
+
int row = e.getRowDataEntities()[0].getRowIndex();
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
### 3. 循环内获取 FieldIndex
|
|
51
|
+
**检查点**:
|
|
52
|
+
- 循环内是否重复调用 `getFieldIndex()`?
|
|
53
|
+
|
|
54
|
+
**修正方案**:
|
|
55
|
+
```java
|
|
56
|
+
// ❌ 错误写法
|
|
57
|
+
for (DynamicObject row : rows) {
|
|
58
|
+
int idx = this.getModel().getFieldIndex("amount");
|
|
59
|
+
this.getModel().setValue(idx, row.getBigDecimal("amount"));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ✅ 正确写法
|
|
63
|
+
int idx = this.getModel().getFieldIndex("amount"); // 循环外缓存
|
|
64
|
+
for (DynamicObject row : rows) {
|
|
65
|
+
this.getModel().setValue(idx, row.getBigDecimal("amount"));
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 4. 无限累积集合
|
|
72
|
+
**检查点**:
|
|
73
|
+
- 静态 List/Map 是否只增不减?
|
|
74
|
+
|
|
75
|
+
**风险**: OOM 内存溢出
|
|
76
|
+
|
|
77
|
+
**修正方案**:
|
|
78
|
+
```java
|
|
79
|
+
// ✅ 使用有界缓存
|
|
80
|
+
private static final int MAX_SIZE = 10000;
|
|
81
|
+
private static LinkedHashMap<Long, Object> cache = new LinkedHashMap<>(16, 0.75f, true) {
|
|
82
|
+
@Override
|
|
83
|
+
protected boolean removeEldestEntry(Map.Entry<Long, Object> eldest) {
|
|
84
|
+
return size() > MAX_SIZE;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## 🟡 P2 级问题
|
|
92
|
+
|
|
93
|
+
### 5. 表格数据量过大
|
|
94
|
+
**检查点**:
|
|
95
|
+
- 表格是否加载了超过 1000 行?
|
|
96
|
+
|
|
97
|
+
**修正方案**:
|
|
98
|
+
```java
|
|
99
|
+
// 使用分页查询
|
|
100
|
+
int pageSize = 500;
|
|
101
|
+
DataSet ds = QueryServiceHelper.queryDataSet(...)
|
|
102
|
+
.limit(pageSize);
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### 6. 过度使用 invokeLater
|
|
108
|
+
**检查点**:
|
|
109
|
+
- 是否在非 UI 线程频繁调用 `invokeLater`?
|
|
110
|
+
|
|
111
|
+
**修正方案**:
|
|
112
|
+
```java
|
|
113
|
+
// 合并多个 invokeLater 调用
|
|
114
|
+
this.getView().invokeLater(() -> {
|
|
115
|
+
// 批量处理 UI 更新
|
|
116
|
+
updateAllFields();
|
|
117
|
+
});
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
### 7. 大数据包循环 setValue
|
|
123
|
+
**检查点**:
|
|
124
|
+
- 分录行数 > 100 条时是否在循环中使用 `model.setValue`?
|
|
125
|
+
- 应改用 `property.setValueFast` 提升性能
|
|
126
|
+
|
|
127
|
+
**风险**: 大批量 setValue 触发大量 propertyChanged 事件,界面严重卡顿
|
|
128
|
+
|
|
129
|
+
**修正方案**:
|
|
130
|
+
```java
|
|
131
|
+
// ❌ 错误写法 - 大数据包循环 setValue
|
|
132
|
+
for (int i = 0; i < 500; i++) {
|
|
133
|
+
getModel().setValue("field1", value1, i);
|
|
134
|
+
getModel().setValue("field2", value2, i);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ✅ 正确写法 - 使用 setValueFast
|
|
138
|
+
DynamicObjectCollection entries = getModel().getEntryEntity("entryEntity");
|
|
139
|
+
for (DynamicObject entry : entries) {
|
|
140
|
+
for (DynamicProperty property : treeEntryProperties) {
|
|
141
|
+
property.setValueFast(newObj, objProps.get(property.getName()).getValueFast(srcObj));
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
getView().updateView("entryEntity"); // 循环外统一刷新
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
### 8. setValue 后未 updateView
|
|
150
|
+
**检查点**:
|
|
151
|
+
- `getModel().setValue()` 之后是否调用了 `getView().updateView()`?
|
|
152
|
+
- 分录操作后是否刷新了界面?
|
|
153
|
+
|
|
154
|
+
**风险**: 数据已修改但界面不刷新,用户看到旧数据
|
|
155
|
+
|
|
156
|
+
**修正方案**:
|
|
157
|
+
```java
|
|
158
|
+
// ❌ 错误写法 - 忘记刷新
|
|
159
|
+
getModel().setValue("fieldKey", value);
|
|
160
|
+
|
|
161
|
+
// ✅ 正确写法
|
|
162
|
+
getModel().setValue("fieldKey", value);
|
|
163
|
+
getView().updateView("fieldKey");
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### 9. beginInit/endInit 不成对
|
|
169
|
+
**检查点**:
|
|
170
|
+
- `beginInit()` 和 `endInit()` 是否成对出现?
|
|
171
|
+
- 是否在 finally 块中调用 `endInit()` 确保异常时也能结束?
|
|
172
|
+
|
|
173
|
+
**风险**: 遗漏 endInit 导致后续所有 setValue 不触发 propertyChanged,表单行为异常
|
|
174
|
+
|
|
175
|
+
**修正方案**:
|
|
176
|
+
```java
|
|
177
|
+
// ❌ 错误写法 - 不成对
|
|
178
|
+
getModel().beginInit();
|
|
179
|
+
getModel().setValue("field1", val1);
|
|
180
|
+
getModel().setValue("field2", val2);
|
|
181
|
+
// 忘记 endInit()
|
|
182
|
+
|
|
183
|
+
// ✅ 正确写法 - try-finally 确保成对
|
|
184
|
+
getModel().beginInit();
|
|
185
|
+
try {
|
|
186
|
+
getModel().setValue("field1", val1);
|
|
187
|
+
getModel().setValue("field2", val2);
|
|
188
|
+
getModel().setValue("field3", val3);
|
|
189
|
+
} finally {
|
|
190
|
+
getModel().endInit();
|
|
191
|
+
}
|
|
192
|
+
getView().updateView();
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### 10. setDataChanged 调用时机错误
|
|
198
|
+
**检查点**:
|
|
199
|
+
- `setDataChanged(false)` 是否放在所有 `setValue` 之后?
|
|
200
|
+
- 是否在 `afterBindData` 末尾调用 `setDataChanged(false)`?
|
|
201
|
+
|
|
202
|
+
**风险**: 提前调用后续 setValue 又会将 dataChanged 设为 true,导致关闭页面时误提示"数据已修改"
|
|
203
|
+
|
|
204
|
+
**修正方案**:
|
|
205
|
+
```java
|
|
206
|
+
// ❌ 错误写法 - setDataChanged 没放最后
|
|
207
|
+
getModel().setDataChanged(false);
|
|
208
|
+
getModel().setValue("fieldA", value); // 这行让 dataChanged 重新变 true
|
|
209
|
+
|
|
210
|
+
// ✅ 正确写法 - 所有 setValue 完成后再调
|
|
211
|
+
getModel().setValue("fieldA", value);
|
|
212
|
+
getModel().setDataChanged(false);
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
### 11. showConfirm 无回调监听器
|
|
218
|
+
**检查点**:
|
|
219
|
+
- `showConfirm` 是否提供了 `ConfirmCallBackListener`?
|
|
220
|
+
- 回调方法 `confirmCallBack` 是否正确处理了用户选择?
|
|
221
|
+
|
|
222
|
+
**风险**: 确认框无法接收用户选择结果
|
|
223
|
+
|
|
224
|
+
**修正方案**:
|
|
225
|
+
```java
|
|
226
|
+
// ❌ 错误写法 - 无回调
|
|
227
|
+
getView().showConfirm(message, MessageBoxOptions.OKCancel, null);
|
|
228
|
+
|
|
229
|
+
// ✅ 正确写法
|
|
230
|
+
getView().showConfirm(message, MessageBoxOptions.OKCancel,
|
|
231
|
+
new ConfirmCallBackListener("callbackId", this));
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
### 12. close 和 returnDataToParent 顺序错误
|
|
237
|
+
**检查点**:
|
|
238
|
+
- 是否先调用 `close()` 再调用 `returnDataToParent()`?(应先返回数据再关闭)
|
|
239
|
+
|
|
240
|
+
**风险**: 先 close 后 returnDataToParent 永远不会执行
|
|
241
|
+
|
|
242
|
+
**修正方案**:
|
|
243
|
+
```java
|
|
244
|
+
// ❌ 错误写法
|
|
245
|
+
getView().close();
|
|
246
|
+
getView().returnDataToParent(data); // 永远不会执行
|
|
247
|
+
|
|
248
|
+
// ✅ 正确写法
|
|
249
|
+
getView().returnDataToParent(data);
|
|
250
|
+
getView().close();
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
### 13. DynamicObject/DynamicObjectCollection 未判空
|
|
256
|
+
**检查点**:
|
|
257
|
+
- 读取 F7 基础资料字段前是否判空?
|
|
258
|
+
- 访问分录集合前是否判空?(`entries == null || entries.isEmpty()`)
|
|
259
|
+
- 嵌套分录访问是否逐层判空?
|
|
260
|
+
- BigDecimal 计算前是否判空?
|
|
261
|
+
|
|
262
|
+
**风险**: NullPointerException
|
|
263
|
+
|
|
264
|
+
**修正方案**:
|
|
265
|
+
```java
|
|
266
|
+
// ❌ 错误写法 - 未判空
|
|
267
|
+
DynamicObject org = (DynamicObject) getModel().getValue("org");
|
|
268
|
+
Long orgId = org.getLong("id"); // org 可能为 null
|
|
269
|
+
|
|
270
|
+
// ✅ 正确写法
|
|
271
|
+
DynamicObject org = (DynamicObject) getModel().getValue("org");
|
|
272
|
+
if (org != null) {
|
|
273
|
+
Long orgId = org.getLong("id");
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ❌ 错误写法 - 嵌套访问未逐层判空
|
|
277
|
+
DynamicObject subRow = entries.get(0).getDynamicObjectCollection("subentry").get(0);
|
|
278
|
+
|
|
279
|
+
// ✅ 正确写法 - 逐层判空
|
|
280
|
+
if (entries != null && !entries.isEmpty()) {
|
|
281
|
+
DynamicObject parentRow = entries.get(0);
|
|
282
|
+
DynamicObjectCollection subEntries = parentRow.getDynamicObjectCollection("subentry");
|
|
283
|
+
if (subEntries != null && !subEntries.isEmpty()) {
|
|
284
|
+
DynamicObject subRow = subEntries.get(0);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
---
|
|
290
|
+
|
|
291
|
+
### 14. 列表按钮操作未校验选中行
|
|
292
|
+
**检查点**:
|
|
293
|
+
- 列表 `itemClick` 中是否先获取选中行再执行业务逻辑?
|
|
294
|
+
- 未选中行时是否提示用户?
|
|
295
|
+
|
|
296
|
+
**风险**: 未选中行时直接执行导致空指针或逻辑错误
|
|
297
|
+
|
|
298
|
+
**修正方案**:
|
|
299
|
+
```java
|
|
300
|
+
// ❌ 错误写法 - 未校验选中行
|
|
301
|
+
@Override
|
|
302
|
+
public void itemClick(ItemClickEvent evt) {
|
|
303
|
+
if ("btnExport".equals(evt.getItemKey())) {
|
|
304
|
+
processSelected(getSelectedRows()); // 可能为空
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ✅ 正确写法
|
|
309
|
+
@Override
|
|
310
|
+
public void itemClick(ItemClickEvent evt) {
|
|
311
|
+
if ("btnExport".equals(evt.getItemKey())) {
|
|
312
|
+
List<Object> selectedIds = this.getSelectedRows();
|
|
313
|
+
if (selectedIds.isEmpty()) {
|
|
314
|
+
getView().showTipNotification("请先选择数据");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
processSelected(selectedIds);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
```
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
金蝶代码问题模式匹配器
|
|
4
|
+
用于快速识别代码中的严重问题
|
|
5
|
+
|
|
6
|
+
使用方法:
|
|
7
|
+
python pattern-matcher.py <file_path>
|
|
8
|
+
|
|
9
|
+
输出:
|
|
10
|
+
- P0 级问题(阻断/严重)
|
|
11
|
+
- P1 级问题(高危/性能)
|
|
12
|
+
- P2 级问题(规范/建议)
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import re
|
|
16
|
+
import sys
|
|
17
|
+
from typing import List, Tuple, Dict, Optional
|
|
18
|
+
|
|
19
|
+
# ==========================================
|
|
20
|
+
# 上下文验证函数(减少误判)
|
|
21
|
+
# ==========================================
|
|
22
|
+
|
|
23
|
+
def _is_in_try_with_resources(code: str, match_start: int) -> bool:
|
|
24
|
+
"""检查匹配位置是否在 try-with-resources 块内"""
|
|
25
|
+
# 向前查找最近的 try( 语句
|
|
26
|
+
before = code[:match_start]
|
|
27
|
+
# 查找最近的 try 块
|
|
28
|
+
try_pos = before.rfind('try (')
|
|
29
|
+
try_pos2 = before.rfind('try(')
|
|
30
|
+
try_pos = max(try_pos, try_pos2)
|
|
31
|
+
if try_pos == -1:
|
|
32
|
+
return False
|
|
33
|
+
# 检查 try 和 match 之间是否有闭合的 }
|
|
34
|
+
between = before[try_pos:]
|
|
35
|
+
open_braces = between.count('{') - between.count('}')
|
|
36
|
+
return open_braces > 0
|
|
37
|
+
|
|
38
|
+
def _is_in_comment(code: str, match_start: int) -> bool:
|
|
39
|
+
"""检查匹配位置是否在注释中"""
|
|
40
|
+
# 检查是否在单行注释中
|
|
41
|
+
line_start = code.rfind('\n', 0, match_start) + 1
|
|
42
|
+
line = code[line_start:match_start]
|
|
43
|
+
if '//' in line:
|
|
44
|
+
return True
|
|
45
|
+
# 检查是否在多行注释中
|
|
46
|
+
last_open = code.rfind('/*', 0, match_start)
|
|
47
|
+
if last_open != -1:
|
|
48
|
+
last_close = code.rfind('*/', 0, match_start)
|
|
49
|
+
if last_close < last_open:
|
|
50
|
+
return True
|
|
51
|
+
return False
|
|
52
|
+
|
|
53
|
+
def _is_org_user_id_field(match_text: str) -> bool:
|
|
54
|
+
"""检查是否是组织/用户/部门相关字段(真正的硬编码ID问题)"""
|
|
55
|
+
id_field_patterns = [
|
|
56
|
+
r'"[^"]*org[^"]*"', # 组织相关
|
|
57
|
+
r'"[^"]*user[^"]*"', # 用户相关
|
|
58
|
+
r'"[^"]*dept[^"]*"', # 部门相关
|
|
59
|
+
r'"[^"]*creator[^"]*"', # 创建人
|
|
60
|
+
r'"[^"]*modifier[^"]*"', # 修改人
|
|
61
|
+
r'"[^"]*approver[^"]*"', # 审核人
|
|
62
|
+
]
|
|
63
|
+
for pattern in id_field_patterns:
|
|
64
|
+
if re.search(pattern, match_text, re.IGNORECASE):
|
|
65
|
+
return True
|
|
66
|
+
return False
|
|
67
|
+
|
|
68
|
+
def _is_null_checked_before(code: str, var_name: str, match_start: int) -> bool:
|
|
69
|
+
"""检查变量在使用前是否已做过空检查"""
|
|
70
|
+
before = code[:match_start]
|
|
71
|
+
# 查找最近100个字符内是否有空检查
|
|
72
|
+
check_range = before[-200:] if len(before) > 200 else before
|
|
73
|
+
null_check_patterns = [
|
|
74
|
+
rf'{var_name}\s*!=\s*null',
|
|
75
|
+
rf'{var_name}\s*==\s*null',
|
|
76
|
+
rf'Optional\.ofNullable\s*\(\s*{var_name}',
|
|
77
|
+
rf'if\s*\(\s*{var_name}\s*!=\s*null',
|
|
78
|
+
]
|
|
79
|
+
for pattern in null_check_patterns:
|
|
80
|
+
if re.search(pattern, check_range):
|
|
81
|
+
return True
|
|
82
|
+
return False
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ==========================================
|
|
86
|
+
# P0 级问题模式定义(阻断/严重)
|
|
87
|
+
# ==========================================
|
|
88
|
+
|
|
89
|
+
PATTERNS_P0 = [
|
|
90
|
+
# 生命周期问题
|
|
91
|
+
(r'public\s+void\s+initialize\s*\([^)]*\)\s*\{[^}]*addItemClickListener',
|
|
92
|
+
'Initialize 注册监听', 'lifecycle-checklist.md', '内存泄漏,监听器无法正确释放'),
|
|
93
|
+
(r'public\s+void\s+initialize\s*\([^)]*\)\s*\{[^}]*getView\(\)',
|
|
94
|
+
'Initialize 操作 UI', 'lifecycle-checklist.md', '生命周期违规'),
|
|
95
|
+
|
|
96
|
+
# 事务问题
|
|
97
|
+
(r'beforeExecuteOperationTransaction[^}]*SaveServiceHelper\.save',
|
|
98
|
+
'操作插件独立保存破坏事务', 'data-transaction-checklist.md', '事务分离,数据不一致,无法回滚'),
|
|
99
|
+
(r'afterExecuteOperationTransaction[^}]*SaveServiceHelper\.save',
|
|
100
|
+
'操作插件独立保存破坏事务', 'data-transaction-checklist.md', '事务分离,数据不一致'),
|
|
101
|
+
|
|
102
|
+
# 资源泄漏 - DataSet(需上下文验证:排除已在 try-with-resources 中的情况)
|
|
103
|
+
(r'DataSet\s+\w+\s*=\s*QueryServiceHelper\.',
|
|
104
|
+
'DataSet 查询未使用 try-with-resources', 'data-transaction-checklist.md', '连接池耗尽,系统崩溃',
|
|
105
|
+
'check_try_with_resources'),
|
|
106
|
+
(r'DataSet\s+\w+\s*=\s*Algo\.',
|
|
107
|
+
'DataSet (Algo) 未使用 try-with-resources', 'data-transaction-checklist.md', '计算资源泄漏',
|
|
108
|
+
'check_try_with_resources'),
|
|
109
|
+
|
|
110
|
+
# 资源泄漏 - AlgoContext(需上下文验证)
|
|
111
|
+
(r'AlgoContext\s+\w+\s*=\s*Algo\.newContext\(\)',
|
|
112
|
+
'AlgoContext 可能未关闭', 'data-transaction-checklist.md', '计算资源泄漏',
|
|
113
|
+
'check_try_with_resources'),
|
|
114
|
+
|
|
115
|
+
# 硬编码组织/用户 ID(缩小匹配范围,仅匹配 org/user/dept 相关字段)
|
|
116
|
+
(r'\.set\s*\(\s*"[^"]*(org|creator|modifier|approver|dept|user)[^"]*"\s*,\s*\d{4,}L?\s*\)',
|
|
117
|
+
'硬编码组织/用户 ID', 'data-transaction-checklist.md', '跨环境部署失败'),
|
|
118
|
+
|
|
119
|
+
# 空指针风险 - P0级(链式调用未判空)
|
|
120
|
+
(r'getDynamicObject\s*\(\s*"[^"]+"\s*\)\.getPkValue\s*\(',
|
|
121
|
+
'getDynamicObject() 结果未判空调用 getPkValue', 'coding-standard-checklist.md', 'NullPointerException'),
|
|
122
|
+
(r'getDynamicObject\s*\(\s*"[^"]+"\s*\)\.getString\s*\(',
|
|
123
|
+
'getDynamicObject() 结果未判空调用 getString', 'coding-standard-checklist.md', 'NullPointerException'),
|
|
124
|
+
(r'getDynamicObject\s*\(\s*"[^"]+"\s*\)\.getBigDecimal\s*\(',
|
|
125
|
+
'getDynamicObject() 结果未判空调用 getBigDecimal', 'coding-standard-checklist.md', 'NullPointerException'),
|
|
126
|
+
(r'getDynamicObject\s*\(\s*"[^"]+"\s*\)\.getLong\s*\(',
|
|
127
|
+
'getDynamicObject() 结果未判空调用 getLong', 'coding-standard-checklist.md', 'NullPointerException'),
|
|
128
|
+
|
|
129
|
+
# 分布式锁未在 finally 释放
|
|
130
|
+
(r'\.lock\s*\(\s*\)[^}]*(?!finally)',
|
|
131
|
+
'分布式锁可能未在 finally 中释放', 'infra-checklist.md', '锁永久不释放,线程阻塞'),
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
# ==========================================
|
|
135
|
+
# P1 级问题模式定义(高危/性能)
|
|
136
|
+
# ==========================================
|
|
137
|
+
|
|
138
|
+
PATTERNS_P1 = [
|
|
139
|
+
# 性能问题 - UI
|
|
140
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*updateView\s*\(',
|
|
141
|
+
'循环内调用 updateView', 'ui-performance-checklist.md', '界面卡顿'),
|
|
142
|
+
(r'while\s*\([^)]+\)\s*\{[^}]*updateView\s*\(',
|
|
143
|
+
'循环内调用 updateView', 'ui-performance-checklist.md', '界面卡顿'),
|
|
144
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*getFieldIndex\s*\(',
|
|
145
|
+
'循环内获取 FieldIndex', 'ui-performance-checklist.md', '性能损耗'),
|
|
146
|
+
|
|
147
|
+
# 性能问题 - 数据库
|
|
148
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*BusinessDataServiceHelper\.loadSingle',
|
|
149
|
+
'循环内调用 loadSingle', 'data-transaction-checklist.md', 'N+1 查询问题'),
|
|
150
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*BusinessDataServiceHelper\.load\s*\(',
|
|
151
|
+
'循环内调用 load', 'data-transaction-checklist.md', 'N+1 查询问题'),
|
|
152
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*SaveServiceHelper\.save',
|
|
153
|
+
'循环内调用 save', 'data-transaction-checklist.md', '性能问题+事务风险'),
|
|
154
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*QueryServiceHelper\.query',
|
|
155
|
+
'循环内调用 QueryServiceHelper', 'data-transaction-checklist.md', 'N+1 查询问题'),
|
|
156
|
+
(r'for\s*\([^)]+\)\s*\{[^}]*DispatchServiceHelper\.invoke',
|
|
157
|
+
'循环内调用微服务', 'data-transaction-checklist.md', 'N+1 远程调用'),
|
|
158
|
+
|
|
159
|
+
# 空指针风险 - P1级
|
|
160
|
+
(r'getDataEntities\s*\(\s*\)\s*\[\s*\d+\s*\]',
|
|
161
|
+
'getDataEntities() 数组访问未判空', 'coding-standard-checklist.md', 'ArrayIndexOutOfBoundsException'),
|
|
162
|
+
(r'this\.dataEntities\s*\[',
|
|
163
|
+
'dataEntities 数组访问未判空', 'coding-standard-checklist.md', '空指针风险'),
|
|
164
|
+
|
|
165
|
+
# JDK 原生线程
|
|
166
|
+
(r'new\s+Thread\s*\(',
|
|
167
|
+
'使用 JDK 原生 Thread', 'infra-checklist.md', '绕过平台线程管理'),
|
|
168
|
+
(r'Executors\.\s*new\w+Pool',
|
|
169
|
+
'使用 JDK 原生线程池', 'infra-checklist.md', '绕过平台线程管理'),
|
|
170
|
+
|
|
171
|
+
# System.out
|
|
172
|
+
(r'System\s*\.\s*out\s*\.\s*print',
|
|
173
|
+
'使用 System.out 而非平台日志', 'coding-standard-checklist.md', '日志不规范'),
|
|
174
|
+
|
|
175
|
+
# 集合问题
|
|
176
|
+
(r'\.size\s*\(\s*\)\s*>\s*0',
|
|
177
|
+
'建议使用 !isEmpty() 替代 size()>0', 'coding-standard-checklist.md', '代码可读性'),
|
|
178
|
+
|
|
179
|
+
# MQ publisher 未关闭
|
|
180
|
+
(r'createSimplePublisher\s*\([^)]*\)',
|
|
181
|
+
'MQ Publisher 可能未关闭', 'infra-checklist.md', '资源泄漏',
|
|
182
|
+
'check_try_with_resources'),
|
|
183
|
+
]
|
|
184
|
+
|
|
185
|
+
# ==========================================
|
|
186
|
+
# P2 级问题模式定义(规范/建议)
|
|
187
|
+
# ==========================================
|
|
188
|
+
|
|
189
|
+
PATTERNS_P2 = [
|
|
190
|
+
# 编码规范
|
|
191
|
+
(r'private\s+final\s+\w+\s+ZERO\s*=\s*BigDecimal\.ZERO',
|
|
192
|
+
'ZERO 字段应声明为 static final', 'coding-standard-checklist.md', '内存浪费'),
|
|
193
|
+
(r'public\s+\w+\s*\(\s*\)\s*\{\s*super\s*\(\s*\)\s*;\s*\}',
|
|
194
|
+
'冗余的空构造函数', 'coding-standard-checklist.md', '代码冗余'),
|
|
195
|
+
(r'logger\.error\s*\(\s*"[^"]+"\s*\)',
|
|
196
|
+
'异常日志缺少堆栈信息', 'coding-standard-checklist.md', '排查困难'),
|
|
197
|
+
(r'new\s+Object\[\s*0\s*\]',
|
|
198
|
+
'new Object[0] 可简化', 'coding-standard-checklist.md', '代码冗余'),
|
|
199
|
+
|
|
200
|
+
# e.printStackTrace
|
|
201
|
+
(r'\.printStackTrace\s*\(\s*\)',
|
|
202
|
+
'禁止直接调用 printStackTrace', 'coding-standard-checklist.md', '日志不规范'),
|
|
203
|
+
|
|
204
|
+
# 空 catch 块
|
|
205
|
+
(r'catch\s*\(\s*\w+\s+\w+\s*\)\s*\{\s*\}',
|
|
206
|
+
'空 catch 块隐藏异常', 'coding-standard-checklist.md', '异常被吞'),
|
|
207
|
+
|
|
208
|
+
# BigDecimal 问题(降级为 P2 - 仅在 DataSet.Row 场景下才是真正风险)
|
|
209
|
+
(r'row\.getBigDecimal\s*\(\s*"[^"]+"\s*\)\.compareTo',
|
|
210
|
+
'DataSet Row.getBigDecimal() 结果可能为 null', 'coding-standard-checklist.md', '空指针风险'),
|
|
211
|
+
]
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def match_patterns(code: str, patterns: List[Tuple], level: str) -> List[dict]:
|
|
215
|
+
"""匹配代码中的问题模式,支持上下文验证减少误判"""
|
|
216
|
+
issues = []
|
|
217
|
+
for pattern_data in patterns:
|
|
218
|
+
# 支持带上下文验证的5元组格式和普通4/3元组格式
|
|
219
|
+
context_check = None
|
|
220
|
+
if len(pattern_data) == 5:
|
|
221
|
+
pattern, desc, ref, risk, context_check = pattern_data
|
|
222
|
+
elif len(pattern_data) == 4:
|
|
223
|
+
pattern, desc, ref, risk = pattern_data
|
|
224
|
+
else:
|
|
225
|
+
pattern, desc, ref = pattern_data
|
|
226
|
+
risk = '未指定'
|
|
227
|
+
|
|
228
|
+
matches = re.finditer(pattern, code, re.DOTALL | re.IGNORECASE)
|
|
229
|
+
for match in matches:
|
|
230
|
+
# 跳过注释中的匹配
|
|
231
|
+
if _is_in_comment(code, match.start()):
|
|
232
|
+
continue
|
|
233
|
+
|
|
234
|
+
# 上下文验证:检查是否在 try-with-resources 中
|
|
235
|
+
if context_check == 'check_try_with_resources':
|
|
236
|
+
if _is_in_try_with_resources(code, match.start()):
|
|
237
|
+
continue # 已在 try-with-resources 中,不是问题
|
|
238
|
+
|
|
239
|
+
issues.append({
|
|
240
|
+
'level': level,
|
|
241
|
+
'description': desc,
|
|
242
|
+
'reference': ref,
|
|
243
|
+
'risk': risk,
|
|
244
|
+
'match': match.group()[:100] + '...' if len(match.group()) > 100 else match.group(),
|
|
245
|
+
'start': match.start(),
|
|
246
|
+
'end': match.end()
|
|
247
|
+
})
|
|
248
|
+
return issues
|
|
249
|
+
|
|
250
|
+
def analyze_file(filepath: str) -> List[dict]:
|
|
251
|
+
"""分析单个文件"""
|
|
252
|
+
try:
|
|
253
|
+
with open(filepath, 'r', encoding='utf-8') as f:
|
|
254
|
+
code = f.read()
|
|
255
|
+
|
|
256
|
+
p0_issues = match_patterns(code, PATTERNS_P0, 'P0')
|
|
257
|
+
p1_issues = match_patterns(code, PATTERNS_P1, 'P1')
|
|
258
|
+
p2_issues = match_patterns(code, PATTERNS_P2, 'P2')
|
|
259
|
+
|
|
260
|
+
return p0_issues + p1_issues + p2_issues
|
|
261
|
+
except Exception as e:
|
|
262
|
+
print(f"Error reading file: {e}")
|
|
263
|
+
return []
|
|
264
|
+
|
|
265
|
+
def print_report(issues: List[dict]):
|
|
266
|
+
"""打印格式化的报告"""
|
|
267
|
+
if not issues:
|
|
268
|
+
print("\n✅ 未发现 P0/P1/P2 级问题模式\n")
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
# 按级别分组
|
|
272
|
+
p0 = [i for i in issues if i['level'] == 'P0']
|
|
273
|
+
p1 = [i for i in issues if i['level'] == 'P1']
|
|
274
|
+
p2 = [i for i in issues if i['level'] == 'P2']
|
|
275
|
+
|
|
276
|
+
print(f"\n{'='*60}")
|
|
277
|
+
print(f"📊 模式匹配扫描报告")
|
|
278
|
+
print(f"{'='*60}")
|
|
279
|
+
print(f" 总计发现 {len(issues)} 个潜在问题\n")
|
|
280
|
+
|
|
281
|
+
if p0:
|
|
282
|
+
print(f"🔴 P0 级问题 ({len(p0)} 个):")
|
|
283
|
+
for i, issue in enumerate(p0, 1):
|
|
284
|
+
print(f" {i}. {issue['description']}")
|
|
285
|
+
print(f" 风险: {issue['risk']}")
|
|
286
|
+
print(f" 参考: references/{issue['reference']}")
|
|
287
|
+
print()
|
|
288
|
+
|
|
289
|
+
if p1:
|
|
290
|
+
print(f"🟠 P1 级问题 ({len(p1)} 个):")
|
|
291
|
+
for i, issue in enumerate(p1, 1):
|
|
292
|
+
print(f" {i}. {issue['description']}")
|
|
293
|
+
print(f" 风险: {issue['risk']}")
|
|
294
|
+
print(f" 参考: references/{issue['reference']}")
|
|
295
|
+
print()
|
|
296
|
+
|
|
297
|
+
if p2:
|
|
298
|
+
print(f"🟡 P2 级问题 ({len(p2)} 个):")
|
|
299
|
+
for i, issue in enumerate(p2, 1):
|
|
300
|
+
print(f" {i}. {issue['description']}")
|
|
301
|
+
print(f" 风险: {issue['risk']}")
|
|
302
|
+
print(f" 参考: references/{issue['reference']}")
|
|
303
|
+
print()
|
|
304
|
+
|
|
305
|
+
# 计算评分
|
|
306
|
+
base_score = 100
|
|
307
|
+
score = base_score - len(p0) * 15 - len(p1) * 8 - len(p2) * 3
|
|
308
|
+
score = max(0, score)
|
|
309
|
+
|
|
310
|
+
print(f"{'='*60}")
|
|
311
|
+
print(f"📐 综合评分: {score} 分", end="")
|
|
312
|
+
if score >= 90:
|
|
313
|
+
print(" (优秀)")
|
|
314
|
+
elif score >= 75:
|
|
315
|
+
print(" (良好)")
|
|
316
|
+
elif score >= 60:
|
|
317
|
+
print(" (中等)")
|
|
318
|
+
else:
|
|
319
|
+
print(" (需改进)")
|
|
320
|
+
print(f"{'='*60}\n")
|
|
321
|
+
|
|
322
|
+
def main():
|
|
323
|
+
if len(sys.argv) < 2:
|
|
324
|
+
print("Usage: python pattern-matcher.py <file_path>")
|
|
325
|
+
print("\n示例:")
|
|
326
|
+
print(" python pattern-matcher.py MyPlugin.java")
|
|
327
|
+
sys.exit(1)
|
|
328
|
+
|
|
329
|
+
filepath = sys.argv[1]
|
|
330
|
+
print(f"\n🔍 正在扫描文件: {filepath}")
|
|
331
|
+
|
|
332
|
+
issues = analyze_file(filepath)
|
|
333
|
+
print_report(issues)
|
|
334
|
+
|
|
335
|
+
if __name__ == '__main__':
|
|
336
|
+
main()
|