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.
Files changed (219) hide show
  1. package/README.md +358 -0
  2. package/dist/cli/kcode.d.ts +15 -0
  3. package/dist/cli/kcode.js +153 -0
  4. package/dist/cli/main.d.ts +2 -0
  5. package/dist/cli/main.js +7 -0
  6. package/docs/KCODE_DISTRIBUTION.md +91 -0
  7. package/extensions/kingdee-harness.ts +180 -0
  8. package/extensions/kingdee-header.ts +122 -0
  9. package/extensions/kingdee-tools.ts +379 -0
  10. package/knowledge/.backup/v1.0.0/version.json +10 -0
  11. package/knowledge/cangqiong/product-notes.md +15 -0
  12. package/knowledge/common/business-flows.md +115 -0
  13. package/knowledge/common/config-guides.md +110 -0
  14. package/knowledge/common/error-patterns.md +170 -0
  15. package/knowledge/common/implementation.md +144 -0
  16. package/knowledge/cosmic/hard-constraints.md +38 -0
  17. package/knowledge/cosmic/ksql-datafix.md +34 -0
  18. package/knowledge/cosmic/platform-baseline.md +32 -0
  19. package/knowledge/cosmic/plugin-decision-matrix.md +40 -0
  20. package/knowledge/cosmic/review-checklist.md +40 -0
  21. package/knowledge/cosmic/unittest.md +35 -0
  22. package/knowledge/enterprise/api-reference.md +186 -0
  23. package/knowledge/enterprise/code-patterns.md +217 -0
  24. package/knowledge/enterprise/plugin-lifecycle.md +188 -0
  25. package/knowledge/enterprise/tables.json +159 -0
  26. package/knowledge/flagship/api-reference.md +237 -0
  27. package/knowledge/flagship/code-patterns.md +246 -0
  28. package/knowledge/flagship/cosmic-platform-note.md +15 -0
  29. package/knowledge/flagship/plugin-lifecycle.md +248 -0
  30. package/knowledge/flagship/tables.json +159 -0
  31. package/knowledge/version.json +10 -0
  32. package/knowledge/xinghan/product-notes.md +15 -0
  33. package/package.json +71 -0
  34. package/prompts/kd-discuss.md +11 -0
  35. package/prompts/kd-execute.md +12 -0
  36. package/prompts/kd-plan.md +12 -0
  37. package/prompts/kd-ship.md +12 -0
  38. package/prompts/kd-spec.md +12 -0
  39. package/prompts/kd-verify.md +12 -0
  40. package/skills/kd-check/SKILL.md +26 -0
  41. package/skills/kd-cosmic-dev/SKILL.md +82 -0
  42. package/skills/kd-cosmic-review/SKILL.md +90 -0
  43. package/skills/kd-cosmic-unittest/SKILL.md +92 -0
  44. package/skills/kd-debug/SKILL.md +30 -0
  45. package/skills/kd-discuss/SKILL.md +24 -0
  46. package/skills/kd-execute/SKILL.md +22 -0
  47. package/skills/kd-gen/SKILL.md +34 -0
  48. package/skills/kd-ksql/SKILL.md +86 -0
  49. package/skills/kd-plan/SKILL.md +24 -0
  50. package/skills/kd-ship/SKILL.md +22 -0
  51. package/skills/kd-spec/SKILL.md +24 -0
  52. package/skills/kd-verify/SKILL.md +22 -0
  53. package/themes/kcode-dark.json +81 -0
  54. package/vendor/kingdee-skills/cosmic-unittest/SKILL.md +788 -0
  55. package/vendor/kingdee-skills/cosmic-unittest/author-cache.json +5 -0
  56. package/vendor/kingdee-skills/cosmic-unittest/cosmic-unittest-skill-overview.html +746 -0
  57. package/vendor/kingdee-skills/cosmic-unittest/examples/business-test.md +205 -0
  58. package/vendor/kingdee-skills/cosmic-unittest/examples/common-test.md +257 -0
  59. package/vendor/kingdee-skills/cosmic-unittest/examples/formplugin-test.md +560 -0
  60. package/vendor/kingdee-skills/cosmic-unittest/examples/op-plugin-test.md +231 -0
  61. package/vendor/kingdee-skills/cosmic-unittest/examples/validator-test.md +232 -0
  62. package/vendor/kingdee-skills/cosmic-unittest/patterns/business-helper.md +184 -0
  63. package/vendor/kingdee-skills/cosmic-unittest/patterns/common-module.md +355 -0
  64. package/vendor/kingdee-skills/cosmic-unittest/patterns/convert-plugin.md +130 -0
  65. package/vendor/kingdee-skills/cosmic-unittest/patterns/formplugin.md +235 -0
  66. package/vendor/kingdee-skills/cosmic-unittest/patterns/op-plugin.md +226 -0
  67. package/vendor/kingdee-skills/cosmic-unittest/patterns/validator.md +206 -0
  68. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/SKILL.md +674 -0
  69. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/advanced-scenario-checklist.md +307 -0
  70. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/algox-performance-checklist.md +129 -0
  71. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/coding-standard-checklist.md +491 -0
  72. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/cosmic-api-checklist.md +285 -0
  73. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-access-checklist.md +261 -0
  74. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/data-transaction-checklist.md +390 -0
  75. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/domain-logic-checklist.md +295 -0
  76. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/form-plugin-checklist.md +508 -0
  77. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/infra-checklist.md +254 -0
  78. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ksql-checklist.md +305 -0
  79. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/lifecycle-checklist.md +298 -0
  80. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/operation-plugin-checklist.md +442 -0
  81. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/test-mock-checklist.md +120 -0
  82. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/references/ui-performance-checklist.md +320 -0
  83. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/pattern-matcher.py +336 -0
  84. package/vendor/kingdee-skills/kingdee-cosmic-reviewer/scripts/review-score-calculator.py +121 -0
  85. package/vendor/kingdee-skills/ok-cosmic/CHANGELOG.md +295 -0
  86. package/vendor/kingdee-skills/ok-cosmic/README.md +460 -0
  87. package/vendor/kingdee-skills/ok-cosmic/SKILL.md +287 -0
  88. package/vendor/kingdee-skills/ok-cosmic/agents/openai.yaml +17 -0
  89. package/vendor/kingdee-skills/ok-cosmic/assets/BatchImportPluginTemplate.java +93 -0
  90. package/vendor/kingdee-skills/ok-cosmic/assets/BillPlugInTemplate.java +156 -0
  91. package/vendor/kingdee-skills/ok-cosmic/assets/ConvertPlugInTemplate.java +255 -0
  92. package/vendor/kingdee-skills/ok-cosmic/assets/FormPluginTemplate.java +597 -0
  93. package/vendor/kingdee-skills/ok-cosmic/assets/IWorkflowPluginTemplate.java +91 -0
  94. package/vendor/kingdee-skills/ok-cosmic/assets/ListPluginTemplate.java +194 -0
  95. package/vendor/kingdee-skills/ok-cosmic/assets/OpPluginTemplate.java +201 -0
  96. package/vendor/kingdee-skills/ok-cosmic/assets/OpenApiControllerTemplate.java +103 -0
  97. package/vendor/kingdee-skills/ok-cosmic/assets/PrintPluginTemplate.java +95 -0
  98. package/vendor/kingdee-skills/ok-cosmic/assets/ReportFormPluginTemplate.java +257 -0
  99. package/vendor/kingdee-skills/ok-cosmic/assets/ReportListDataPluginTemplate.java +70 -0
  100. package/vendor/kingdee-skills/ok-cosmic/assets/StandardTreeListPluginTemplate.java +130 -0
  101. package/vendor/kingdee-skills/ok-cosmic/assets/TaskTemplate.java +80 -0
  102. package/vendor/kingdee-skills/ok-cosmic/assets/TreeListPluginTemplate.java +152 -0
  103. package/vendor/kingdee-skills/ok-cosmic/assets/WriteBackPlugInTemplate.java +286 -0
  104. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/attachment/AttachmentUploadBindSample.java +93 -0
  105. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/BotpTracePushSample.java +168 -0
  106. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/botp/SampleConvertPlugin.java +223 -0
  107. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/cache/SampleCacheUsage.java +218 -0
  108. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/concurrent/SampleThreadPoolBatch.java +156 -0
  109. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectCrudSample.java +205 -0
  110. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/data/DynamicObjectOpsSample.java +100 -0
  111. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/BeforeOperationConfirmSample.java +217 -0
  112. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ConfirmDialogSample.java +131 -0
  113. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/EntryRowCalculateSample.java +116 -0
  114. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/F7FilterSample.java +134 -0
  115. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/GetAndSetValueSample.java +176 -0
  116. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/HyperlinkJumpSample.java +124 -0
  117. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/OpenBillModalSample.java +253 -0
  118. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ReturnParentDataSample.java +295 -0
  119. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/TreeControlSample.java +140 -0
  120. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/form/ViewControlOpsSample.java +132 -0
  121. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPluginBasicSample.java +170 -0
  122. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/list/ListPreOpenFilterSample.java +68 -0
  123. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/message/MessageNotifySample.java +95 -0
  124. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/SampleMQConsumer.java +198 -0
  125. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/mq/sample_mq.xml +15 -0
  126. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OpAddValidatorsSample.java +137 -0
  127. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/operation/OperationOptionBridgeSample.java +228 -0
  128. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/package-info.java +19 -0
  129. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BaseDataQuerySample.java +194 -0
  130. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/BatchQuerySample.java +368 -0
  131. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/query/DataSetQueryStatSample.java +131 -0
  132. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportFormPlugin.java +179 -0
  133. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/report/SampleReportListDataPlugin.java +616 -0
  134. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/snippets-guide.md +64 -0
  135. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/task/ScheduleTaskSample.java +160 -0
  136. package/vendor/kingdee-skills/ok-cosmic/assets/snippets/workflow/SampleWorkflowPlugin.java +302 -0
  137. package/vendor/kingdee-skills/ok-cosmic/manifest.json +78 -0
  138. package/vendor/kingdee-skills/ok-cosmic/ok-cosmic-intro.html +903 -0
  139. package/vendor/kingdee-skills/ok-cosmic/references/adv/attachment-api.md +114 -0
  140. package/vendor/kingdee-skills/ok-cosmic/references/adv/botp-convert.md +98 -0
  141. package/vendor/kingdee-skills/ok-cosmic/references/adv/dynamic-object.md +113 -0
  142. package/vendor/kingdee-skills/ok-cosmic/references/adv/entity-metadata.md +123 -0
  143. package/vendor/kingdee-skills/ok-cosmic/references/adv/event-lifecycle.md +184 -0
  144. package/vendor/kingdee-skills/ok-cosmic/references/adv/flex-prop.md +114 -0
  145. package/vendor/kingdee-skills/ok-cosmic/references/adv/form-utils.md +133 -0
  146. package/vendor/kingdee-skills/ok-cosmic/references/adv/operate-chain.md +159 -0
  147. package/vendor/kingdee-skills/ok-cosmic/references/adv/plugin-base.md +218 -0
  148. package/vendor/kingdee-skills/ok-cosmic/references/adv/query-dataset.md +149 -0
  149. package/vendor/kingdee-skills/ok-cosmic/references/adv/request-context.md +88 -0
  150. package/vendor/kingdee-skills/ok-cosmic/references/adv/view-handler.md +157 -0
  151. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-bill.md +76 -0
  152. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-botp.md +70 -0
  153. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-form.md +165 -0
  154. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-import.md +69 -0
  155. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-list.md +227 -0
  156. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-openapi.md +112 -0
  157. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-operation.md +135 -0
  158. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-print.md +65 -0
  159. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-data.md +64 -0
  160. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-report-form.md +90 -0
  161. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-task.md +62 -0
  162. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-tree-list.md +71 -0
  163. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-workflow.md +82 -0
  164. package/vendor/kingdee-skills/ok-cosmic/references/base/plugin/plugin-writeback.md +71 -0
  165. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-algo.md +67 -0
  166. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-cache.md +63 -0
  167. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-model-svc.md +82 -0
  168. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-dynamic-object.md +70 -0
  169. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-entity-model.md +61 -0
  170. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-exception.md +64 -0
  171. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-file.md +63 -0
  172. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-id.md +47 -0
  173. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-lock.md +61 -0
  174. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-log.md +63 -0
  175. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-network-control.md +70 -0
  176. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-orm-access.md +78 -0
  177. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-request-context.md +62 -0
  178. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-threadpool.md +63 -0
  179. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-tx.md +64 -0
  180. package/vendor/kingdee-skills/ok-cosmic/references/base/sdk/sdk-utils.md +67 -0
  181. package/vendor/kingdee-skills/ok-cosmic/requirements.txt +2 -0
  182. package/vendor/kingdee-skills/ok-cosmic/rules/a-layer-rules.json +24 -0
  183. package/vendor/kingdee-skills/ok-cosmic/rules/anti-patterns.md +48 -0
  184. package/vendor/kingdee-skills/ok-cosmic/rules/cheat-sheet.md +256 -0
  185. package/vendor/kingdee-skills/ok-cosmic/rules/coding-preferences.md +140 -0
  186. package/vendor/kingdee-skills/ok-cosmic/rules/constraints.md +61 -0
  187. package/vendor/kingdee-skills/ok-cosmic/rules/decision-matrix.md +222 -0
  188. package/vendor/kingdee-skills/ok-cosmic/rules/intent-routing.md +94 -0
  189. package/vendor/kingdee-skills/ok-cosmic/rules/platform-baseline.md +69 -0
  190. package/vendor/kingdee-skills/ok-cosmic/rules/post-check.md +109 -0
  191. package/vendor/kingdee-skills/ok-cosmic/scripts/config_loader.py +204 -0
  192. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-api-knowledge.py +910 -0
  193. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-basedata-query.py +359 -0
  194. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-config-check.py +181 -0
  195. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-extpoints-query.py +389 -0
  196. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-form-metadata.py +856 -0
  197. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-check.py +262 -0
  198. package/vendor/kingdee-skills/ok-cosmic/scripts/cosmic-post-lint.py +293 -0
  199. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/__init__.py +2 -0
  200. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/base.py +393 -0
  201. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/resource_check.py +176 -0
  202. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/scene_check.py +375 -0
  203. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/style_check.py +434 -0
  204. package/vendor/kingdee-skills/ok-cosmic/scripts/lint/verify_check.py +36 -0
  205. package/vendor/kingdee-skills/ok-cosmic/scripts/route_client.py +186 -0
  206. package/vendor/kingdee-skills/ok-cosmic/scripts/script_utils.py +40 -0
  207. package/vendor/kingdee-skills/ok-cosmic/scripts/sqlite_cache.py +142 -0
  208. package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-commons.jar +0 -0
  209. package/vendor/kingdee-skills/ok-cosmic/setup/cuslib/kd-cd-cosmic-features.jar +0 -0
  210. package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic-docs.db +0 -0
  211. package/vendor/kingdee-skills/ok-cosmic/setup/ok-cosmic.json +13 -0
  212. package/vendor/kingdee-skills/ok-cosmic/setup/setup-mac.sh +18 -0
  213. package/vendor/kingdee-skills/ok-cosmic/setup/setup-windows.bat +53 -0
  214. package/vendor/kingdee-skills/ok-cosmic/setup/setup.jar +0 -0
  215. package/vendor/kingdee-skills/ok-ksql/SKILL.md +81 -0
  216. package/vendor/kingdee-skills/ok-ksql/agents/openai.yaml +7 -0
  217. package/vendor/kingdee-skills/ok-ksql/manifest.json +14 -0
  218. package/vendor/kingdee-skills/ok-ksql/references/ksql-datafix.md +452 -0
  219. package/vendor/kingdee-skills/ok-ksql/scripts/ksql_lint.py +363 -0
