@yss123/cage-cli 0.1.0 → 0.2.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 (540) hide show
  1. package/README.md +23 -1
  2. package/dist/analyze.js +2 -0
  3. package/dist/analyze.js.map +1 -1
  4. package/dist/config.d.ts +2 -2
  5. package/dist/config.js +0 -1
  6. package/dist/config.js.map +1 -1
  7. package/dist/context.d.ts +72 -0
  8. package/dist/context.js +202 -0
  9. package/dist/context.js.map +1 -0
  10. package/dist/create-prompt.d.ts +5 -0
  11. package/dist/create-prompt.js +50 -0
  12. package/dist/create-prompt.js.map +1 -0
  13. package/dist/create.d.ts +18 -1
  14. package/dist/create.js +30 -13
  15. package/dist/create.js.map +1 -1
  16. package/dist/dev.js +12 -51
  17. package/dist/dev.js.map +1 -1
  18. package/dist/gen/contracts.js +34 -13
  19. package/dist/gen/contracts.js.map +1 -1
  20. package/dist/gen/index.js +43 -3
  21. package/dist/gen/index.js.map +1 -1
  22. package/dist/gen/zod-code.d.ts +1 -1
  23. package/dist/gen/zod-code.js +3 -1
  24. package/dist/gen/zod-code.js.map +1 -1
  25. package/dist/graph-command.d.ts +11 -0
  26. package/dist/graph-command.js +67 -0
  27. package/dist/graph-command.js.map +1 -0
  28. package/dist/graph-model.d.ts +21 -0
  29. package/dist/graph-model.js +101 -0
  30. package/dist/graph-model.js.map +1 -0
  31. package/dist/graph-presentation.d.ts +11 -0
  32. package/dist/graph-presentation.js +25 -0
  33. package/dist/graph-presentation.js.map +1 -0
  34. package/dist/graph-query.d.ts +171 -0
  35. package/dist/graph-query.js +242 -0
  36. package/dist/graph-query.js.map +1 -0
  37. package/dist/graph-relations.d.ts +14 -0
  38. package/dist/graph-relations.js +142 -0
  39. package/dist/graph-relations.js.map +1 -0
  40. package/dist/graph-search.d.ts +21 -0
  41. package/dist/graph-search.js +63 -0
  42. package/dist/graph-search.js.map +1 -0
  43. package/dist/graph-snapshot.d.ts +2 -4
  44. package/dist/graph-snapshot.js +2 -19
  45. package/dist/graph-snapshot.js.map +1 -1
  46. package/dist/graph.js +19 -1
  47. package/dist/graph.js.map +1 -1
  48. package/dist/impact.d.ts +6 -0
  49. package/dist/impact.js +28 -16
  50. package/dist/impact.js.map +1 -1
  51. package/dist/index.d.ts +5 -1
  52. package/dist/index.js +5 -1
  53. package/dist/index.js.map +1 -1
  54. package/dist/init.d.ts +6 -0
  55. package/dist/init.js +131 -0
  56. package/dist/init.js.map +1 -0
  57. package/dist/main.js +77 -32
  58. package/dist/main.js.map +1 -1
  59. package/dist/migrate.d.ts +3 -6
  60. package/dist/migrate.js +14 -53
  61. package/dist/migrate.js.map +1 -1
  62. package/dist/ontology.d.ts +3 -1
  63. package/dist/ontology.js +13 -3
  64. package/dist/ontology.js.map +1 -1
  65. package/dist/project-structure.d.ts +8 -0
  66. package/dist/project-structure.js +94 -0
  67. package/dist/project-structure.js.map +1 -0
  68. package/dist/static-check.js +1 -4
  69. package/dist/static-check.js.map +1 -1
  70. package/dist/sync.js +82 -10
  71. package/dist/sync.js.map +1 -1
  72. package/graph-ui/app.js +46 -8
  73. package/graph-ui/graph-model.js +100 -0
  74. package/graph-ui/graph-relations.js +141 -0
  75. package/graph-ui/graph-search.js +62 -0
  76. package/graph-ui/model.js +19 -77
  77. package/graph-ui/relations.js +2 -125
  78. package/graph-ui/structure.js +4 -1
  79. package/graph-ui/style.css +21 -0
  80. package/package.json +8 -8
  81. package/templates/app/.env.example +58 -14
  82. package/templates/app/AGENTS.md +95 -12
  83. package/templates/app/Dockerfile +2 -0
  84. package/templates/app/README.md +46 -16
  85. package/templates/app/_gitignore +8 -0
  86. package/templates/app/apps/api/package.json +2 -2
  87. package/templates/app/apps/web/src/app/app.tsx +18 -0
  88. package/templates/app/apps/web/src/app/workspace-layout.tsx +70 -0
  89. package/templates/app/apps/web/src/main.tsx +4 -543
  90. package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +20 -0
  91. package/templates/app/apps/web/src/modules/approval/index.ts +2 -0
  92. package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +54 -0
  93. package/templates/app/apps/web/src/modules/auth/index.ts +1 -0
  94. package/templates/app/apps/web/src/modules/auth/pages/login-page.tsx +59 -0
  95. package/templates/app/apps/web/src/modules/inventory/index.ts +1 -0
  96. package/templates/app/apps/web/src/modules/inventory/pages/inventory-page.tsx +74 -0
  97. package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +60 -0
  98. package/templates/app/apps/web/src/modules/purchase/index.ts +2 -0
  99. package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +210 -0
  100. package/templates/app/apps/web/src/shared/hooks/use-command.ts +29 -0
  101. package/templates/app/apps/web/src/shared/lib/error-message.ts +15 -0
  102. package/templates/app/apps/web/src/shared/ui/notice.tsx +13 -0
  103. package/templates/app/apps/web/src/shared/ui/page-heading.tsx +28 -0
  104. package/templates/app/apps/web/vite.config.ts +3 -0
  105. package/templates/app/cage.config.json +1 -1
  106. package/templates/app/docs/ai/README.md +77 -0
  107. package/templates/app/docs/ai/architecture.md +122 -0
  108. package/templates/app/docs/ai/ontology-context.md +117 -0
  109. package/templates/app/docs/ai/primitives.md +249 -0
  110. package/templates/app/docs/ai/project-structure.md +123 -0
  111. package/templates/app/docs/ai/purchase-example.md +97 -0
  112. package/templates/app/docs/ai/recipes.md +81 -0
  113. package/templates/app/docs/ai/relations.md +160 -0
  114. package/templates/app/docs/ai/transfers.md +92 -0
  115. package/templates/app/docs/ai/troubleshooting.md +60 -0
  116. package/templates/app/docs/ai/workflow.md +139 -0
  117. package/templates/app/docs/errors/CAGE-APPR-001.md +47 -0
  118. package/templates/app/docs/errors/CAGE-APPR-002.md +47 -0
  119. package/templates/app/docs/errors/CAGE-APPR-003.md +47 -0
  120. package/templates/app/docs/errors/CAGE-APPR-004.md +47 -0
  121. package/templates/app/docs/errors/CAGE-APPR-005.md +47 -0
  122. package/templates/app/docs/errors/CAGE-APPR-006.md +47 -0
  123. package/templates/app/docs/errors/CAGE-APPR-007.md +50 -0
  124. package/templates/app/docs/errors/CAGE-APPR-010.md +48 -0
  125. package/templates/app/docs/errors/CAGE-AUTH-001.md +47 -0
  126. package/templates/app/docs/errors/CAGE-BUDGET-001.md +47 -0
  127. package/templates/app/docs/errors/CAGE-BUDGET-002.md +47 -0
  128. package/templates/app/docs/errors/CAGE-BUDGET-003.md +47 -0
  129. package/templates/app/docs/errors/CAGE-CMD-002.md +47 -0
  130. package/templates/app/docs/errors/CAGE-CMD-003.md +47 -0
  131. package/templates/app/docs/errors/CAGE-CONN-001.md +47 -0
  132. package/templates/app/docs/errors/CAGE-CONN-002.md +47 -0
  133. package/templates/app/docs/errors/CAGE-CONN-003.md +47 -0
  134. package/templates/app/docs/errors/CAGE-CONN-004.md +47 -0
  135. package/templates/app/docs/errors/CAGE-CONN-005.md +47 -0
  136. package/templates/app/docs/errors/CAGE-CONN-006.md +47 -0
  137. package/templates/app/docs/errors/CAGE-CONN-007.md +47 -0
  138. package/templates/app/docs/errors/CAGE-CONN-008.md +47 -0
  139. package/templates/app/docs/errors/CAGE-CONN-009.md +47 -0
  140. package/templates/app/docs/errors/CAGE-CONN-010.md +47 -0
  141. package/templates/app/docs/errors/CAGE-CONN-011.md +47 -0
  142. package/templates/app/docs/errors/CAGE-CONN-012.md +47 -0
  143. package/templates/app/docs/errors/CAGE-CREATE-001.md +52 -0
  144. package/templates/app/docs/errors/CAGE-DECL-001.md +48 -0
  145. package/templates/app/docs/errors/CAGE-DECL-002.md +47 -0
  146. package/templates/app/docs/errors/CAGE-DECL-003.md +47 -0
  147. package/templates/app/docs/errors/CAGE-DECL-004.md +47 -0
  148. package/templates/app/docs/errors/CAGE-DEP-001.md +48 -0
  149. package/templates/app/docs/errors/CAGE-DEP-002.md +47 -0
  150. package/templates/app/docs/errors/CAGE-DEP-003.md +47 -0
  151. package/templates/app/docs/errors/CAGE-DEP-004.md +47 -0
  152. package/templates/app/docs/errors/CAGE-DEP-005.md +47 -0
  153. package/templates/app/docs/errors/CAGE-DLQ-001.md +48 -0
  154. package/templates/app/docs/errors/CAGE-ENV-001.md +52 -0
  155. package/templates/app/docs/errors/CAGE-EVENT-001.md +47 -0
  156. package/templates/app/docs/errors/CAGE-EVENT-002.md +50 -0
  157. package/templates/app/docs/errors/CAGE-FIELD-001.md +47 -0
  158. package/templates/app/docs/errors/CAGE-FIELD-002.md +47 -0
  159. package/templates/app/docs/errors/CAGE-FIELD-003.md +47 -0
  160. package/templates/app/docs/errors/CAGE-FIELD-004.md +47 -0
  161. package/templates/app/docs/errors/CAGE-GEN-001.md +48 -0
  162. package/templates/app/docs/errors/CAGE-GEN-002.md +47 -0
  163. package/templates/app/docs/errors/CAGE-GRANT-001.md +49 -0
  164. package/templates/app/docs/errors/CAGE-GRANT-002.md +48 -0
  165. package/templates/app/docs/errors/CAGE-GRANT-003.md +47 -0
  166. package/templates/app/docs/errors/CAGE-IMPACT-001.md +48 -0
  167. package/templates/app/docs/errors/CAGE-INIT-001.md +47 -0
  168. package/templates/app/docs/errors/CAGE-INIT-002.md +49 -0
  169. package/templates/app/docs/errors/CAGE-INIT-003.md +48 -0
  170. package/templates/app/docs/errors/CAGE-INIT-004.md +48 -0
  171. package/templates/app/docs/errors/CAGE-JOB-001.md +47 -0
  172. package/templates/app/docs/errors/CAGE-JOB-002.md +47 -0
  173. package/templates/app/docs/errors/CAGE-JOB-003.md +47 -0
  174. package/templates/app/docs/errors/CAGE-LINT-001.md +47 -0
  175. package/templates/app/docs/errors/CAGE-MIG-001.md +47 -0
  176. package/templates/app/docs/errors/CAGE-MIG-002.md +47 -0
  177. package/templates/app/docs/errors/CAGE-MIG-003.md +5 -0
  178. package/templates/app/docs/errors/CAGE-MIG-004.md +5 -0
  179. package/templates/app/docs/errors/CAGE-MIG-005.md +47 -0
  180. package/templates/app/docs/errors/CAGE-NAME-001.md +47 -0
  181. package/templates/app/docs/errors/CAGE-NAME-002.md +47 -0
  182. package/templates/app/docs/errors/CAGE-NAME-003.md +47 -0
  183. package/templates/app/docs/errors/CAGE-NAME-004.md +47 -0
  184. package/templates/app/docs/errors/CAGE-NAME-005.md +47 -0
  185. package/templates/app/docs/errors/CAGE-NAME-006.md +47 -0
  186. package/templates/app/docs/errors/CAGE-ORG-001.md +49 -0
  187. package/templates/app/docs/errors/CAGE-PAIR-002.md +50 -0
  188. package/templates/app/docs/errors/CAGE-QUERY-001.md +47 -0
  189. package/templates/app/docs/errors/CAGE-QUERY-002.md +47 -0
  190. package/templates/app/docs/errors/CAGE-QUERY-003.md +49 -0
  191. package/templates/app/docs/errors/CAGE-REACT-001.md +47 -0
  192. package/templates/app/docs/errors/CAGE-REACT-002.md +47 -0
  193. package/templates/app/docs/errors/CAGE-READS-001.md +47 -0
  194. package/templates/app/docs/errors/CAGE-REG-001.md +53 -0
  195. package/templates/app/docs/errors/CAGE-REG-002.md +48 -0
  196. package/templates/app/docs/errors/CAGE-REG-003.md +47 -0
  197. package/templates/app/docs/errors/CAGE-REL-001.md +64 -0
  198. package/templates/app/docs/errors/CAGE-ROUTE-001.md +47 -0
  199. package/templates/app/docs/errors/CAGE-SCOPE-001.md +47 -0
  200. package/templates/app/docs/errors/CAGE-SCOPE-002.md +47 -0
  201. package/templates/app/docs/errors/CAGE-SECRET-001.md +48 -0
  202. package/templates/app/docs/errors/CAGE-SECRET-002.md +47 -0
  203. package/templates/app/docs/errors/CAGE-SENS-001.md +48 -0
  204. package/templates/app/docs/errors/CAGE-SQL-001.md +47 -0
  205. package/templates/app/docs/errors/CAGE-SQL-002.md +47 -0
  206. package/templates/app/docs/errors/CAGE-STATE-001.md +49 -0
  207. package/templates/app/docs/errors/CAGE-STATE-002.md +47 -0
  208. package/templates/app/docs/errors/CAGE-STATE-003.md +47 -0
  209. package/templates/app/docs/errors/CAGE-STATE-004.md +47 -0
  210. package/templates/app/docs/errors/CAGE-STATE-005.md +48 -0
  211. package/templates/app/docs/errors/CAGE-STATE-010.md +48 -0
  212. package/templates/app/docs/errors/CAGE-STATE-011.md +48 -0
  213. package/templates/app/docs/errors/CAGE-STRUCT-001.md +51 -0
  214. package/templates/app/docs/errors/CAGE-STRUCT-002.md +51 -0
  215. package/templates/app/docs/errors/CAGE-SYNC-001.md +48 -0
  216. package/templates/app/docs/errors/CAGE-TEST-001.md +47 -0
  217. package/templates/app/docs/errors/CAGE-UNSAFE-001.md +48 -0
  218. package/templates/app/docs/errors/CAGE-UPGRADE-001.md +48 -0
  219. package/templates/app/docs/errors/README.md +307 -0
  220. package/templates/app/docs/errors/runtime.md +50 -0
  221. package/templates/app/package.json +4 -7
  222. package/templates/app/packages/contracts/package.json +1 -1
  223. package/templates/app/packages/contracts/src/{dept.ts → aggregates/dept/index.ts} +3 -3
  224. package/templates/app/packages/contracts/src/{inventory.ts → aggregates/inventory/index.ts} +3 -3
  225. package/templates/app/packages/contracts/src/{inventory-lock.ts → aggregates/inventory-lock/index.ts} +3 -3
  226. package/templates/app/packages/contracts/src/{purchase-order.ts → aggregates/purchase-order/index.ts} +3 -3
  227. package/templates/app/packages/contracts/src/{role.ts → aggregates/role/index.ts} +3 -3
  228. package/templates/app/packages/contracts/src/{user.ts → aggregates/user/index.ts} +3 -3
  229. package/templates/app/packages/contracts/src/index.ts +14 -14
  230. package/templates/app/packages/domain/package.json +4 -4
  231. package/templates/app/packages/domain/src/index.ts +4 -7
  232. package/templates/app/packages/domain/src/{erp.ts → integrations/erp.ts} +1 -1
  233. package/templates/app/packages/domain/src/modules/inventory/index.ts +2 -0
  234. package/templates/app/packages/domain/src/modules/purchase/index.ts +6 -0
  235. package/templates/app/packages/domain/src/{purchase-order.test.ts → modules/purchase/purchase-order.test.ts} +2 -2
  236. package/templates/app/packages/domain/src/{purchase-order.ts → modules/purchase/purchase-order.ts} +1 -1
  237. package/templates/app/packages/domain/src/{reactions.ts → workflows/purchase-inventory.ts} +2 -2
  238. package/templates/blank/.dockerignore +10 -0
  239. package/templates/blank/.env.example +61 -0
  240. package/templates/blank/.github/workflows/cage.yml +44 -0
  241. package/templates/blank/AGENTS.md +96 -0
  242. package/templates/blank/Dockerfile +25 -0
  243. package/templates/blank/README.md +37 -0
  244. package/templates/blank/_gitignore +8 -0
  245. package/templates/blank/apps/api/package.json +15 -0
  246. package/templates/blank/apps/api/src/main.ts +11 -0
  247. package/templates/blank/apps/api/src/worker.ts +8 -0
  248. package/templates/blank/apps/api/tsconfig.json +5 -0
  249. package/templates/blank/apps/web/index.html +13 -0
  250. package/templates/blank/apps/web/package.json +22 -0
  251. package/templates/blank/apps/web/src/app/app.tsx +44 -0
  252. package/templates/blank/apps/web/src/app/styles/global.css +516 -0
  253. package/templates/blank/apps/web/src/app/workspace-layout.tsx +70 -0
  254. package/templates/blank/apps/web/src/main.tsx +13 -0
  255. package/templates/blank/apps/web/src/modules/auth/index.ts +1 -0
  256. package/templates/blank/apps/web/src/modules/auth/pages/login-page.tsx +87 -0
  257. package/templates/blank/apps/web/src/modules/workspace/index.ts +1 -0
  258. package/templates/blank/apps/web/src/modules/workspace/pages/home-page.tsx +45 -0
  259. package/templates/blank/apps/web/src/shared/lib/error-message.ts +11 -0
  260. package/templates/blank/apps/web/src/shared/ui/brand.tsx +11 -0
  261. package/templates/blank/apps/web/tsconfig.json +11 -0
  262. package/templates/blank/apps/web/vite.config.ts +19 -0
  263. package/templates/blank/cage.config.json +14 -0
  264. package/templates/blank/db/migrations/0001_init.sql +63 -0
  265. package/templates/blank/docs/ai/README.md +77 -0
  266. package/templates/blank/docs/ai/architecture.md +122 -0
  267. package/templates/blank/docs/ai/ontology-context.md +117 -0
  268. package/templates/blank/docs/ai/primitives.md +249 -0
  269. package/templates/blank/docs/ai/project-structure.md +123 -0
  270. package/templates/blank/docs/ai/purchase-example.md +97 -0
  271. package/templates/blank/docs/ai/recipes.md +81 -0
  272. package/templates/blank/docs/ai/relations.md +160 -0
  273. package/templates/blank/docs/ai/transfers.md +92 -0
  274. package/templates/blank/docs/ai/troubleshooting.md +60 -0
  275. package/templates/blank/docs/ai/workflow.md +139 -0
  276. package/templates/blank/docs/errors/CAGE-APPR-001.md +47 -0
  277. package/templates/blank/docs/errors/CAGE-APPR-002.md +47 -0
  278. package/templates/blank/docs/errors/CAGE-APPR-003.md +47 -0
  279. package/templates/blank/docs/errors/CAGE-APPR-004.md +47 -0
  280. package/templates/blank/docs/errors/CAGE-APPR-005.md +47 -0
  281. package/templates/blank/docs/errors/CAGE-APPR-006.md +47 -0
  282. package/templates/blank/docs/errors/CAGE-APPR-007.md +50 -0
  283. package/templates/blank/docs/errors/CAGE-APPR-010.md +48 -0
  284. package/templates/blank/docs/errors/CAGE-AUTH-001.md +47 -0
  285. package/templates/blank/docs/errors/CAGE-BUDGET-001.md +47 -0
  286. package/templates/blank/docs/errors/CAGE-BUDGET-002.md +47 -0
  287. package/templates/blank/docs/errors/CAGE-BUDGET-003.md +47 -0
  288. package/templates/blank/docs/errors/CAGE-CMD-002.md +47 -0
  289. package/templates/blank/docs/errors/CAGE-CMD-003.md +47 -0
  290. package/templates/blank/docs/errors/CAGE-CONN-001.md +47 -0
  291. package/templates/blank/docs/errors/CAGE-CONN-002.md +47 -0
  292. package/templates/blank/docs/errors/CAGE-CONN-003.md +47 -0
  293. package/templates/blank/docs/errors/CAGE-CONN-004.md +47 -0
  294. package/templates/blank/docs/errors/CAGE-CONN-005.md +47 -0
  295. package/templates/blank/docs/errors/CAGE-CONN-006.md +47 -0
  296. package/templates/blank/docs/errors/CAGE-CONN-007.md +47 -0
  297. package/templates/blank/docs/errors/CAGE-CONN-008.md +47 -0
  298. package/templates/blank/docs/errors/CAGE-CONN-009.md +47 -0
  299. package/templates/blank/docs/errors/CAGE-CONN-010.md +47 -0
  300. package/templates/blank/docs/errors/CAGE-CONN-011.md +47 -0
  301. package/templates/blank/docs/errors/CAGE-CONN-012.md +47 -0
  302. package/templates/blank/docs/errors/CAGE-CREATE-001.md +52 -0
  303. package/templates/blank/docs/errors/CAGE-DECL-001.md +48 -0
  304. package/templates/blank/docs/errors/CAGE-DECL-002.md +47 -0
  305. package/templates/blank/docs/errors/CAGE-DECL-003.md +47 -0
  306. package/templates/blank/docs/errors/CAGE-DECL-004.md +47 -0
  307. package/templates/blank/docs/errors/CAGE-DEP-001.md +48 -0
  308. package/templates/blank/docs/errors/CAGE-DEP-002.md +47 -0
  309. package/templates/blank/docs/errors/CAGE-DEP-003.md +47 -0
  310. package/templates/blank/docs/errors/CAGE-DEP-004.md +47 -0
  311. package/templates/blank/docs/errors/CAGE-DEP-005.md +47 -0
  312. package/templates/blank/docs/errors/CAGE-DLQ-001.md +48 -0
  313. package/templates/blank/docs/errors/CAGE-ENV-001.md +52 -0
  314. package/templates/blank/docs/errors/CAGE-EVENT-001.md +47 -0
  315. package/templates/blank/docs/errors/CAGE-EVENT-002.md +50 -0
  316. package/templates/blank/docs/errors/CAGE-FIELD-001.md +47 -0
  317. package/templates/blank/docs/errors/CAGE-FIELD-002.md +47 -0
  318. package/templates/blank/docs/errors/CAGE-FIELD-003.md +47 -0
  319. package/templates/blank/docs/errors/CAGE-FIELD-004.md +47 -0
  320. package/templates/blank/docs/errors/CAGE-GEN-001.md +48 -0
  321. package/templates/blank/docs/errors/CAGE-GEN-002.md +47 -0
  322. package/templates/blank/docs/errors/CAGE-GRANT-001.md +49 -0
  323. package/templates/blank/docs/errors/CAGE-GRANT-002.md +48 -0
  324. package/templates/blank/docs/errors/CAGE-GRANT-003.md +47 -0
  325. package/templates/blank/docs/errors/CAGE-IMPACT-001.md +48 -0
  326. package/templates/blank/docs/errors/CAGE-INIT-001.md +47 -0
  327. package/templates/blank/docs/errors/CAGE-INIT-002.md +49 -0
  328. package/templates/blank/docs/errors/CAGE-INIT-003.md +48 -0
  329. package/templates/blank/docs/errors/CAGE-INIT-004.md +48 -0
  330. package/templates/blank/docs/errors/CAGE-JOB-001.md +47 -0
  331. package/templates/blank/docs/errors/CAGE-JOB-002.md +47 -0
  332. package/templates/blank/docs/errors/CAGE-JOB-003.md +47 -0
  333. package/templates/blank/docs/errors/CAGE-LINT-001.md +47 -0
  334. package/templates/blank/docs/errors/CAGE-MIG-001.md +47 -0
  335. package/templates/blank/docs/errors/CAGE-MIG-002.md +47 -0
  336. package/templates/blank/docs/errors/CAGE-MIG-003.md +5 -0
  337. package/templates/blank/docs/errors/CAGE-MIG-004.md +5 -0
  338. package/templates/blank/docs/errors/CAGE-MIG-005.md +47 -0
  339. package/templates/blank/docs/errors/CAGE-NAME-001.md +47 -0
  340. package/templates/blank/docs/errors/CAGE-NAME-002.md +47 -0
  341. package/templates/blank/docs/errors/CAGE-NAME-003.md +47 -0
  342. package/templates/blank/docs/errors/CAGE-NAME-004.md +47 -0
  343. package/templates/blank/docs/errors/CAGE-NAME-005.md +47 -0
  344. package/templates/blank/docs/errors/CAGE-NAME-006.md +47 -0
  345. package/templates/blank/docs/errors/CAGE-ORG-001.md +49 -0
  346. package/templates/blank/docs/errors/CAGE-PAIR-002.md +50 -0
  347. package/templates/blank/docs/errors/CAGE-QUERY-001.md +47 -0
  348. package/templates/blank/docs/errors/CAGE-QUERY-002.md +47 -0
  349. package/templates/blank/docs/errors/CAGE-QUERY-003.md +49 -0
  350. package/templates/blank/docs/errors/CAGE-REACT-001.md +47 -0
  351. package/templates/blank/docs/errors/CAGE-REACT-002.md +47 -0
  352. package/templates/blank/docs/errors/CAGE-READS-001.md +47 -0
  353. package/templates/blank/docs/errors/CAGE-REG-001.md +53 -0
  354. package/templates/blank/docs/errors/CAGE-REG-002.md +48 -0
  355. package/templates/blank/docs/errors/CAGE-REG-003.md +47 -0
  356. package/templates/blank/docs/errors/CAGE-REL-001.md +64 -0
  357. package/templates/blank/docs/errors/CAGE-ROUTE-001.md +47 -0
  358. package/templates/blank/docs/errors/CAGE-SCOPE-001.md +47 -0
  359. package/templates/blank/docs/errors/CAGE-SCOPE-002.md +47 -0
  360. package/templates/blank/docs/errors/CAGE-SECRET-001.md +48 -0
  361. package/templates/blank/docs/errors/CAGE-SECRET-002.md +47 -0
  362. package/templates/blank/docs/errors/CAGE-SENS-001.md +48 -0
  363. package/templates/blank/docs/errors/CAGE-SQL-001.md +47 -0
  364. package/templates/blank/docs/errors/CAGE-SQL-002.md +47 -0
  365. package/templates/blank/docs/errors/CAGE-STATE-001.md +49 -0
  366. package/templates/blank/docs/errors/CAGE-STATE-002.md +47 -0
  367. package/templates/blank/docs/errors/CAGE-STATE-003.md +47 -0
  368. package/templates/blank/docs/errors/CAGE-STATE-004.md +47 -0
  369. package/templates/blank/docs/errors/CAGE-STATE-005.md +48 -0
  370. package/templates/blank/docs/errors/CAGE-STATE-010.md +48 -0
  371. package/templates/blank/docs/errors/CAGE-STATE-011.md +48 -0
  372. package/templates/blank/docs/errors/CAGE-STRUCT-001.md +51 -0
  373. package/templates/blank/docs/errors/CAGE-STRUCT-002.md +51 -0
  374. package/templates/blank/docs/errors/CAGE-SYNC-001.md +48 -0
  375. package/templates/blank/docs/errors/CAGE-TEST-001.md +47 -0
  376. package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +48 -0
  377. package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +48 -0
  378. package/templates/blank/docs/errors/README.md +307 -0
  379. package/templates/blank/docs/errors/runtime.md +50 -0
  380. package/templates/blank/docs/examples/purchase-admin/README.md +5 -0
  381. package/templates/blank/docs/examples/purchase-admin/apps/api/src/main.ts +10 -0
  382. package/templates/blank/docs/examples/purchase-admin/apps/api/src/worker.ts +14 -0
  383. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/app.tsx +18 -0
  384. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +663 -0
  385. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/workspace-layout.tsx +70 -0
  386. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +13 -0
  387. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +20 -0
  388. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/index.ts +2 -0
  389. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +54 -0
  390. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/index.ts +1 -0
  391. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/auth/pages/login-page.tsx +59 -0
  392. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/index.ts +1 -0
  393. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/inventory/pages/inventory-page.tsx +74 -0
  394. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +60 -0
  395. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/index.ts +2 -0
  396. package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +210 -0
  397. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/hooks/use-command.ts +29 -0
  398. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +15 -0
  399. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/notice.tsx +13 -0
  400. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/page-heading.tsx +28 -0
  401. package/templates/blank/docs/examples/purchase-admin/cage.config.json +114 -0
  402. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/dept/index.ts +74 -0
  403. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory/index.ts +57 -0
  404. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/inventory-lock/index.ts +52 -0
  405. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +72 -0
  406. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +85 -0
  407. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +89 -0
  408. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +81 -0
  409. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/queries/index.ts +17 -0
  410. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/index.ts +6 -0
  411. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/integrations/erp.ts +36 -0
  412. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/index.ts +2 -0
  413. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/inventory/inventory.ts +43 -0
  414. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +18 -0
  415. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +6 -0
  416. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/jobs.ts +13 -0
  417. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +137 -0
  418. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +122 -0
  419. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/queries.ts +10 -0
  420. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-inventory.ts +24 -0
  421. package/templates/blank/package.json +22 -0
  422. package/templates/blank/packages/contracts/package.json +16 -0
  423. package/templates/blank/packages/contracts/src/aggregates/dept/index.ts +74 -0
  424. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +85 -0
  425. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +89 -0
  426. package/templates/blank/packages/contracts/src/index.ts +55 -0
  427. package/templates/blank/packages/contracts/src/queries/index.ts +13 -0
  428. package/templates/blank/packages/contracts/tsconfig.json +5 -0
  429. package/templates/blank/packages/domain/package.json +23 -0
  430. package/templates/blank/packages/domain/src/index.ts +4 -0
  431. package/templates/blank/packages/domain/tsconfig.json +5 -0
  432. package/templates/blank/pnpm-workspace.yaml +10 -0
  433. package/templates/blank/renovate.json +5 -0
  434. package/templates/blank/tsconfig.base.json +8 -0
  435. package/dist/database.d.ts +0 -5
  436. package/dist/database.js +0 -70
  437. package/dist/database.js.map +0 -1
  438. package/managed/errors/CAGE-APPR-001.md +0 -32
  439. package/managed/errors/CAGE-APPR-002.md +0 -32
  440. package/managed/errors/CAGE-APPR-003.md +0 -32
  441. package/managed/errors/CAGE-APPR-004.md +0 -32
  442. package/managed/errors/CAGE-APPR-005.md +0 -32
  443. package/managed/errors/CAGE-APPR-006.md +0 -32
  444. package/managed/errors/CAGE-APPR-007.md +0 -32
  445. package/managed/errors/CAGE-APPR-010.md +0 -32
  446. package/managed/errors/CAGE-AUTH-001.md +0 -32
  447. package/managed/errors/CAGE-BUDGET-001.md +0 -32
  448. package/managed/errors/CAGE-BUDGET-002.md +0 -32
  449. package/managed/errors/CAGE-BUDGET-003.md +0 -32
  450. package/managed/errors/CAGE-CMD-002.md +0 -32
  451. package/managed/errors/CAGE-CMD-003.md +0 -34
  452. package/managed/errors/CAGE-CONN-001.md +0 -32
  453. package/managed/errors/CAGE-CONN-002.md +0 -32
  454. package/managed/errors/CAGE-CONN-003.md +0 -32
  455. package/managed/errors/CAGE-CONN-004.md +0 -32
  456. package/managed/errors/CAGE-CONN-005.md +0 -32
  457. package/managed/errors/CAGE-CONN-006.md +0 -32
  458. package/managed/errors/CAGE-CONN-007.md +0 -32
  459. package/managed/errors/CAGE-CONN-008.md +0 -32
  460. package/managed/errors/CAGE-CONN-009.md +0 -32
  461. package/managed/errors/CAGE-CONN-010.md +0 -32
  462. package/managed/errors/CAGE-CONN-011.md +0 -32
  463. package/managed/errors/CAGE-CONN-012.md +0 -32
  464. package/managed/errors/CAGE-CREATE-001.md +0 -32
  465. package/managed/errors/CAGE-DECL-001.md +0 -34
  466. package/managed/errors/CAGE-DECL-002.md +0 -34
  467. package/managed/errors/CAGE-DECL-003.md +0 -34
  468. package/managed/errors/CAGE-DECL-004.md +0 -34
  469. package/managed/errors/CAGE-DEP-001.md +0 -34
  470. package/managed/errors/CAGE-DEP-002.md +0 -34
  471. package/managed/errors/CAGE-DEP-003.md +0 -34
  472. package/managed/errors/CAGE-DEP-004.md +0 -34
  473. package/managed/errors/CAGE-DEP-005.md +0 -34
  474. package/managed/errors/CAGE-DLQ-001.md +0 -32
  475. package/managed/errors/CAGE-ENV-001.md +0 -32
  476. package/managed/errors/CAGE-EVENT-001.md +0 -32
  477. package/managed/errors/CAGE-EVENT-002.md +0 -32
  478. package/managed/errors/CAGE-FIELD-001.md +0 -32
  479. package/managed/errors/CAGE-FIELD-002.md +0 -32
  480. package/managed/errors/CAGE-FIELD-003.md +0 -32
  481. package/managed/errors/CAGE-FIELD-004.md +0 -32
  482. package/managed/errors/CAGE-GEN-001.md +0 -33
  483. package/managed/errors/CAGE-GEN-002.md +0 -33
  484. package/managed/errors/CAGE-GRANT-001.md +0 -32
  485. package/managed/errors/CAGE-GRANT-002.md +0 -32
  486. package/managed/errors/CAGE-GRANT-003.md +0 -32
  487. package/managed/errors/CAGE-IMPACT-001.md +0 -32
  488. package/managed/errors/CAGE-JOB-001.md +0 -32
  489. package/managed/errors/CAGE-JOB-002.md +0 -32
  490. package/managed/errors/CAGE-JOB-003.md +0 -32
  491. package/managed/errors/CAGE-LINT-001.md +0 -32
  492. package/managed/errors/CAGE-MIG-001.md +0 -32
  493. package/managed/errors/CAGE-MIG-002.md +0 -32
  494. package/managed/errors/CAGE-MIG-003.md +0 -32
  495. package/managed/errors/CAGE-MIG-004.md +0 -32
  496. package/managed/errors/CAGE-MIG-005.md +0 -32
  497. package/managed/errors/CAGE-NAME-001.md +0 -32
  498. package/managed/errors/CAGE-NAME-002.md +0 -32
  499. package/managed/errors/CAGE-NAME-003.md +0 -32
  500. package/managed/errors/CAGE-NAME-004.md +0 -32
  501. package/managed/errors/CAGE-NAME-005.md +0 -32
  502. package/managed/errors/CAGE-NAME-006.md +0 -32
  503. package/managed/errors/CAGE-ORG-001.md +0 -32
  504. package/managed/errors/CAGE-PAIR-002.md +0 -33
  505. package/managed/errors/CAGE-QUERY-001.md +0 -32
  506. package/managed/errors/CAGE-QUERY-002.md +0 -32
  507. package/managed/errors/CAGE-QUERY-003.md +0 -32
  508. package/managed/errors/CAGE-REACT-001.md +0 -32
  509. package/managed/errors/CAGE-REACT-002.md +0 -32
  510. package/managed/errors/CAGE-READS-001.md +0 -32
  511. package/managed/errors/CAGE-REG-001.md +0 -32
  512. package/managed/errors/CAGE-REG-002.md +0 -32
  513. package/managed/errors/CAGE-REG-003.md +0 -32
  514. package/managed/errors/CAGE-ROUTE-001.md +0 -32
  515. package/managed/errors/CAGE-SCOPE-001.md +0 -32
  516. package/managed/errors/CAGE-SCOPE-002.md +0 -32
  517. package/managed/errors/CAGE-SECRET-001.md +0 -32
  518. package/managed/errors/CAGE-SECRET-002.md +0 -32
  519. package/managed/errors/CAGE-SENS-001.md +0 -32
  520. package/managed/errors/CAGE-SQL-001.md +0 -32
  521. package/managed/errors/CAGE-SQL-002.md +0 -32
  522. package/managed/errors/CAGE-STATE-001.md +0 -32
  523. package/managed/errors/CAGE-STATE-002.md +0 -32
  524. package/managed/errors/CAGE-STATE-003.md +0 -32
  525. package/managed/errors/CAGE-STATE-004.md +0 -32
  526. package/managed/errors/CAGE-STATE-005.md +0 -32
  527. package/managed/errors/CAGE-STATE-010.md +0 -32
  528. package/managed/errors/CAGE-STATE-011.md +0 -32
  529. package/managed/errors/CAGE-SYNC-001.md +0 -35
  530. package/managed/errors/CAGE-TEST-001.md +0 -33
  531. package/managed/errors/CAGE-UNSAFE-001.md +0 -32
  532. package/managed/errors/CAGE-UPGRADE-001.md +0 -32
  533. package/managed/errors/README.md +0 -99
  534. package/templates/app/compose.yaml +0 -39
  535. /package/templates/app/apps/web/src/{style.css → app/styles/global.css} +0 -0
  536. /package/templates/app/packages/contracts/src/{queries.ts → queries/index.ts} +0 -0
  537. /package/templates/app/packages/domain/src/{inventory.ts → modules/inventory/inventory.ts} +0 -0
  538. /package/templates/app/packages/domain/src/{approvals.ts → modules/purchase/approvals.ts} +0 -0
  539. /package/templates/app/packages/domain/src/{jobs.ts → modules/purchase/jobs.ts} +0 -0
  540. /package/templates/app/packages/domain/src/{queries.ts → modules/purchase/queries.ts} +0 -0