@@ -0,0 +1,390 @@
1
+ # 数据事务与资源释放检查表
2
+
3
+ ## 🔴 P0 级问题
4
+
5
+ ### 1. 操作插件中独立保存
6
+ **检查点**:
7
+ - `beforeExecuteOperationTransaction()` 内是否调用了 `SaveServiceHelper.save()`?
8
+ - `afterExecuteOperationTransaction()` 内是否调用了独立的保存操作?
9
+
10
+ **风险**: 导致事务分离,数据不一致,无法回滚
11
+
12
+ **修正方案**:
13
+ ```java
14
+ // ❌ 错误写法 - 独立保存破坏事务
15
+ @Override
16
+ public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
17
+ DynamicObject data = e.getDataEntities()[0];
18
+ SaveServiceHelper.save(new DynamicObject[]{data}); // 破坏事务!
19
+ }
20
+
21
+ // ✅ 正确写法 - 在事务内修改数据
22
+ @Override
23
+ public void beforeExecuteOperationTransaction(BeforeOperationArgs e) {
24
+ DynamicObject data = e.getDataEntities()[0];
25
+ data.set("status", "updated"); // 直接修改,由框架统一保存
26
+ }
27
+ ```
28
+
29
+ ---
30
+
31
+ ### 2. DataSet 资源未关闭
32
+ **检查点**:
33
+ - `DataSet` 是否使用了 try-with-resources?
34
+ - 是否在循环中创建 `DataSet` 但未关闭?
35
+
36
+ **风险**: 导致连接池耗尽,系统崩溃
37
+
38
+ **修正方案**:
39
+ ```java
40
+ // ❌ 错误写法 - 资源泄漏
41
+ DataSet ds = QueryServiceHelper.queryDataSet("query", "entity", fields, filters, null);
42
+ ds.forEach(row -> process(row));
43
+ // ds 未关闭!
44
+
45
+ // ✅ 正确写法 - try-with-resources
46
+ try (DataSet ds = QueryServiceHelper.queryDataSet("query", "entity", fields, filters, null)) {
47
+ ds.forEach(row -> process(row));
48
+ }
49
+ ```
50
+
51
+ ---
52
+
53
+ ### 3. 硬编码组织/用户 ID
54
+ **检查点**:
55
+ - 代码中是否有数字字面量作为**组织ID、用户ID、部门ID**?
56
+ - 是否有 `set("org", 1001L)` 这样的硬编码?
57
+
58
+ **⚠️ 误判排除 - 以下场景允许硬编码,不应报为问题**:
59
+ - **单据类型ID**: 业务场景必须使用特定单据类型ID,如 `billTypeId = "xxx"`,这是业务规则决定的
60
+ - **枚举值/状态值**: 如 `status == 1`、`billstatus = "C"` 等,是业务约定的固定枚举
61
+ - **AppId / FormId**: 苍穹应用ID和表单ID是元数据定义的固定标识,不会跨环境变化
62
+ - **常量类中定义的ID**: 如 `SMAppParameterConst.APPID`,属于集中管理的常量
63
+ - **审查原则**: 只有**组织ID、用户ID、部门ID、人员ID**等会随环境变化的ID硬编码才是问题;单据类型、应用标识等固定业务标识不属于硬编码问题
64
+
65
+ **风险**: 跨环境部署失败,数据错乱(仅限组织/用户等环境相关ID)
66
+
67
+ **修正方案**:
68
+ ```java
69
+ // ❌ 错误写法 - 组织/用户ID硬编码(P0)
70
+ data.set("org", 1001L);
71
+ data.set("creator", 88888L);
72
+
73
+ // ✅ 正确写法 - 从上下文获取
74
+ data.set("org", this.getContext().getCurrentOrganizationId());
75
+ data.set("creator", this.getContext().getCurrentUserId());
76
+
77
+ // ✅ 以下硬编码是合理的,不应报问题:
78
+ // 1. 单据类型ID - 业务规则固定
79
+ String billTypeId = "64e1db5a57e8e7";
80
+ // 2. AppId - 元数据固定标识
81
+ String appId = "/JJVQ13HQZAJ";
82
+ // 3. 枚举值/状态值 - 业务约定
83
+ if ("C".equals(billStatus)) { ... }
84
+ ```
85
+
86
+ ---
87
+
88
+ ### 4. AlgoContext 未关闭
89
+ **检查点**:
90
+ - `Algo.newContext()` 创建的上下文是否关闭?
91
+
92
+ **修正方案**:
93
+ ```java
94
+ // ❌ 错误写法
95
+ AlgoContext context = Algo.newContext();
96
+ DataSet ds = ...;
97
+ // context 未关闭!
98
+
99
+ // ✅ 正确写法
100
+ try (AlgoContext context = Algo.newContext()) {
101
+ DataSet ds = ...;
102
+ }
103
+ ```
104
+
105
+ ---
106
+
107
+ ### 5. DataSet 查询字段与使用字段不匹配
108
+ **检查点**:
109
+ - `QueryServiceHelper.queryDataSet()` 的 fields 参数是否包含了后续使用的所有字段?
110
+ - `Algo.create().query()` 的 select 字段是否包含了后续使用的所有字段?
111
+ - 遍历 `DataSet` 时,`row.getXxx("fieldname")` 的字段是否在查询时声明?
112
+
113
+ **风险**: 未声明的字段会返回默认值(false/0/null),导致业务逻辑错误,且难以排查
114
+
115
+ **修正方案**:
116
+ ```java
117
+ // ❌ 错误写法 - 查询字段缺失
118
+ DataSet ds = QueryServiceHelper.queryDataSet("query", "entity", "id,name", filters, null);
119
+ for (Row row : ds) {
120
+ boolean isActive = row.getBoolean("isactive"); // isactive 未在 fields 中声明!
121
+ // isactive 永远返回 false,导致逻辑错误
122
+ }
123
+
124
+ // ✅ 正确写法 - 包含所有需要的字段
125
+ DataSet ds = QueryServiceHelper.queryDataSet("query", "entity", "id,name,isactive", filters, null);
126
+ for (Row row : ds) {
127
+ boolean isActive = row.getBoolean("isactive"); // 正确获取真实值
128
+ }
129
+
130
+ // ❌ 错误写法 - Algo 查询字段缺失
131
+ DataSet ds = Algo.create().query("entity")
132
+ .filter("status = 1")
133
+ .select("id,code");
134
+ ds.forEach(row -> {
135
+ String name = row.getString("name"); // name 未在 select 中声明!
136
+ });
137
+
138
+ // ✅ 正确写法 - Algo 包含所有字段
139
+ DataSet ds = Algo.create().query("entity")
140
+ .filter("status = 1")
141
+ .select("id,code,name");
142
+ ds.forEach(row -> {
143
+ String name = row.getString("name"); // 正确获取
144
+ });
145
+ ```
146
+
147
+ **检查方法**:
148
+ 1. 找到所有 `queryDataSet()` 或 `Algo.query()` 调用
149
+ 2. 提取 fields/select 参数中的字段列表
150
+ 3. 检查后续代码中所有 `row.getXxx()` 调用
151
+ 4. 验证每个使用的字段是否在字段列表中
152
+
153
+ ---
154
+
155
+ ### 6. 分布式缓存 Key 未按账套隔离
156
+ **检查点**:
157
+ - `cache.put()` / `cache.get()` 的 key 是否未包含 `accountId` 前缀?
158
+ - 是否直接使用业务字符串作为 key,未拼接 `RequestContext.get().getAccountId()`?
159
+
160
+ **风险**: 不同账套的缓存数据互相覆盖/读取,导致数据安全隔离失效
161
+
162
+ **修正方案**:
163
+ ```java
164
+ // ❌ 错误写法 - key 未隔离,不同账套会互相污染
165
+ DistributeSessionlessCache cache = CacheFactory.getCommonCacheFactory()
166
+ .getDistributeSessionlessCache("customRegion");
167
+ cache.put("demo", "test");
168
+
169
+ // ✅ 正确写法 - key 前缀加上 accountId 实现账套隔离
170
+ DistributeSessionlessCache cache = CacheFactory.getCommonCacheFactory()
171
+ .getDistributeSessionlessCache("customRegion");
172
+ String accountId = RequestContext.get().getAccountId();
173
+ cache.put(accountId + "_demo", "test");
174
+ // get 时同样需要加前缀
175
+ Object value = cache.get(accountId + "_demo");
176
+ ```
177
+
178
+ ---
179
+
180
+ ## 🟠 P1 级问题
181
+
182
+ ### 5. 大批量数据一次性加载
183
+ **检查点**:
184
+ - 是否一次性加载大量数据到内存?
185
+ - 是否使用了分页查询?
186
+
187
+ **修正方案**:
188
+ ```java
189
+ // ❌ 错误写法
190
+ List<DynamicObject> all = BusinessDataServiceHelper.load(filters);
191
+
192
+ // ✅ 正确写法 - 分批处理
193
+ int pageSize = 1000;
194
+ int page = 0;
195
+ while (true) {
196
+ List<DynamicObject> batch = BusinessDataServiceHelper.load(filters, page * pageSize, pageSize);
197
+ if (batch.isEmpty()) break;
198
+ process(batch);
199
+ page++;
200
+ }
201
+ ```
202
+
203
+ ---
204
+
205
+ ### 6. 事务传播配置错误
206
+ **检查点**:
207
+ - 服务方法是否正确配置了事务传播行为?
208
+
209
+ **修正方案**:
210
+ ```java
211
+ // 使用 @Transactional 注解时注意传播行为
212
+ @Transactional(propagation = Propagation.REQUIRED) // 默认
213
+ @Transactional(propagation = Propagation.REQUIRES_NEW) // 新事务
214
+ ```
215
+
216
+ ---
217
+
218
+ ### 7. 循环内数据库操作
219
+ **检查点**:
220
+ - 循环内是否调用 `DB.***()` / `ORM.create` / `DispatchServiceHelper.invoke***()`?
221
+ - 循环内是否调用 `BusinessDataServiceHelper.loadSingle()` / `load()`?
222
+ - 循环内是否调用 `SaveServiceHelper.save()`?
223
+
224
+ **风险**: N+1 查询,性能骤降,可能导致数据库连接池耗尽
225
+
226
+ **修正方案**:
227
+ ```java
228
+ // ❌ 错误写法 - 循环内逐条查询
229
+ for (DynamicObject entry : entries) {
230
+ DynamicObject detail = BusinessDataServiceHelper.loadSingle(
231
+ entry.get("detailid"), "entityname"); // 每次循环都查库
232
+ }
233
+
234
+ // ✅ 正确写法 - 批量查询后再遍历
235
+ Object[] ids = entries.stream().map(e -> e.get("detailid")).toArray();
236
+ DynamicObject[] details = BusinessDataServiceHelper.load(ids, "entityname");
237
+ Map<Object, DynamicObject> detailMap = Arrays.stream(details)
238
+ .collect(Collectors.toMap(d -> d.get("id"), d -> d));
239
+ for (DynamicObject entry : entries) {
240
+ DynamicObject detail = detailMap.get(entry.get("detailid"));
241
+ }
242
+ ```
243
+
244
+ ---
245
+
246
+ ### 8. 无过滤条件查询大数据量
247
+ **检查点**:
248
+ - 查询是否缺少过滤条件?
249
+ - 默认查询范围是否过大?(应缩小到当月/当天/当前组织)
250
+
251
+ **风险**: 全表扫描导致数据库性能问题
252
+
253
+ **修正方案**:
254
+ ```java
255
+ // ❌ 错误写法 - 无过滤条件
256
+ DynamicObject[] all = BusinessDataServiceHelper.load("entityname", "id,name", null);
257
+
258
+ // ✅ 正确写法 - 添加合理的过滤条件
259
+ QFilter orgFilter = new QFilter("org", QCP.equals, RequestContext.get().getOrgId());
260
+ QFilter dateFilter = new QFilter("bizdate", QCP.large_equals, DateUtils.getMonthStart());
261
+ DynamicObject[] data = BusinessDataServiceHelper.load("entityname", "id,name",
262
+ new QFilter[]{orgFilter, dateFilter});
263
+ ```
264
+
265
+ ---
266
+
267
+ ### 9. SQL 拼接字符串(SQL 注入风险)
268
+ **检查点**:
269
+ - 查询参数是否直接拼接到 SQL 字符串中?
270
+ - 是否使用了 `Statement` 而非 `PreparedStatement`?
271
+
272
+ **风险**: SQL 注入攻击,数据泄露
273
+
274
+ **修正方案**:
275
+ ```java
276
+ // ❌ 错误写法 - SQL 拼接
277
+ String sql = "SELECT * FROM t_table WHERE name = '" + userName + "'";
278
+ DB.query(sql);
279
+
280
+ // ✅ 正确写法 - 参数化查询
281
+ QFilter filter = new QFilter("name", QCP.equals, userName);
282
+ QueryServiceHelper.query("entityname", "id,name", new QFilter[]{filter});
283
+ ```
284
+
285
+ ---
286
+
287
+ ### 10. 查询字段层级过深
288
+ **检查点**:
289
+ - 查询字段层级是否超过 4 层?(过深产生笛卡尔积,性能骤降)
290
+
291
+ **风险**: 笛卡尔积导致查询性能急剧下降
292
+
293
+ ---
294
+
295
+ ### 11. 使用 JDK 原生线程/线程池
296
+ **检查点**:
297
+ - 是否使用了 `new Thread()`、`Executors.newXxx()` 等 JDK 原生线程池?
298
+ - 必须使用苍穹平台线程池 `kd.bos.threads.ThreadPools`
299
+
300
+ **风险**: 绕过平台线程管理,可能导致上下文丢失、资源泄漏
301
+
302
+ **修正方案**:
303
+ ```java
304
+ // ❌ 错误写法 - JDK 原生线程
305
+ new Thread(() -> { /* 异步逻辑 */ }).start();
306
+ ExecutorService pool = Executors.newFixedThreadPool(4);
307
+
308
+ // ✅ 正确写法 - 苍穹平台线程池
309
+ ThreadPools.executeOnce(() -> { /* 异步逻辑 */ });
310
+ // 或
311
+ ThreadPool pool = ThreadPools.newFixedThreadPool(4);
312
+ ```
313
+
314
+ ---
315
+
316
+ ### 12. 数据访问方法选择不当
317
+ **检查点**:
318
+ - 判断存在是否使用了 `queryOne` 后判断?(应使用 `exists`)
319
+ - 大数据量查询是否使用了 `load`?(应使用 `queryDataSet`)
320
+ - 只读场景是否使用了 `loadSingle`?(应使用 `query`/`queryOne`)
321
+ - 查询结果**不需要保存回库**时,是否使用了 `BusinessDataServiceHelper.load`?(应改用 `QueryServiceHelper.query`)
322
+ - 高频读取基础资料是否使用了 `loadSingle`?(应使用 `loadSingleFromCache`)
323
+ - `loadFromCache` 是否用于数据量 > 1000 的场景?(不推荐)
324
+
325
+ **数据访问方法选择指南**:
326
+ | 场景 | 推荐方法 | 说明 |
327
+ |------|---------|------|
328
+ | 判断存在 | `QueryServiceHelper.exists` | 比 queryOne 后判断更高效 |
329
+ | 大数据量查询 | `QueryServiceHelper.queryDataSet` | 流式,性能最佳,必须 try-with-resources |
330
+ | 小量只读 | `QueryServiceHelper.query/queryOne` | 返回 plainObject,性能好 |
331
+ | 需要保存回库 | `BusinessDataServiceHelper.loadSingle/load` | 实时读库 |
332
+ | 高频读取小量基础资料 | `loadSingleFromCache/loadFromCache` | 数据量<1000才推荐 |
333
+
334
+ ---
335
+
336
+ ### 13. 敏感信息硬编码
337
+ **检查点**:
338
+ - 代码中是否硬编码了账号、密码、外链等敏感信息?
339
+ - 页面是否明文显示敏感信息?
340
+ - 是否使用了 XML 外部实体(`<!DOCTYPE>`、`<!ENTITY>`、`SYSTEM`)?
341
+
342
+ **风险**: 安全漏洞,敏感信息泄露
343
+
344
+ **修正方案**:
345
+ ```java
346
+ // ❌ 错误写法 - 硬编码密码
347
+ String password = "admin123";
348
+
349
+ // ✅ 正确写法 - 加密后存 MC(管理中心)
350
+ String password = MCConfigHelper.getConfig("db.password");
351
+ ```
352
+
353
+ ---
354
+
355
+ ### 14. 第三方 API 调用未设置超时
356
+ **检查点**:
357
+ - 所有第三方 API 调用是否设置了超时时间?
358
+
359
+ **风险**: 第三方服务不可用时导致线程阻塞
360
+
361
+ ---
362
+
363
+ ### 15. 微服务调用不规范
364
+ **检查点**:
365
+ - 跨云跨应用是否通过微服务调用?(禁止直接依赖其他应用的 jar 包)
366
+ - 服务工厂路由规则是否正确?(`{isv}.{cloudId}.{appId}.ServiceFactory`)
367
+
368
+ **修正方案**:
369
+ ```java
370
+ // ❌ 错误写法 - 直接依赖其他应用 jar 包调用
371
+ OtherAppService service = new OtherAppService();
372
+
373
+ // ✅ 正确写法 - 通过微服务调用
374
+ Object result = DispatchServiceHelper.invokeService(
375
+ "isv.ti.bo", // Factory 类限定前缀
376
+ "appId", // 应用 Id
377
+ "xxxService", // 注册的服务名称
378
+ "methodName", // 调用方法
379
+ new Object[]{...} // 入参
380
+ );
381
+ ```
382
+
383
+ ---
384
+
385
+ ### 16. 事务边界不合理
386
+ **检查点**:
387
+ - 是否存在大事务?(耗时操作应拆分为多个小事务)
388
+ - 需要事务控制和权限控制的逻辑是否放在了表单插件中?(应在操作插件中完成)
389
+
390
+ **风险**: 大事务导致数据库锁等待、超时
@@ -0,0 +1,295 @@
1
+ # 领域特定业务逻辑检查表
2
+
3
+ ## 🔴 P0 级问题
4
+
5
+ ### 1. 余额模型校验缺失
6
+ **检查点**:
7
+ - 余额扣减是否调用了官方 API?
8
+ - 是否直接操作余额表而不经过标准服务?
9
+
10
+ **风险**: 数据不一致,余额错误
11
+
12
+ **修正方案**:
13
+ ```java
14
+ // ❌ 错误写法 - 直接操作余额表
15
+ DynamicObject balance = BusinessDataServiceHelper.loadSingle(balanceId, "balance_entity");
16
+ balance.set("amount", newAmount);
17
+ SaveServiceHelper.save(balance);
18
+
19
+ // ✅ 正确写法 - 使用官方余额服务
20
+ BalanceServiceHelper.deductBalance(orgId, accountId, amount, billInfo);
21
+ ```
22
+
23
+ ---
24
+
25
+ ### 2. F7 过滤缺失数据范围
26
+ **检查点**:
27
+ - F7 字段是否注入了数据权限?
28
+ - 是否有正确的组织范围过滤?
29
+
30
+ **风险**: 数据越权访问
31
+
32
+ **修正方案**:
33
+ ```java
34
+ // ❌ 错误写法 - 无权限过滤
35
+ QFilter filter = new QFilter("status", "=", "A");
36
+
37
+ // ✅ 正确写法 - 注入数据权限
38
+ QFilter filter = new QFilter("status", "=", "A");
39
+ filter.and(new QFilter("org", "in", PermissionHelper.getPermissionOrgs()));
40
+ ```
41
+
42
+ ---
43
+
44
+ ### 3. 金额/数量计算精度丢失
45
+ **检查点**:
46
+ - 金额计算是否使用了 `double` 或 `float` 类型?
47
+ - BigDecimal 除法是否指定了精度和舍入模式?
48
+ - 金额比较是否使用了 `==` 而非 `compareTo`?
49
+ - 多次乘除运算是否存在中间精度丢失?
50
+
51
+ **风险**: 财务数据精度丢失,对账不平
52
+
53
+ **修正方案**:
54
+ ```java
55
+ // ❌ 错误写法 - double 计算金额
56
+ double amount = price * qty;
57
+ double taxRate = 0.13;
58
+ double taxAmount = amount * taxRate;
59
+
60
+ // ❌ 错误写法 - BigDecimal 除法未指定精度
61
+ BigDecimal unitPrice = totalAmount.divide(qty); // ArithmeticException!
62
+
63
+ // ❌ 错误写法 - 金额比较用 ==
64
+ if (amount == BigDecimal.ZERO) { ... }
65
+
66
+ // ✅ 正确写法
67
+ BigDecimal amount = price.multiply(qty);
68
+ BigDecimal taxAmount = amount.multiply(new BigDecimal("0.13"));
69
+ BigDecimal unitPrice = totalAmount.divide(qty, 10, RoundingMode.HALF_UP);
70
+ if (amount.compareTo(BigDecimal.ZERO) == 0) { ... }
71
+ ```
72
+
73
+ ---
74
+
75
+ ### 4. 单据状态流转不合法
76
+ **检查点**:
77
+ - 单据状态变更是否遵循合法的状态流转路径?
78
+ - 是否存在跳过中间状态直接变更的情况?(如从"暂存"直接到"已审核")
79
+ - 反审核、作废等逆向操作是否检查了下游单据状态?
80
+
81
+ **风险**: 状态混乱,业务流程被破坏
82
+
83
+ **修正方案**:
84
+ ```java
85
+ // ❌ 错误写法 - 未校验当前状态直接变更
86
+ data.set("billstatus", "C"); // 直接设为已审核
87
+
88
+ // ✅ 正确写法 - 通过操作服务变更状态(框架会校验状态流转)
89
+ OperationServiceHelper.executeOperate("audit", entityName,
90
+ new Object[]{billId}, OperateOption.create());
91
+ ```
92
+
93
+ ---
94
+
95
+ ### 5. 收付款计划金额一致性
96
+ **检查点**:
97
+ - 收付款计划行的金额合计是否等于单据总金额?
98
+ - 新增/删除/修改计划行后是否重算了金额分摊?
99
+ - 是否存在尾差未处理的情况?
100
+
101
+ **风险**: 收付款金额与单据金额不一致,财务对账差异
102
+
103
+ **修正方案**:
104
+ ```java
105
+ // ✅ 正确做法 - 修改计划行后重算金额分摊
106
+ // 1. 计算各行分摊比例
107
+ // 2. 按比例分配金额
108
+ // 3. 尾差放到最后一行
109
+ BigDecimal total = BigDecimal.ZERO;
110
+ for (int i = 0; i < planEntries.size() - 1; i++) {
111
+ BigDecimal ratio = planEntries.get(i).getBigDecimal("ratio");
112
+ BigDecimal lineAmt = totalAmount.multiply(ratio).setScale(2, RoundingMode.HALF_UP);
113
+ planEntries.get(i).set("amount", lineAmt);
114
+ total = total.add(lineAmt);
115
+ }
116
+ // 尾差放最后一行
117
+ planEntries.get(planEntries.size() - 1).set("amount", totalAmount.subtract(total));
118
+ ```
119
+
120
+ ---
121
+
122
+ ## 🟠 P1 级问题
123
+
124
+ ### 6. 库存操作未走标准服务
125
+ **检查点**:
126
+ - 是否直接操作库存表?
127
+ - 是否使用了标准库存服务?
128
+
129
+ **修正方案**:
130
+ ```java
131
+ // ✅ 使用标准库存服务
132
+ InventoryServiceHelper.operateInventory(operates);
133
+ ```
134
+
135
+ ---
136
+
137
+ ### 7. 单据编号未使用编码规则
138
+ **检查点**:
139
+ - 是否手动生成单据编号?
140
+
141
+ **修正方案**:
142
+ ```java
143
+ // ❌ 错误写法
144
+ String billNo = "PO" + System.currentTimeMillis();
145
+
146
+ // ✅ 正确写法
147
+ String billNo = BillCodeHelper.getBillNo(billTypeId, orgId);
148
+ ```
149
+
150
+ ---
151
+
152
+ ### 8. BOTP 下推转换数据完整性
153
+ **检查点**:
154
+ - 下推转换后是否校验了目标单据的必填字段是否完整?
155
+ - 下推数量/金额是否超过源单可下推量?
156
+ - 是否正确设置了源单关联信息(反写字段)?
157
+ - 转换规则中的字段映射是否覆盖了所有需要携带的字段?
158
+
159
+ **风险**: 下推单据数据不完整导致后续业务异常
160
+
161
+ **修正方案**:
162
+ ```java
163
+ // ✅ 下推后校验必填字段
164
+ ConvertOperationResult result = ConvertServiceHelper.push(srcEntityName, targetEntityName, ids, option);
165
+ if (result.isSuccess()) {
166
+ DynamicObject[] targetBills = result.getTargetBills();
167
+ for (DynamicObject bill : targetBills) {
168
+ // 校验必填字段完整性
169
+ validateRequiredFields(bill);
170
+ }
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ### 9. 票据/应收应付业务规则
177
+ **检查点**:
178
+ - 票据到期日计算是否正确处理了节假日和工作日?
179
+ - 应收/应付核销金额是否超过未核销余额?
180
+ - 收款/付款时是否正确匹配了应收/应付单据?
181
+ - 红蓝字单据的金额正负号是否正确?
182
+ - 预收/预付转应收/应付的业务流转是否完整?
183
+
184
+ **风险**: 财务数据错误,核销异常
185
+
186
+ **修正方案**:
187
+ ```java
188
+ // ❌ 错误写法 - 未校验核销金额
189
+ data.set("writeOffAmount", inputAmount); // 直接设置,可能超过余额
190
+
191
+ // ✅ 正确写法 - 校验核销金额不超过未核销余额
192
+ BigDecimal unWriteOffAmt = totalAmount.subtract(alreadyWriteOffAmt);
193
+ if (inputAmount.compareTo(unWriteOffAmt) > 0) {
194
+ throw new KDBizException("核销金额不能超过未核销余额");
195
+ }
196
+ ```
197
+
198
+ ---
199
+
200
+ ### 10. 价格/税额计算规则
201
+ **检查点**:
202
+ - 含税/不含税价格转换是否正确?
203
+ - 税额计算公式是否符合业务规则?
204
+ - 折扣/优惠计算是否正确应用?
205
+ - 多币种场景是否正确换算汇率?
206
+
207
+ **风险**: 价格/税额计算错误
208
+
209
+ **修正方案**:
210
+ ```java
211
+ // ❌ 错误写法 - 含税价转不含税价精度丢失
212
+ BigDecimal priceWithoutTax = priceWithTax.divide(
213
+ BigDecimal.ONE.add(taxRate)); // 可能除不尽!
214
+
215
+ // ✅ 正确写法 - 指定精度
216
+ BigDecimal priceWithoutTax = priceWithTax.divide(
217
+ BigDecimal.ONE.add(taxRate), 10, RoundingMode.HALF_UP);
218
+ ```
219
+
220
+ ---
221
+
222
+ ### 11. 操作权限校验
223
+ **检查点**:
224
+ - 敏感操作(审核、反审核、删除)是否校验了用户权限?
225
+ - 是否存在绕过权限校验的路径?
226
+ - 微服务间调用是否传递了权限上下文?
227
+
228
+ **风险**: 越权操作,数据安全风险
229
+
230
+ ---
231
+
232
+ ### 12. 维度一致性校验
233
+ **检查点**:
234
+ - 多维度的业务数据(组织、币别、结算方式等)在不同位置是否保持一致?
235
+ - 维度变更后是否触发了关联字段的重新计算?
236
+ - 跨分录的维度关联是否正确校验?
237
+
238
+ **风险**: 维度不一致导致业务数据错误
239
+
240
+ ---
241
+
242
+ ## 🟡 P2 级问题
243
+
244
+ ### 13. 多语言字段未处理
245
+ **检查点**:
246
+ - 是否正确处理多语言字段?
247
+
248
+ **修正方案**:
249
+ ```java
250
+ // ✅ 正确获取多语言值
251
+ String localValue = data.getLocaleString("multiLangField");
252
+ ```
253
+
254
+ ---
255
+
256
+ ### 14. 业务日志记录不完整
257
+ **检查点**:
258
+ - 关键业务操作(审核、反审核、修改金额等)是否记录了操作日志?
259
+ - 日志内容是否包含操作前后的关键数据变化?
260
+
261
+ **修正方案**:
262
+ ```java
263
+ // ✅ 记录关键业务操作日志
264
+ logger.info("单据[{}]审核: 操作人={}, 审核金额={}",
265
+ billNo, currentUser, amount);
266
+ ```
267
+
268
+ ---
269
+
270
+ ### 15. 批量操作未考虑部分失败
271
+ **检查点**:
272
+ - 批量操作是否考虑了部分成功部分失败的场景?
273
+ - 失败的记录是否有明确的错误信息反馈?
274
+
275
+ **修正方案**:
276
+ ```java
277
+ // ❌ 错误写法 - 一条失败全部中断
278
+ for (DynamicObject obj : entities) {
279
+ processData(obj); // 一条异常全部停止
280
+ }
281
+
282
+ // ✅ 正确写法 - 逐条处理,收集错误
283
+ List<String> errors = new ArrayList<>();
284
+ for (DynamicObject obj : entities) {
285
+ try {
286
+ processData(obj);
287
+ } catch (Exception e) {
288
+ errors.add(String.format("单据[%s]处理失败: %s",
289
+ obj.getString("billno"), e.getMessage()));
290
+ }
291
+ }
292
+ if (!errors.isEmpty()) {
293
+ throw new KDBizException(String.join("\n", errors));
294
+ }
295
+ ```