@@ -0,0 +1,97 @@
1
+ # 标准采购范例:从声明到业务页面
2
+
3
+ [返回学习入口](README.md) · [业务任务配方](recipes.md)
4
+
5
+ 选择 `--template purchase` 创建的是可运行的采购应用,下面的路径相对应用根目录;选择 `--template blank` 时,同一份范例仅作为参考源码保存在 `docs/examples/purchase-admin`,下面路径相对该参考目录,它不参与当前应用的编译、注册或迁移。框架能力来自 npm 依赖。先沿一条真实链路读代码,再把需要的模式应用到自己的业务。
6
+
7
+ ## 按这个顺序读文件
8
+
9
+ | 文件(相对应用根目录) | 看懂的业务能力 | 改造时关注什么 |
10
+ | --- | --- | --- |
11
+ | `packages/domain/src/index.ts` | 哪些声明真正被注册 | 保留组织模型,导出新增业务,避免导出名冲突 |
12
+ | `packages/domain/src/modules/inventory/inventory.ts` | 最简字段、创建命令、资源锁聚合 | scope.none 不等于无需授权;创建与释放都是命令 |
13
+ | `packages/domain/src/modules/purchase/purchase-order.ts` | 单据生命周期、金额、明细、约束、事件 | 哪些字段由输入提供,哪些由内核维护;每个状态的允许操作 |
14
+ | `packages/domain/src/workflows/purchase-inventory.ts` | 同事务建锁、异步释放 | acquires/releases 的配对及 map 返回 null 的原因 |
15
+ | `packages/domain/src/modules/purchase/approvals.ts` | 启动/取消审批、条件步骤与选人 | routable、部门负责人、角色 code、结论命令 |
16
+ | `packages/domain/src/integrations/erp.ts` | 出站推单、成功回写、入站回调 | 白名单、secret 名、schema、幂等与 HMAC |
17
+ | `packages/domain/src/modules/purchase/queries.ts` | 按状态汇总 | reads、安全视图、命名参数 |
18
+ | `packages/domain/src/modules/purchase/jobs.ts` | 过期草稿取消 | reads/calls 和 job 调用身份 |
19
+ | `apps/api/src/main.ts`、`apps/api/src/worker.ts` | 如何启动框架与 worker | 保持薄装配,fake 只用于相应环境 |
20
+ | `packages/contracts/src/aggregates/purchase-order/index.ts` | 生成的前端协议与类型 | 只读它,不手改;process/external 命令不生成浏览器写方法 |
21
+ | `apps/web/src/main.tsx`、`app` | 挂载、全局 Provider、会话与导航组合 | 入口保持简洁,从模块公开入口组装页面 |
22
+ | `apps/web/src/modules/purchase`、`inventory`、`approval` | 各业务页面与交互 | 调契约、处理错误、刷新列表、展示导出任务;不互相穿透 |
23
+ | `packages/domain/src/modules/purchase/purchase-order.test.ts` | 有业务数据的成功/拒绝用例 | 越权、自审批、额度、库存锁、晚到 ERP 回执 |
24
+
25
+ ## 一次正常采购发生了什么
26
+
27
+ ```text
28
+ 采购员新建 → draft
29
+ → submit → pending_approval + OrderSubmitted(同事务提交)
30
+ → worker 消费事件 → 按 poRoute 创建经理待办
31
+ → 经理审批;金额 > 50000 时还有财务步骤
32
+ → 流程完成 → 调用 approve
33
+ → 职责分离、额度、库存检查
34
+ → approved + OrderApproved
35
+ → 同事务 lockOnApprove → InventoryLock.held
36
+ → worker 消费 OrderApproved → fake/真实 ERP 推单
37
+ → recordErpNo(external,保持当前状态)
38
+ → 收货回调 → markReceived → received + OrderReceived
39
+ → worker 释放锁 → InventoryLock.released
40
+ ```
41
+
42
+ submit 返回成功只表示单据和事件已提交,不表示待办、ERP 或其他异步工作已经完成。worker 未运行时,单据可以已经处于待审批,但没有待办。
43
+
44
+ ### 为什么分三个聚合
45
+
46
+ - `PurchaseOrder` 管单据、审批结论和采购状态。
47
+ - `Inventory` 是物料台账,提供库存检查的数据。
48
+ - `InventoryLock` 记录某张订单的资源占用,使用订单 id 作为锁 id,让释放目标明确。
49
+
50
+ 审批通过和建锁放在同一事务,是为了避免“单据已批准却没拿到资源”。ERP 网络请求放到 outbox,是因为远端调用无法与本地 PostgreSQL 共同回滚。
51
+
52
+ ### 取消为什么要带 previousStatus
53
+
54
+ 草稿或待审批取消时还没有库存锁,不应释放不存在的锁;已批准取消才需要释放。因此 `OrderCancelled` 的显式 emit 映射携带旧状态,反应通过 `previousStatus === 'approved'` 决定是否调用释放命令。
55
+
56
+ 新增撤回、作废等动作时,每一条离开已批准状态的路径都要考虑资源。仅把命令 `to` 改成新状态,可能被 `CAGE-PAIR-002` 拦下;补了标签也仍要确保真正的释放命令会执行。
57
+
58
+ ### ERP 回执为什么用 to: null
59
+
60
+ 网络应答可能晚于取消或收货到达。`recordErpNo` 允许从 approved/received/cancelled 更新单号,用 `to: null` 保持加锁时的当前状态;不会把取消单重新变成已批准。它仍受调用身份、允许状态、版本和审计约束。
61
+
62
+ 本例没有真实 ERP 取消补偿。上线时需另外设计远端撤销、退货或人工补偿,不能把“本地取消成功”当成“远端订单已撤销”。
63
+
64
+ ## 首次体验所需业务配置
65
+
66
+ 以下体验需要采购模板;空白模板不会出现采购页面和审批配置。先按[开发步骤](workflow.md)准备已有数据库并启动 API、worker、Web。首次管理员凭证按 API 提示获取,不把凭证写到仓库。以下是业务配置,不是需要写进框架内核的逻辑。
67
+
68
+ | 对象 | 配置要点 |
69
+ | --- | --- |
70
+ | 采购员 | 创建/查看/提交/取消采购单等所需能力,所属部门,以及采购单数据范围 |
71
+ | 经理 | `PurchaseOrder.read/approve/reject`,适当数据范围,`approve` 的 `ceiling` 额度参数 |
72
+ | 部门 | 将采购员所属部门的负责人设为经理;不能为空 |
73
+ | 财务 | 角色 **code** 为 `finance`,给实际用户分配;配置审批所需权限、范围及额度 |
74
+ | 库存台账 | 预先登记与采购明细相同的 sku,数量满足演示检查 |
75
+ | 连接器环境 | fake 阶段使用 worker 注册的 fake;真实环境另配 host 对应密钥及回调签名密钥 |
76
+
77
+ 经理与提单人必须是不同用户。配置“负责人”决定选谁;配置角色能力与范围决定此人能否完成结论命令,两者都需要。金额 **大于** 50,000 才追加财务步骤,恰好 50,000 不满足 gt 条件。
78
+
79
+ ## 页面如何使用框架
80
+
81
+ 采购页和库存页通过 `@app/contracts` 获取方法、快照类型和查询 hooks。命令成功后刷新相应列表;失败时按 code/domainCode 展示原因。只有允许 user 调用的命令才生成页面方法,因此审批通过应使用待办接口。
82
+
83
+ `apps/web` 中对字段和按钮的中文名称属于业务文案。图谱名称来自 `cage.config.json` 的 `graph.labels`,比如 `command:PurchaseOrder.submit` 对应“提交审批”。替换业务时别忘了更新这些名称,不要改英文能力标识来做翻译。
84
+
85
+ 导出按钮是调用通用异步接口的一个例子;框架不规定所有业务都要放相同的按钮。导入、导出、任务列表与错误报告应按客户页面需要接入,参考[开发步骤](workflow.md)中的客户端方法。
86
+
87
+ ## 把示例改成自己的业务
88
+
89
+ 先按[目录规范](project-structure.md)新增自己的领域模块和前端模块,完成一条完整链路,再逐步移除不需要的采购导出、路由、反应、连接器和页面引用。`@app/domain`、`@app/contracts` 是应用内包名,可以保留;npm 框架包也不需要重命名。
90
+
91
+ 若数据库尚未投入使用,可为新应用审查和规划初始结构;只要迁移已执行或已发布,就不能通过覆盖 `0001_init.sql` 来假装采购表从未存在。用新迁移表达后续变更,并明确已有数据处理方式。
92
+
93
+ ## 本例可以证明和不能证明的事
94
+
95
+ 它演示了真实 PG 上的命令与安全读侧、审批结论约束、同事务建锁、异步释放、连接器 fake 和签名回调。`Inventory.available` 是演示台账数量,尚未实现多订单并发扣减、实物出入库和退货核算;fake ERP 不证明第三方生产联调完成。
96
+
97
+ 扩展业务时把这些边界写进实现说明,补必要的业务规则与有意义的回归用例,而不是直接给范例换标题后声称生产需求已经全部覆盖。
@@ -0,0 +1,81 @@
1
+ # 业务任务配方
2
+
3
+ [返回学习入口](README.md) · [原语示例](primitives.md) · [开发步骤](workflow.md)
4
+
5
+ 本页用于把用户的业务描述转成 Cage 项目的可执行修改。按需要读对应配方即可,不需要修改框架内部。
6
+
7
+ ## 先把需求落到一张业务表
8
+
9
+ 开始编码前,从用户需求或现有业务中确定以下信息。能从上下文确认的直接使用;真正缺失且影响实现的再询问,不要为技术细节反复让用户确认。
10
+
11
+ | 业务问题 | 落到什么声明 | 例如 |
12
+ | --- | --- | --- |
13
+ | 管理什么对象?有哪些字段? | aggregate、字段与 Zod 输入 | 报销单:金额、事由、部门、提单人、明细 |
14
+ | 有哪些阶段?谁能做什么? | states、command、invokedBy | 草稿可编辑、提交后审批、完成后不可撤回 |
15
+ | 数据属于谁/哪个部门? | 范围字段 + 角色数据范围 | 申请人看本人,经理看部门及下级 |
16
+ | 每次操作有什么硬规则? | constraints、invariants、fail | 提单人不可审批、额度上限、金额必须为正 |
17
+ | 成功后还要发生什么? | event、reaction、approval、outbound | 启动审批、登记额度占用、推送财务系统 |
18
+ | 取消/失败要撤销什么? | cancelOn、释放命令、补偿流程 | 取消待办、释放占用、远端撤销或人工补偿 |
19
+ | 页面需要看什么? | 生成的 list/get、query、业务页面 | 分页、详情、按状态汇总、导出与导入 |
20
+
21
+ ## 新增一个普通管理对象
22
+
23
+ 按[最小便笺业务](primitives.md)新增 `packages/domain/src/modules/<业务>/<对象>.ts`:字段、范围、不变量、create/update/archive 命令。采用唯一的导出名和必要的 `name` 覆盖,避免 index 的 `export *` 丢失重名符号。
24
+
25
+ 从模块 index 到 domain 入口导出后运行 check/gen,审查迁移,再在 `apps/web/src/modules/<业务>` 使用生成 contracts 做列表、表单和操作按钮,经模块公开入口接入 app。最后给角色授权并补图谱中文名。完整归属规则见[目录规范](project-structure.md)。不要手写增删改查路由、ORM 实体与 repository,也不要复制框架鉴权代码。
26
+
27
+ 完成标准:授权用户能创建和修改,范围外数据不可见,归档后的允许行为符合业务要求,生成契约和迁移一致。没有审批需求时不必添加审批;没有外部系统时不必添加连接器。
28
+
29
+ ## 增加一对多或多对多
30
+
31
+ 参考[对象关系](relations.md),在聚合的同一个 fields 中添加 `hasMany/manyToMany`。纯关联交给框架生成中间表;关系本身有职位、状态等业务属性时再建立有意义的聚合。hasMany 反查目标上的 ref,修改归属执行子对象命令;manyToMany 用所属对象的命令返回 ID 数组。
32
+
33
+ 生成后前端通过 `对象.related.字段(id, params)` 或 `use对象Related字段` 分页读取,不从普通 get 响应猜测已加载关联。给角色配置两侧 read 能力与范围;添加成员时也检查目标可见性。修改关系执行版本检查,不能把当前一页当作完整集合替换。不要创建新的业务连接池、ORM 中间实体或手写关系路由。
34
+
35
+ ## 给业务单据加审批
36
+
37
+ 1. 为单据补草稿、待审批、通过、驳回等真实状态及命令;提交时发事件。
38
+ 2. 通过/驳回使用 process 命令,根据业务添加职责分离、额度等约束。
39
+ 3. 添加 approval 绑定和 route;只将需要的字段放进 routable,取消命令发事件并配置 cancelOn。
40
+ 4. 配置实际部门负责人、角色或指定用户;给审批人配置能力、范围和额度。
41
+ 5. 页面通过待办接口操作。通过后需占用资源时用反应调用资源命令,按业务一致性选择同事务。
42
+
43
+ 完成标准:提交生成待办、正确的人审批、越权和自审批拒绝、取消能结束待办、资源配对完整。任意流程设计器、动态 JS 选人、转签/加签不属于当前现成能力,需要明确扩展范围。
44
+
45
+ ## 增加报表或复杂列表
46
+
47
+ 普通分页和详情先使用生成的 list/get;汇总、联表等需求再添加 query。写清 reads、输入 schema,选择参数化 SQL 或 build;只查对应 safe 视图,不为“报表方便”切到管理员连接。
48
+
49
+ 重新生成后使用 `queries.你的查询()` 或生成 hook。检查真实调用人的 read 能力和数据范围;管理员能看到结果不能证明普通用户的报表正确。
50
+
51
+ ## 接入外部系统
52
+
53
+ 沿 `packages/domain/src/integrations/erp.ts` 的结构声明 host、密钥名、操作 schema、outbound 和 inbound。远端成功后的字段回写也走 external 命令。先用 fake 完成本地链路,再在测试环境做真实联调。
54
+
55
+ 明确重复请求如何识别、哪些错误重试、回执晚到怎么办、取消后如何补偿。不要假设本地事务可以回滚已经提交给第三方的写入。
56
+
57
+ ## 增加导入或导出
58
+
59
+ 在客户实际需要的业务页调用客户端/生成契约,展示任务进行中、完成和失败。导入映射到用户可调用的命令,准备与该命令输入匹配的 CSV;不要生成直接写业务表的上传接口。
60
+
61
+ 明确逐行还是整批原子模式、失败报告如何呈现、任务过期如何处理。任务提交成功不能立即标成导入/导出完成,worker 与下载时授权也是链路的一部分。完整方法、CSV 例子与协议见[业务页导入导出](transfers.md)。
62
+
63
+ ## 由采购演示换成客户业务
64
+
65
+ 空白模板不需要清理采购业务,直接从 `packages/domain/src` 新增自己的声明和页面;参考源码在 `docs/examples/purchase-admin`,不在运行入口中。下面的替换步骤只适用于采购模板。
66
+
67
+ 建议先保留运行薄壳和一条可验证的演示链路,新增真实业务并接通页面,再移除不需要的演示引用。清理时沿领域导出、反应、审批、连接器、查询、任务、页面和 graph.labels 一起检查,避免残留仍在运行的自动任务。
68
+
69
+ 组织模型仍从 `@yss123/cage-admin/domain` 导出。已执行迁移作为历史保留,新表/删表通过新迁移表达;需要保存旧数据时先制定迁移方案,不能只把整个 migrations 文件夹删除。
70
+
71
+ ## 给编程工具的任务模板
72
+
73
+ ```text
74
+ 阅读当前项目 AGENTS.md,按 docs/ai/README.md 找到本次需求对应的原语和标准范例。
75
+ 遵守 docs/ai/project-structure.md:领域和前端按业务建 modules,app 组合页面,shared 只放通用代码,不平铺 src 或堆进 main.tsx。
76
+ 业务:……;角色与数据范围:……;状态与允许动作:……;审批/外部系统/资源占用:……。
77
+ 先列出需要修改的 domain、页面、契约、迁移和授权,再直接完成实现。
78
+ 使用当前公开 API;对无法表达的需求说明具体缺口,不编造方法。
79
+ 生成文件通过 cage gen 更新,数据库使用已有 .env 配置,按改动完成必要验证。
80
+ 交付时说明用户如何体验、管理员需配置什么,以及通过/跳过/未执行的检查。
81
+ ```
@@ -0,0 +1,160 @@
1
+ # 业务对象之间的关系
2
+
3
+ [返回学习入口](README.md) · [最小业务](primitives.md) · [开发步骤](workflow.md)
4
+
5
+ 关系和普通属性统一写在 `aggregate(...).fields` 中。业务不需要另写 `relations` 区块,也不用为单纯“便笺与用户的关联”创建 `NoteMember` 聚合。
6
+
7
+ ## 选择哪一种声明
8
+
9
+ | 业务含义 | fields 中的写法 | 数据库如何保存 | 如何修改 |
10
+ | --- | --- | --- | --- |
11
+ | 多条便笺属于同一部门(多对一) | `deptId: ref('Dept')` | 本表 UUID 外键列 | 所属对象的命令 |
12
+ | 一个目标在租户内最多被引用一次(至多一对一) | `reviewerId: ref('User').unique().optional()` | 外键列 + `(tenant_id, reviewer_id)` 唯一索引 | 所属对象的命令;重复引用返回 CONFLICT |
13
+ | 一条便笺有多条评论(一对多) | `comments: hasMany('NoteComment', { mappedBy: 'noteId' })` | 外键在评论表的 `noteId: ref('Note')` 上;父表不增加列 | 评论自己的创建/归属命令;父字段只读 |
14
+ | 便笺有多个协作人,一个人也能协作多条便笺(多对多) | `members: manyToMany('User', { description: '协作成员' })` | 框架生成中间表、同租户复合外键和组合主键 | 所属便笺的命令返回目标 ID 数组 |
15
+ | 一张单据内的 JSON 明细 | `items: list({ sku: text(), qty: int() })` | 主表 JSONB 列 | 所属对象的命令;它不是对象关联 |
16
+
17
+ `ref().unique()` 表达“至多一个”,不强制目标一定被引用,也不自动创建目标。多对一的每个来源只有一个外键,但多个来源可以填同一目标 ID。一对多需要目标本来就是有业务含义的对象,例如评论、订单行;这种业务对象有自己的字段和命令。
18
+
19
+ `hasMany` 和 `manyToMany` 是集合关系,不支持 `.optional()`、`.unique()`、`.hidden()` 或 `.sensitive()`。没有关联使用空集合;目标对象的敏感属性由目标自身的权限掩码。可以使用 `.describe()`,也可以在工厂选项中写 `description`。关系不能嵌进 `list`、事件载荷或作为 scope 的部门/创建人字段;事件需要 ID 数组时使用 `list(id())`。
20
+
21
+ ## 完整领域例子
22
+
23
+ 下面可作为 `packages/domain/src/modules/note/note.ts`。它是[最小便笺](primitives.md)的关系版本,按需要替换原文件,避免重复声明 Note。
24
+
25
+ ```ts
26
+ import {
27
+ aggregate, command, fail, hasMany, manyToMany, ref, scope, states, text,
28
+ } from '@yss123/cage-kernel';
29
+ import { z } from 'zod';
30
+
31
+ export const Note = aggregate('Note', {
32
+ description: '部门便笺',
33
+ fields: {
34
+ title: text(120),
35
+ deptId: ref('Dept'),
36
+ createdBy: ref('User'),
37
+ // 这是 ID 集合。框架负责物理中间表、约束、关联写入和事务。
38
+ members: manyToMany('User', { description: '协作成员' }),
39
+ // 从评论上的 noteId 反查;这不是一份可写的评论 ID 数组。
40
+ comments: hasMany('NoteComment', { mappedBy: 'noteId', description: '便笺评论' }),
41
+ },
42
+ states: states('active', 'archived', { terminal: ['archived'] }),
43
+ scope: scope.deptSubtree('deptId'),
44
+ invariants: { titleNotBlank: (note) => note.title.trim().length > 0 },
45
+ });
46
+
47
+ export const NoteComment = aggregate('NoteComment', {
48
+ description: '便笺评论',
49
+ fields: {
50
+ noteId: ref('Note'),
51
+ body: text(2000),
52
+ deptId: ref('Dept'),
53
+ createdBy: ref('User'),
54
+ },
55
+ scope: scope.deptSubtree('deptId'),
56
+ });
57
+
58
+ export const createNote = command(Note, {
59
+ name: 'create', from: 'new', to: 'active',
60
+ input: z.object({
61
+ title: z.string().trim().min(1).max(120),
62
+ members: z.array(z.string().uuid()).optional(),
63
+ }),
64
+ // members 不传时由内核初始化为 [];部门与创建人仍按当前身份填入。
65
+ handler: (draft) => draft,
66
+ });
67
+
68
+ export const setNoteMembers = command(Note, {
69
+ name: 'setMembers', from: 'active', to: null,
70
+ input: z.object({ id: z.string().uuid(), members: z.array(z.string().uuid()) }),
71
+ // 完整替换集合;[] 表示全部解绑。只改关系也会递增便笺版本并写审计。
72
+ handler: (_draft, input) => ({ members: input.members }),
73
+ });
74
+
75
+ export const addNoteMembers = command(Note, {
76
+ name: 'addMembers', from: 'active', to: null,
77
+ input: z.object({ id: z.string().uuid(), memberIds: z.array(z.string().uuid()) }),
78
+ // 草稿包含完整的既有关联。增量追加可以保留当前操作者看不到的旧成员。
79
+ // 数组去重与排序由框架统一处理,业务不再维护第二套集合规范化。
80
+ handler: (draft, input) => ({ members: [...draft.members, ...input.memberIds] }),
81
+ });
82
+
83
+ export const createNoteComment = command(NoteComment, {
84
+ name: 'create', from: 'new', to: 'active', reads: { notes: Note },
85
+ input: z.object({ noteId: z.string().uuid(), body: z.string().trim().min(1).max(2000) }),
86
+ handler: async (_draft, input, { notes }) => {
87
+ const note = await notes.get(input.noteId);
88
+ if (!note) return fail('NOTE_NOT_AVAILABLE');
89
+ if (note.status !== 'active') return fail('NOTE_ARCHIVED');
90
+ // 这是“评论沿用便笺部门”的业务规则,必须显式表达,框架不猜测所属关系。
91
+ return { noteId: note.id, body: input.body, deptId: note.deptId };
92
+ },
93
+ });
94
+ ```
95
+
96
+ 在 `modules/note/index.ts` 写 `export * from './note.js';`,再从 domain 的 `index.ts` 保留组织模型及已有模块,导出新模块:
97
+
98
+ ```ts
99
+ export * from '@yss123/cage-admin/domain';
100
+ export * from './modules/note/index.js';
101
+ ```
102
+
103
+ 不写双向对象导入时,可用目标聚合名字符串解决循环引用。`mappedBy` 填**目标字段的 TypeScript 名称**,例如 `noteId`;不能填 SQL 列名 `note_id`、目标表名或普通文本字段。目标 ref 需要公开且非敏感,关系接口才能通过目标安全视图反查。
104
+
105
+ ## 页面如何使用
106
+
107
+ `pnpm gen` 后,生成契约会有以下方法。命令与列表方法的结果仍是 `CageResult`,页面按 `ok/code/domainCode` 处理:
108
+
109
+ ```ts
110
+ import {
111
+ note, noteComment, useNoteRelatedMembers, useNoteRelatedComments,
112
+ } from '@app/contracts';
113
+
114
+ // 命令修改关系,第三个参数为详情上看到的版本号,过期版本返回 CONFLICT。
115
+ const changed = await note.setMembers(noteId, { members: selectedUserIds }, version);
116
+ // 目标是评论对象,不给 hasMany 赋值。
117
+ const comment = await noteComment.create({ noteId, body: '已确认需求' });
118
+
119
+ // 与普通列表一样支持目标字段筛选、分页、排序,单页最多 200 条。
120
+ const members = await note.related.members(noteId, { page: 1, pageSize: 20 });
121
+ const comments = await note.related.comments(noteId, { page: 1, sort: '-createdAt' });
122
+
123
+ // React 组件内使用;没有选中 ID 时 hook 不发请求。
124
+ const memberQuery = useNoteRelatedMembers(noteId, { page: 1 });
125
+ const commentQuery = useNoteRelatedComments(noteId, { page: 1 });
126
+ ```
127
+
128
+ 上面的 `noteId/selectedUserIds/version` 来自页面的实际选择与详情数据;hook 在 React 组件或自定义 hook 内调用。保存成功后让相关缓存失效,例如 `queryClient.invalidateQueries({ queryKey: ['note', noteId] })`;新增评论还应更新评论列表。框架不自动推断所有跨对象缓存依赖。
129
+
130
+ 自动接口为:
131
+
132
+ ```text
133
+ GET /api/note/:id/relations/members
134
+ GET /api/note/:id/relations/comments
135
+ ```
136
+
137
+ 响应是 `{ items, total, page, pageSize }`,items 为**目标对象**的普通快照。`note.get/list` 和命令响应不自动展开成员、评论或完整 ID 集合,避免普通详情无限加载与绕过目标权限。生成的普通 Note 类型也不包含虚拟关系;不能从 `note.get()` 中直接读取 `members`。
138
+
139
+ ## 权限与事务
140
+
141
+ - 修改成员需要 `Note.setMembers` 或对应增量命令能力,以及便笺范围。新增的用户还必须在调用人的 User 范围内,真人调用需要 `User.read`;范围外或其他租户目标返回 NOT_FOUND,缺能力返回 FORBIDDEN。
142
+ - 读取成员需要 `Note.read` 和 `User.read`;读取评论需要 `Note.read` 和 `NoteComment.read`。所属对象不可见返回 NOT_FOUND;目标范围外的数据不计入 items/total。目标敏感字段按其 unmask 能力返回掩码或明文。
143
+ - 角色需要分别配置 Note、NoteComment 和 User 的实际数据范围;`hasMany` 不会自动让父对象可见的人看到所有子对象,关系声明本身也不会授予能力。
144
+ - 多对多是无序集合。框架统一 UUID 大小写、去重和排序;关联表不保存排列顺序。主表版本、关系增删、审计、outbox 和同事务反应共同提交或回滚。
145
+ - 只校验本次新增的目标。某个旧成员后来变为不可见时,只修改标题仍保留其关联;公开分页只返回当前可见目标。页面不能把某一页或可见子集当作完整集合传给 setMembers,否则会解绑未列出的成员;需要保留时使用增量命令。
146
+ - `hasMany` 不出现在可写草稿中。输入或 handler 给它赋值会返回 INVALID_INPUT。子对象跨范围或跨租户归属的业务规则要在子命令/约束中明确校验;普通 ref 的行为保持不变,不会继承 manyToMany 的新增目标可见性检查。
147
+
148
+ ## 迁移、图谱与边界
149
+
150
+ 第一次声明 `members` 后执行 `pnpm gen`,会生成 `core.note__members` 草稿。表只保存 `tenant_id/source_id/target_id`,以三列组合主键避免重复;两侧 `(tenant_id, id)` 唯一键与复合外键保证同租户。解绑只删链接,不删除 User;硬删除来源时数据库级联清链接,硬删除被引用目标时外键拒绝。
151
+
152
+ 关系表不注册为业务聚合,不增加独立 CRUD、状态机、审批或角色能力点。`hasMany` 本身不产生新表,只复用目标 ref 已有外键与索引。权限读取通过 `safe.note__members` 和两侧安全视图;业务项目不直接写这些表。
153
+
154
+ 按项目流程运行 `pnpm exec cage check`、`pnpm gen`,审查后 `pnpm migrate`;不要编辑已执行迁移,不会为关系另建一个数据库。删除关系声明后迁移只提示收缩,不自动删除已有链接数据。改变目标类型、把旧 JSON 数组改为关系等操作需要专门的数据迁移;框架不会猜测如何转换历史数据。
155
+
156
+ `pnpm exec cage graph` 的对象卡片将它们标为“一对多 / 多对多”,集合关系不显示主表列的必填星号。点击连线可看 1:N、N:M、mappedBy 外键位置、中间表名称,以及为什么连接、读写方式。
157
+
158
+ 当前每个 `manyToMany` 字段拥有自己的关系。两边各写一次会生成**两份独立关联**,不会自动成为互为镜像的双向关系;本版没有 manyToMany 的 inverse/mappedBy 选项。需要反向统计时声明 query,列出双方 reads 并查询安全视图。自关联可写 `manyToMany('Note')`,仍是有方向的集合,A 关联 B 不会自动使 B 关联 A。
159
+
160
+ 如果链接本身有职位、参与时间、分摊比例、审批状态等业务属性,它已是有意义的业务对象,应建如“项目任职”的聚合并定义自己的命令,而不是把这些属性塞进自动中间表。关系字段的职责是表达单纯关联。
@@ -0,0 +1,92 @@
1
+ # 在客户业务页接入导入和导出
2
+
3
+ [返回学习入口](README.md) · [开发步骤](workflow.md)
4
+
5
+ 框架提供持久化任务、CSV 处理、权限和下载接口。业务项目决定入口、列名、上传表单、任务状态与报告如何展示;不要求用户进入系统管理页,也不要求具备 Sys.ops 能力。路径中的 `/api/sys/transfers` 是通用任务接口。
6
+
7
+ 继续使用项目已有的 createServer 和 startWorker,不另写数据库连接、队列或文件服务。先审查并运行 `pnpm migrate`,同时部署 API 与 worker;只运行 API 会让任务一直等待。
8
+
9
+ ## 业务前端如何调用
10
+
11
+ 以下是业务事件处理函数,可按页面需要使用。目标导出对象与导入命令来自实际业务,不必固定为采购或库存。
12
+
13
+ ```ts
14
+ import { client, purchaseOrder } from '@app/contracts';
15
+
16
+ export async function exportApprovedOrders() {
17
+ // 同一次业务操作需要重试时,在页面保存并复用 key。
18
+ const key = crypto.randomUUID();
19
+ return purchaseOrder.export({ status: 'approved' }, { idempotencyKey: key });
20
+ }
21
+
22
+ export async function importCommandCsv(command: string, file: File, key: string) {
23
+ return client.importCsv(command, await file.text(), {
24
+ atomic: false,
25
+ idempotencyKey: key,
26
+ });
27
+ }
28
+
29
+ export async function inspectTransfer(id: string) {
30
+ const result = await client.transfer(id);
31
+ if (!result.ok) return result;
32
+ if (result.data.status !== 'completed') return result;
33
+ // 导入完成还可能有失败行,页面需展示报告中的 failed/succeeded。
34
+ if (result.data.kind === 'import') return client.importReport(id);
35
+ return client.transferDownloadUrl(id);
36
+ }
37
+ ```
38
+
39
+ 所有提交先判断 `result.ok`,再保存返回任务的 id,HTTP 202 仅表示已接受。React 可从 `@app/contracts` 引入 `useTransfer(id)`:等待、处理中和重试中每两秒刷新,完成/失败/过期停止。没有 React 时自行调用 client.transfer。
40
+
41
+ 页面返回时可用 `client.transfers('实际聚合名')` 恢复本人该对象最近任务。任务状态包括 queued、processing、retrying、completed、failed、expired;completed 的导入也可能部分失败或整批回滚,不能只按状态显示“全部成功”。
42
+
43
+ ## 导入 CSV 怎么准备
44
+
45
+ 目标命令必须已经导出、允许 user 调用、使用 Zod object 输入,并且当前用户具有对应命令能力。比如业务输入 `{ sku: string, available: number }` 的 UTF-8 CSV:
46
+
47
+ ```csv
48
+ sku,available
49
+ SKU-001,100
50
+ SKU-002,50
51
+ ```
52
+
53
+ - 表头是**命令输入字段名**,使用小驼峰,不是数据库列名或中文标题。页面可自行做中文列名映射。
54
+ - 可选列可省略;空的可选单元格表示不提供值。已有记录命令需要 id,还可提供 expectedVersion 控制乐观锁。
55
+ - 数字按数字解析,布尔使用 true/false,数组与对象写 JSON,金额按字符串输入。
56
+ - 复杂单元格仍要遵守 CSV 引号规则,例如 items 单元格可以是 `"[{""sku"":""A-01"",""qty"":1}]"`。
57
+ - 支持 BOM、CRLF、引号中的逗号/换行及双引号转义。重复/未知表头、错列和未闭合引号等文件错误会在排队前拒绝。
58
+ - 每行仍走原命令的权限、范围、状态、约束、不变量、审计、事件与同事务反应;导入不能绕过 process/external 限制。
59
+
60
+ 默认 `atomic: false`:成功行提交,失败行及其同事务反应回滚。`atomic: true`:任意行失败撤销整批业务写入,原成功行在报告中标为 ABORTED。
61
+
62
+ 报告只含记录号、ok/code/domainCode/fields,不返回原始值或命令快照。表头算第 1 条,第一条数据记录为第 2 条;带换行的引号单元格仍只算一条记录。页面用记录号帮助用户修复 CSV,不把失败文本直接拼成 HTML。
63
+
64
+ **导出文件不是导入模板。** 读侧导出包含 id/status/version 等系统列及掩码值,不能原样回写;按所选命令整理输入。
65
+
66
+ ## 接口与授权
67
+
68
+ | HTTP | 输入或结果 |
69
+ | --- | --- |
70
+ | POST `/api/{聚合短横线名}/export` | 列表筛选参数;返回 202 和任务,需要该聚合 export 能力 |
71
+ | POST `/api/sys/transfers/import` | `{ command, csv, atomic? }`,command 是 `聚合.命令` 能力标识;返回 202 和任务 |
72
+ | GET `/api/sys/transfers/options` | 本人可用的导出对象、导入命令、字段 name/required 与限制 |
73
+ | GET `/api/sys/transfers?aggregate=聚合名` | 本人最近 100 个任务,按需筛选业务对象 |
74
+ | GET `/api/sys/transfers/:id` | 本人单个任务的状态、计数及 errorCode |
75
+ | GET `/api/sys/transfers/:id/download` | 已完成导出的 CSV,或已完成导入的 JSON 报告 |
76
+
77
+ options 可用 `client.get('/api/sys/transfers/options')` 读取,没有额外假定的专用 helper。直接调用 HTTP 时,提交必须附会话、CSRF 以及 1–200 字符 Idempotency-Key;client 会处理这些协议。相同租户/用户的同键同内容返回同一任务,同键不同内容返回 CONFLICT。
78
+
79
+ 导出经过安全视图,按提交人的范围和明文权限读取。仅提交人能下载;账号停用、能力收回或范围/明文权限/部门树变化后,旧文件可能被拒绝下载,需要重新导出。系统运维权限不会自动授予他人的文件下载权。
80
+
81
+ ## 限制与排错
82
+
83
+ | 项目 | 当前限制 |
84
+ | --- | --- |
85
+ | 导入 | UTF-8 CSV,1 MiB / 1000 条数据记录 |
86
+ | 导出 | 100000 行 / 20 MiB;超限任务失败,不静默截断 |
87
+ | 导出分页参数 | page/pageSize 不限制导出范围;使用业务筛选条件缩小数据 |
88
+ | 有效期 | 提交起 24 小时,到期拒绝下载,由 worker 清理文件和结果 |
89
+ | 存储 | PostgreSQL 持久化,API 与 worker 不需要共享磁盘 |
90
+ | 暂未提供 | XLSX、对象存储、断点续传、逐行实时进度、超大文件流式导入 |
91
+
92
+ 任务一直 queued 先查 worker;failed 看 errorCode/导入报告;数据库故障耗尽重试时查原有死信链路。先修复根因再重放,别创建直接写业务表的“快速导入”接口。
@@ -0,0 +1,60 @@
1
+ # 排错方法
2
+
3
+ [返回学习入口](README.md) · [错误码册](../errors/README.md) · [HTTP 运行时错误](../errors/runtime.md)
4
+
5
+ 先保留完整诊断的 code、源码位置、reason/fix、requestId 或 outboxId。检查所在阶段,再修复原因;不要通过改业务状态、给全局权限、注释约束或禁用 lint 消除现象。
6
+
7
+ ## 按现象定位
8
+
9
+ | 现象 | 先查什么 | 常见原因 |
10
+ | --- | --- | --- |
11
+ | dev 启不动 | `.env` 是否在项目根目录,数据库服务是否可连接 | 缺连接、密码/端口错误、没执行迁移、端口占用 |
12
+ | init 提示账号已存在 | CAGE-INIT-002;找已有 APP/READ/MIGRATE 连接 | 角色在 PG 集群内共享;不应覆盖原密码 |
13
+ | 新业务不出现在接口/图谱 | 顶层 export const 和 domain/index.ts | 漏导出、重名导出、放进函数/条件声明 |
14
+ | API 403 | code/detail、角色能力、调用身份、硬约束 | 能力不足、CSRF、职责分离、额度缺失/超限 |
15
+ | 详情 404 | 记录 id、租户和数据范围 | 不存在或在范围外;不能仅凭 404 断言被删除 |
16
+ | 列表为空 | read 能力、范围与相应字段 | 默认本人范围、无 owner 字段或部门不匹配 |
17
+ | 电话看不到明文 | sensitive 标注、unmask 能力 | 默认掩码是正常安全行为 |
18
+ | 提交成功但无待办 | worker、outbox/死信、审批路由 | 未投递、缺审批人、没有匹配的绑定/路由 |
19
+ | 已批准但 ERP 单号未回写 | worker、同步记录、死信、环境/fake | 密钥/白名单/响应错误,或异步还未完成 |
20
+ | 已取消但锁未释放 | 释放事件、反应、死信 | 先前消息阻塞、map 条件不匹配或遗漏释放 |
21
+ | 导入/导出一直 queued | worker 与任务详情 | 后台进程未启动或前序任务失败 |
22
+ | gen --check 失败 | CAGE-GEN-001/002 | 声明修改未生成、手改契约、迁移快照不一致 |
23
+
24
+ ## CAGE-APPR-007:找不到审批人
25
+
26
+ 先读所属领域模块 `approvals.ts` 的实际步骤(采购范例为 `packages/domain/src/modules/purchase/approvals.ts`),按选人器处理:
27
+
28
+ | 选人器 | 配置位置 | 常见误解 |
29
+ | --- | --- | --- |
30
+ | `approver.deptLeader('deptId')` | 单据 deptId 指向的部门,在 `/admin` 设置负责人 | 给用户经理角色不等于给部门设置负责人 |
31
+ | `approver.role('finance')` | 创建 code 为 finance 的角色,并给实际用户分配 | 选人看角色 code,不是中文显示名称 |
32
+ | `approver.user(id)` | 使用存在的用户 UUID,核对业务可用性 | 把用户名或角色名当成 UUID |
33
+
34
+ 选人成功后,再检查此人所需能力、数据范围、额度。示例中提单人不能审批自己,经理的 `PurchaseOrder.approve` 必须有 ceiling 参数。
35
+
36
+ 若事件已进入死信,在 `/admin` 查看对应 outboxId 的错误并修正配置,再点击重放。原 submit 通常已提交,单据处于 pending_approval;不要重复提交,更不要直接 SQL 改回 draft。重放后确认待办及后续事件,不把“点击重放成功”当成整条链路已经完成。
37
+
38
+ ## CAGE-PAIR-002:新增操作遗漏释放
39
+
40
+ 查看报错命令是否离开持有资源的状态,沿 `命令 → emits 事件 → reaction → releases 资源` 检查。补业务事件与真正的释放命令,声明相同资源标签,并确认 map 在这条路径能执行。
41
+
42
+ 不要只加一个不执行的反应骗过图规则,也不要删 acquires 标签。图规则检查结构,业务测试还要覆盖关键条件分支。
43
+
44
+ ## 数据库错误应提供什么信息
45
+
46
+ 提供发生于 init/migrate/API/worker 哪一步、主机/端口/库名、使用哪个角色、PostgreSQL 错误码、是否有待执行迁移。URL 中密码特殊字符需编码;修改环境变量后重启进程。
47
+
48
+ - 连接拒绝:检查数据库服务、主机端口和容器端口映射。
49
+ - 认证失败(如 28P01):核对对应角色密码;不重置其他项目共享角色。
50
+ - 无权限:核对当前账号职责;读账号不能用来执行迁移,业务进程不应改成管理员连接。
51
+ - 表/视图不存在:审查后执行 migrate,确认连接的是同一个数据库。
52
+ - 已执行迁移校验和不符:恢复原文件,新增迁移;不要改 sys.schema_migrations 的 checksum。
53
+
54
+ 不要贴出整个 `.env`、初始化凭证文件、Cookie 密钥或恢复文件内容。管理员连接只用于准备权限,日常业务继续使用独立账号。
55
+
56
+ ## 如何说明没有验证的部分
57
+
58
+ 报告构建/检查通过的命令,另列跳过和未执行的检查及原因。fake 成功、图规则通过、HTTP 202、无权限用户看不到按钮,都不能单独证明真实第三方、任务完成或后端权限正确。
59
+
60
+ 错误码册中的例子用于解释局部规则;完整业务代码以当前项目 domain 和生成 contracts 为准。错误来自内部源码时,手册给出路径和位置;独立项目无需把内部源码复制进来修补,应先修自己的声明/配置或明确报告框架缺口。