@yss123/cage-cli 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -3
- package/dist/approval-tree.d.ts +88 -0
- package/dist/approval-tree.js +18 -0
- package/dist/approval-tree.js.map +1 -0
- package/dist/capabilities.js +46 -31
- package/dist/capabilities.js.map +1 -1
- package/dist/graph-approvals.d.ts +11 -1
- package/dist/graph-approvals.js +18 -5
- package/dist/graph-approvals.js.map +1 -1
- package/dist/graph-relations.js +10 -8
- package/dist/graph-relations.js.map +1 -1
- package/dist/graph.js +2 -0
- package/dist/graph.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/ontology.js +8 -5
- package/dist/ontology.js.map +1 -1
- package/dist/project-structure.js +2 -2
- package/dist/project-structure.js.map +1 -1
- package/dist/sync.d.ts +1 -1
- package/dist/sync.js +25 -108
- package/dist/sync.js.map +1 -1
- package/graph-ui/approval-flow.js +28 -8
- package/graph-ui/approval-parallel.js +110 -0
- package/graph-ui/approval-tree.js +17 -0
- package/graph-ui/business.css +26 -0
- package/graph-ui/business.js +30 -13
- package/graph-ui/graph-approvals.js +18 -5
- package/graph-ui/graph-relations.js +10 -8
- package/package.json +6 -6
- package/templates/app/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/app/AGENTS.md +39 -122
- package/templates/app/CLAUDE.md +6 -0
- package/templates/app/README.md +9 -8
- package/templates/app/apps/api/package.json +2 -2
- package/templates/app/apps/web/package.json +1 -1
- package/templates/app/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/app/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/app/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/app/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/app/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/app/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/app/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/app/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/app/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/app/cage.config.json +14 -0
- package/templates/app/db/migrations/0004_draft.sql +27 -0
- package/templates/app/docs/README.md +11 -0
- package/templates/app/docs/development/README.md +64 -0
- package/templates/app/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/app/docs/development/examples/note/note.ts +67 -0
- package/templates/app/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{blank/docs/ai → app/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/architecture.md +4 -4
- package/templates/app/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{blank/docs/ai → app/docs/development/getting-started}/workflow.md +13 -12
- package/templates/app/docs/development/guides/approvals.md +270 -0
- package/templates/app/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{blank/docs/ai → app/docs/development/guides}/files.md +5 -1
- package/templates/app/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{blank/docs/ai → app/docs/development/guides}/recipes.md +31 -11
- package/templates/app/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/app/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/guides}/transfers.md +1 -1
- package/templates/{blank/docs/ai → app/docs/development/reference}/commenting.md +1 -1
- package/templates/app/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/app/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/app/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{blank/docs/ai → app/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{blank/docs/ai → app/docs/development/tools}/ontology-context.md +3 -3
- package/templates/app/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/app/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/app/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/app/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/app/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/app/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/app/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/app/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/app/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/app/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/app/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/app/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/app/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/app/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/app/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/app/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/app/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/app/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/app/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/app/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/app/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/app/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/app/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/app/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/app/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/app/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/app/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/app/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/app/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/app/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/app/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/app/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/app/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/app/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/app/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/app/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/app/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/app/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/app/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/app/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/app/docs/errors/README.md +3 -3
- package/templates/app/docs/errors/runtime.md +1 -1
- package/templates/app/package.json +3 -3
- package/templates/app/packages/contracts/package.json +1 -1
- package/templates/app/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/app/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/app/packages/contracts/src/index.ts +9 -0
- package/templates/app/packages/domain/package.json +5 -5
- package/templates/app/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/app/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/app/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/.agents/skills/cage-development/SKILL.md +10 -0
- package/templates/blank/AGENTS.md +39 -122
- package/templates/blank/CLAUDE.md +6 -0
- package/templates/blank/README.md +3 -3
- package/templates/blank/apps/api/package.json +1 -1
- package/templates/blank/apps/web/package.json +1 -1
- package/templates/blank/docs/README.md +11 -0
- package/templates/blank/docs/development/README.md +64 -0
- package/templates/blank/docs/development/examples/note/note-detail-page.tsx +106 -0
- package/templates/blank/docs/development/examples/note/note.ts +67 -0
- package/templates/blank/docs/development/examples/note/notes-page.tsx +87 -0
- package/templates/{app/docs/ai → blank/docs/development/examples}/purchase-example.md +24 -10
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/architecture.md +4 -4
- package/templates/blank/docs/development/getting-started/first-feature.md +66 -0
- package/templates/{app/docs/ai → blank/docs/development/getting-started}/workflow.md +13 -12
- package/templates/blank/docs/development/guides/approvals.md +270 -0
- package/templates/blank/docs/development/guides/business-collaboration.md +63 -0
- package/templates/{app/docs/ai → blank/docs/development/guides}/files.md +5 -1
- package/templates/blank/docs/{ai → development/guides}/intelligence.md +4 -4
- package/templates/{app/docs/ai → blank/docs/development/guides}/recipes.md +31 -11
- package/templates/blank/docs/{ai → development/guides}/relations.md +2 -2
- package/templates/blank/docs/{ai → development/guides}/routing.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/guides}/transfers.md +1 -1
- package/templates/{app/docs/ai → blank/docs/development/reference}/commenting.md +1 -1
- package/templates/blank/docs/{ai → development/reference}/primitives.md +8 -62
- package/templates/blank/docs/{ai → development/reference}/project-structure.md +2 -2
- package/templates/blank/docs/development/skills/cage-development/SKILL.md +134 -0
- package/templates/{app/docs/ai → blank/docs/development/tools}/diagnostics.md +3 -3
- package/templates/{app/docs/ai → blank/docs/development/tools}/ontology-context.md +3 -3
- package/templates/blank/docs/{ai → development/tools}/troubleshooting.md +7 -1
- package/templates/blank/docs/errors/CAGE-AI-001.md +3 -3
- package/templates/blank/docs/errors/CAGE-APPR-001.md +13 -10
- package/templates/blank/docs/errors/CAGE-APPR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-APPR-005.md +5 -5
- package/templates/blank/docs/errors/CAGE-APPR-006.md +25 -10
- package/templates/blank/docs/errors/CAGE-APPR-007.md +9 -7
- package/templates/blank/docs/errors/CAGE-APPR-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUDIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-AUTH-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BIZ-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-BUDGET-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CMD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-007.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-008.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-009.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-012.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-013.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-014.md +1 -1
- package/templates/blank/docs/errors/CAGE-CONN-015.md +1 -1
- package/templates/blank/docs/errors/CAGE-CREATE-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-DECL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DECL-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-DEP-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-DIAG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-DLQ-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-ENV-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-EVENT-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-EVENT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-FIELD-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-FILE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GEN-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-GRANT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-IMPACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-INIT-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-JOB-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-LINT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-MIG-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-NAME-006.md +1 -1
- package/templates/blank/docs/errors/CAGE-ORG-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-PAIR-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-QUERY-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REACT-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-READS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-001.md +2 -2
- package/templates/blank/docs/errors/CAGE-REG-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-REG-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-REL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-ROUTE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SCOPE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SECRET-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-SENS-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-SQL-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-004.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-005.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-010.md +1 -1
- package/templates/blank/docs/errors/CAGE-STATE-011.md +1 -1
- package/templates/blank/docs/errors/CAGE-STRUCT-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-STRUCT-002.md +3 -3
- package/templates/blank/docs/errors/CAGE-SYNC-001.md +4 -4
- package/templates/blank/docs/errors/CAGE-TEST-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-TEST-003.md +1 -1
- package/templates/blank/docs/errors/CAGE-UNSAFE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-UPGRADE-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-001.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-002.md +1 -1
- package/templates/blank/docs/errors/CAGE-WORKER-003.md +1 -1
- package/templates/blank/docs/errors/README.md +3 -3
- package/templates/blank/docs/errors/runtime.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/README.md +1 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/components/task-operation-dialog.tsx +61 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/hooks/use-approval-tasks.ts +6 -9
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks-page.tsx +81 -49
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/approval/pages/approval-tasks.css +18 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/approval-history.tsx +39 -4
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/order-reviewers.tsx +38 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/components/purchase-order-form.tsx +9 -2
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-intelligence.css +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-order-detail-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/modules/purchase/pages/purchase-orders-page.tsx +2 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +6 -1
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.css +10 -0
- package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.tsx +51 -0
- package/templates/blank/docs/examples/purchase-admin/cage.config.json +14 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/approval-member/index.ts +70 -0
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/purchase-order/index.ts +10 -2
- package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/index.ts +9 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approval-members.ts +54 -0
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +19 -8
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/index.ts +2 -1
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +169 -7
- package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +56 -5
- package/templates/blank/package.json +3 -3
- package/templates/blank/packages/contracts/package.json +1 -1
- package/templates/blank/packages/domain/package.json +5 -5
- package/templates/app/docs/ai/README.md +0 -98
- package/templates/blank/docs/ai/README.md +0 -98
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- 迁移草稿:由 cage gen 生成,人审后提交;已发布的迁移不要修改
|
|
2
|
+
-- 破坏性变更按「先扩展再收缩」分多次发布
|
|
3
|
+
|
|
4
|
+
-- 新表:来自聚合 ApprovalMember
|
|
5
|
+
CREATE TABLE IF NOT EXISTS "core"."approval_member" (
|
|
6
|
+
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
7
|
+
"tenant_id" uuid NOT NULL,
|
|
8
|
+
"user_id" uuid NOT NULL REFERENCES "sys"."user"(id),
|
|
9
|
+
"display_name" text NOT NULL,
|
|
10
|
+
"specialty" text NOT NULL,
|
|
11
|
+
"status" text NOT NULL,
|
|
12
|
+
"version" integer NOT NULL DEFAULT 1,
|
|
13
|
+
"created_at" timestamptz NOT NULL DEFAULT now(),
|
|
14
|
+
"updated_at" timestamptz NOT NULL DEFAULT now(),
|
|
15
|
+
CONSTRAINT "approval_member_status_check" CHECK (status IN ('active', 'retired'))
|
|
16
|
+
);
|
|
17
|
+
CREATE UNIQUE INDEX IF NOT EXISTS "approval_member_user_id_uq" ON "core"."approval_member" (tenant_id, "user_id");
|
|
18
|
+
CREATE INDEX IF NOT EXISTS "approval_member_status_idx" ON "core"."approval_member" ("tenant_id", "status");
|
|
19
|
+
CREATE INDEX IF NOT EXISTS "approval_member_user_id_idx" ON "core"."approval_member" ("user_id");
|
|
20
|
+
|
|
21
|
+
ALTER TABLE "core"."purchase_order" ADD COLUMN IF NOT EXISTS "needs_specialist_review" boolean;
|
|
22
|
+
|
|
23
|
+
ALTER TABLE "core"."purchase_order" ADD COLUMN IF NOT EXISTS "reviewer_ids" jsonb;
|
|
24
|
+
|
|
25
|
+
/* cage:schema
|
|
26
|
+
{"version":1,"tables":[{"schema":"sys","name":"dept","aggregate":"Dept","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"name","type":"text","notNull":true},{"name":"code","type":"text","notNull":false},{"name":"parent_id","type":"uuid","notNull":false,"references":"sys.dept"},{"name":"ancestors","type":"text","notNull":true},{"name":"leader_id","type":"uuid","notNull":false},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["active","archived"],"indexes":[{"name":"dept_status_idx","columns":["tenant_id","status"]},{"name":"dept_parent_id_idx","columns":["parent_id"]}]},{"schema":"sys","name":"user","aggregate":"User","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"username","type":"text","notNull":true,"unique":true},{"name":"name","type":"text","notNull":true},{"name":"dept_id","type":"uuid","notNull":true,"references":"sys.dept"},{"name":"role_ids","type":"jsonb","notNull":true},{"name":"phone","type":"text","notNull":false},{"name":"email","type":"text","notNull":false},{"name":"sso_subject","type":"text","notNull":false},{"name":"password_hash","type":"text","notNull":false},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["active","disabled"],"indexes":[{"name":"user_tenant_dept_id_idx","columns":["tenant_id","dept_id"]},{"name":"user_status_idx","columns":["tenant_id","status"]}]},{"schema":"core","name":"approval_member","aggregate":"ApprovalMember","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"user_id","type":"uuid","notNull":true,"references":"sys.user","unique":true},{"name":"display_name","type":"text","notNull":true},{"name":"specialty","type":"text","notNull":true},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["active","retired"],"indexes":[{"name":"approval_member_status_idx","columns":["tenant_id","status"]},{"name":"approval_member_user_id_idx","columns":["user_id"]}]},{"schema":"core","name":"inventory","aggregate":"Inventory","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"sku","type":"text","notNull":true,"unique":true},{"name":"available","type":"integer","notNull":true},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["active","deleted"],"indexes":[{"name":"inventory_status_idx","columns":["tenant_id","status"]}]},{"schema":"core","name":"inventory_lock","aggregate":"InventoryLock","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"order_id","type":"uuid","notNull":true},{"name":"items","type":"jsonb","notNull":true},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["held","released"],"indexes":[{"name":"inventory_lock_status_idx","columns":["tenant_id","status"]}]},{"schema":"core","name":"purchase_order","aggregate":"PurchaseOrder","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"dept_id","type":"uuid","notNull":true,"references":"sys.dept"},{"name":"created_by","type":"uuid","notNull":true,"references":"sys.user"},{"name":"supplier_phone","type":"text","notNull":false},{"name":"amount","type":"numeric(18,2)","notNull":true},{"name":"reason","type":"text","notNull":false},{"name":"needs_specialist_review","type":"boolean","notNull":false},{"name":"reviewer_ids","type":"jsonb","notNull":false},{"name":"items","type":"jsonb","notNull":true},{"name":"erp_no","type":"text","notNull":false},{"name":"attachments","type":"jsonb","notNull":true},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["draft","pending_approval","approved","received","rejected","cancelled"],"indexes":[{"name":"purchase_order_tenant_dept_id_idx","columns":["tenant_id","dept_id"]},{"name":"purchase_order_tenant_created_by_idx","columns":["tenant_id","created_by"]},{"name":"purchase_order_status_idx","columns":["tenant_id","status"]}]},{"schema":"sys","name":"role","aggregate":"Role","columns":[{"name":"id","type":"uuid","notNull":true},{"name":"tenant_id","type":"uuid","notNull":true},{"name":"code","type":"text","notNull":true,"unique":true},{"name":"name","type":"text","notNull":true},{"name":"capabilities","type":"jsonb","notNull":true},{"name":"scopes","type":"jsonb","notNull":true},{"name":"status","type":"text","notNull":true},{"name":"version","type":"integer","notNull":true},{"name":"created_at","type":"timestamptz","notNull":true},{"name":"updated_at","type":"timestamptz","notNull":true}],"statusValues":["active","archived"],"indexes":[{"name":"role_status_idx","columns":["tenant_id","status"]}]}]}
|
|
27
|
+
*/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# 本地框架文档
|
|
2
|
+
|
|
3
|
+
从 [框架学习入口](development/README.md) 或 [Cage 开发 Skill](development/skills/cage-development/SKILL.md) 开始。
|
|
4
|
+
|
|
5
|
+
- `development/getting-started`:运行模型、完整入门课和开发步骤。
|
|
6
|
+
- `development/guides`:[业务澄清与共同设计](development/guides/business-collaboration.md)、能力选择与各专题接入。
|
|
7
|
+
- `development/reference`:原语与工程规范。
|
|
8
|
+
- `development/tools`:诊断、本体查询和排错。
|
|
9
|
+
- `development/examples`:教学源码与采购导读。
|
|
10
|
+
- `development/skills`:框架开发方法。
|
|
11
|
+
- [错误码册](errors/README.md):按实际错误定位原因。
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Cage 框架学习入口
|
|
2
|
+
|
|
3
|
+
本目录是开发者与编程 Agent 共用的 Cage 应用开发教材,帮助你理解框架并把业务需求落实为可运行的应用。开发方法的统一入口是 [cage-development Skill](skills/cage-development/SKILL.md)。教材由框架维护,随脚手架分发;读取教材无需额外服务、模型或向量索引。
|
|
4
|
+
|
|
5
|
+
模型接入、智能助理和 AI 审批属于框架的原生 AI 能力,具体接入见[原生 AI 专题](guides/intelligence.md)。
|
|
6
|
+
|
|
7
|
+
## 按当前阶段阅读
|
|
8
|
+
|
|
9
|
+
| 你现在需要什么 | 从哪里开始 | 应得到什么 |
|
|
10
|
+
| --- | --- | --- |
|
|
11
|
+
| 第一次使用 Cage | [Skill 的运行模型](skills/cage-development/SKILL.md#先建立正确的运行模型) → [第一条完整业务链](getting-started/first-feature.md) | 理解声明、导出、生成、授权、契约与持久读写如何连接 |
|
|
12
|
+
| 用户描述或文档不清楚 | [业务澄清与共同设计](guides/business-collaboration.md) | 主动提出疑问、可行选项与建议,与用户选择业务规则,再落实到实现和验收 |
|
|
13
|
+
| 把需求转成设计 | [业务设计与能力选择](guides/recipes.md) | 确定聚合边界、命令、范围、关系和一致性要求 |
|
|
14
|
+
| 实现或修改功能 | [Skill 的实施流程](skills/cage-development/SKILL.md#4-沿一条真实链路实施再扩展) → [开发步骤](getting-started/workflow.md) | 沿实际调用链完成受影响的功能,并取得验证证据 |
|
|
15
|
+
| 已有功能不正常 | [排错方法](tools/troubleshooting.md) → [诊断工具](tools/diagnostics.md) / [错误码册](../errors/README.md) | 找到第一个失败边界,修复原因并复验 |
|
|
16
|
+
| 查询一个能力的用法 | 下方专题表,必要时本地搜索术语 | 找到当前任务的 API、接入前提与限制 |
|
|
17
|
+
|
|
18
|
+
已经熟悉框架时不必每次重读课程。不要将所有专题一次加载,也不要把参考业务注册到客户应用。
|
|
19
|
+
|
|
20
|
+
## 按资料用途查找
|
|
21
|
+
|
|
22
|
+
| 目录 | 内容与入口 |
|
|
23
|
+
| --- | --- |
|
|
24
|
+
| `getting-started/` 入门 | [运行模型](getting-started/architecture.md)、[完整入门课](getting-started/first-feature.md)、[开发步骤](getting-started/workflow.md) |
|
|
25
|
+
| `guides/` 专题指南 | [业务澄清](guides/business-collaboration.md)、[能力选择](guides/recipes.md)、[审批](guides/approvals.md)、[文件](guides/files.md)、[关系](guides/relations.md)、[路由](guides/routing.md)、[数据传输](guides/transfers.md)、[原生 AI](guides/intelligence.md) |
|
|
26
|
+
| `reference/` 参考规范 | [原语](reference/primitives.md)、[目录组织](reference/project-structure.md)、[中文注释](reference/commenting.md) |
|
|
27
|
+
| `tools/` 工具与排错 | [诊断](tools/diagnostics.md)、[本体上下文](tools/ontology-context.md)、[排错方法](tools/troubleshooting.md) |
|
|
28
|
+
| `examples/` 教学示例 | [便笺源码](examples/note/note.ts)、[采购导读](examples/purchase-example.md);按入门课与导读阅读,源码不自动注册 |
|
|
29
|
+
| `skills/` agent 开发流程 | [cage-development](skills/cage-development/SKILL.md),按任务加载上述教材 |
|
|
30
|
+
|
|
31
|
+
查明确符号可用 `rg -n "术语或符号" docs/development docs/errors`,没有 rg 时使用 agent 的本地文件搜索。用 `pnpm exec cage capabilities 符号 --format json` 核对已安装版本公开签名,用 `pnpm exec cage context "业务关键词" --format json` 定位当前应用。候选命中不能代替阅读上下文,未命中也不能证明框架不支持。
|
|
32
|
+
|
|
33
|
+
## 确认代码与资料的位置
|
|
34
|
+
|
|
35
|
+
本指南的业务路径和命令默认相对应用根目录。应用的 `packages/domain/src` 定义业务,`packages/contracts/src` 是生成契约,`apps/web/src` 是界面,`apps/api/src` 是薄启动入口,`db/migrations` 保存审查过的迁移。
|
|
36
|
+
|
|
37
|
+
空白模板只启用组织基础;`docs/examples/purchase-admin` 是离线采购参考源码,不参与注册、编译或迁移。采购模板直接在业务目录包含该范例。框架仓库的应用根目录是 `examples/purchase-admin`,规范教材位于仓库 `docs/development`,以应用 AGENTS.md 的说明为准。
|
|
38
|
+
|
|
39
|
+
`docs/development` 是框架教学内容的唯一手写来源;Skills 发现入口和模板副本由同步脚本生成。用户的业务约定保留在项目 AGENTS.md 的托管区外。教材分发检查只能证明资料存在且一致,模型是否学会需要框架维护者另做任务评估。
|
|
40
|
+
|
|
41
|
+
## 当前实现边界
|
|
42
|
+
|
|
43
|
+
以下区分可避免 agent 把方案中的愿景写成不存在的功能。
|
|
44
|
+
|
|
45
|
+
| 已有能力 | 使用边界 |
|
|
46
|
+
| --- | --- |
|
|
47
|
+
| 真实 PostgreSQL 连接、分账号、表结构迁移 | 连接已有数据库;`init` 不建库;不会自动识别并接管任意历史表 |
|
|
48
|
+
| 聚合/命令、能力与范围、掩码、审计、outbox、worker | handler 不做数据库/网络 IO;生产角色不会自动拿到新能力 |
|
|
49
|
+
| fields 中的 ref / hasMany / manyToMany | hasMany 为只读反查;manyToMany 自动中间表并随主对象事务写入,公开读取独立分页;双向集合不会自动互为镜像 |
|
|
50
|
+
| 本体抽取、中文检索/节点/路径/AI 上下文、图规则、图谱、生成、影响分析与门禁 | 上下文注明证据、源码和覆盖边界;名称与测试匹配是候选线索,不能证明任意 JS 正确;跳过用例仍需补足 |
|
|
51
|
+
| 有界集合 reactionEach、显式步骤 order | 稳定唯一键排序后串行调用目标命令;同事务整体回滚,异步链按 outbox 恢复;不是并行写表接口 |
|
|
52
|
+
| capabilities、doctor、timeline、explain、snapshot | 实际运行诊断需要受控会话与权限;未取得的证据为 unknown,授权解释不执行 handler |
|
|
53
|
+
| 代码审批路由与任务流转 | 顺序/条件、`parallel/branch` 结构化 AND 分支与汇合、any/all、字段选人、候选领取/释放、转办、单层委派归还、前后/并行必签和动作历史;候选池仅支持 any,追加票全部必签。条件和依赖随提交冻结,currentStep 仅为最小活动索引;尚无任意连线/循环/OR 网关、岗位/代理规则、通用恢复、任意 JS 选人或在途迁移,见[任务流转](guides/approvals.md) |
|
|
54
|
+
| 审批时限、站内提醒与催办 | 人工节点按实际开启时间计算自然分钟 SLA,worker 持久投递临期/超时提醒;申请人可按完整活动集合催办,同实例共享冷却,接收人可标已读。旧快照不自动获得政策;尚无工作日历、自动升级、抄送或邮件/短信通知 |
|
|
55
|
+
| 审批界面与 SDK | Admin/采购待办可领取、释放、归还受托任务并查看等待加签;采购另有协作名录选人器、转办/委派/三种加签及分页人工历史,view 查询参数保留视图。启用运营后通用 Admin 提供我的申请、催办和流程提醒;页面仍需应用启用、真实授权与浏览器验收。AI 助理目前只查询本人待办及提交决定,无人员管理写工具 |
|
|
56
|
+
| 原生 AI 审批与领域助理 | 现有 `step` 支持 `kind: 'ai'`;三态审核、自动工具、会话及偏好记忆可复用;需显式启用、授权并做模型质量验收 |
|
|
57
|
+
| Admin 模型与性格、助理 SDK | 动态模型连接和性格管理;Chat Completions / Responses 兼容性以实际服务为准;SDK 提供流式状态与页面快照,悬浮窗和 Markdown 由应用实现 |
|
|
58
|
+
| 连接器 fake、出站、入站验签、同步记录 | fake 不等于真实 ERP 联调,也不等于完整请求录制/回放平台 |
|
|
59
|
+
| 异步 CSV 导入/导出接口与客户端任务查询 | 界面由业务项目决定;导出结果不是可直接回导的命令输入模板 |
|
|
60
|
+
| 私有文件字段、流式分片上传、暂停与续传、范围下载 | 默认本地存储,提供对象存储适配接口;尚无内置 S3/OSS、签名直传、病毒扫描及文件共享 |
|
|
61
|
+
| 组织模型、系统管理界面 | 暂无移动部门(改上级)的完整支持、菜单配置页 |
|
|
62
|
+
| 库存锁采购范例 | 演示资源生命周期;不是仓库级并发库存扣减、退货或 ERP 撤销补偿系统 |
|
|
63
|
+
|
|
64
|
+
文档与实现冲突时,以当前代码和检查结果为依据,指出冲突并修正文档;不要默默扩展公开 API。规划、代码存在、构建通过、真实联调通过是不同状态,交付时分别说明。
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 入门课详情页,从 URL 的 id 独立读取便笺并通过契约修改或归档。
|
|
3
|
+
* 由 app 组合到 /notes/:id;query 缓存与退出清理由现有应用会话层管理。
|
|
4
|
+
* 每次写入传服务端记录版本;冲突后刷新而不自动覆盖,归档后仅展示结果。
|
|
5
|
+
*/
|
|
6
|
+
import { useRef, useState } from 'react';
|
|
7
|
+
import { Link, useParams } from 'react-router-dom';
|
|
8
|
+
import { note, useNote } from '@app/contracts';
|
|
9
|
+
import { useCan } from '@yss123/cage-client';
|
|
10
|
+
|
|
11
|
+
/** 加载独立详情;标题草稿为 null 时显示服务端标题,空字符串仍表示用户正在编辑。 */
|
|
12
|
+
export function NoteDetailPage() {
|
|
13
|
+
const { id = '' } = useParams();
|
|
14
|
+
const query = useNote(id);
|
|
15
|
+
const canUpdate = useCan('Note.update');
|
|
16
|
+
const canArchive = useCan('Note.archive');
|
|
17
|
+
const [draft, setDraft] = useState<{ id: string; title: string } | null>(null);
|
|
18
|
+
const [error, setError] = useState('');
|
|
19
|
+
const [saving, setSaving] = useState(false);
|
|
20
|
+
const submitting = useRef(false);
|
|
21
|
+
const record = query.data;
|
|
22
|
+
const title = draft?.id === id ? draft.title : (record?.title ?? '');
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 保存或归档当前记录,使用读取版本防止覆盖并发变更;失败保留草稿,成功后清除草稿并读取。
|
|
26
|
+
* 冲突刷新后要求用户核对再提交;按钮禁用与引用锁限制本页同时提交,不替代内核检查。
|
|
27
|
+
*/
|
|
28
|
+
async function save(action: 'update' | 'archive') {
|
|
29
|
+
if (!record || submitting.current) return;
|
|
30
|
+
submitting.current = true;
|
|
31
|
+
setSaving(true);
|
|
32
|
+
setError('');
|
|
33
|
+
try {
|
|
34
|
+
const result =
|
|
35
|
+
action === 'update'
|
|
36
|
+
? await note.update(record.id, { title }, record.version)
|
|
37
|
+
: await note.archive(record.id, record.version);
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
setError(
|
|
40
|
+
result.code === 'CONFLICT'
|
|
41
|
+
? '记录已变化,请核对最新数据和当前草稿后再提交。'
|
|
42
|
+
: `操作失败:${result.code}`,
|
|
43
|
+
);
|
|
44
|
+
if (result.code === 'CONFLICT') await query.refetch();
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
setDraft(null);
|
|
48
|
+
await query.refetch();
|
|
49
|
+
} catch {
|
|
50
|
+
setError('请求未完成,请重新读取记录确认结果。');
|
|
51
|
+
} finally {
|
|
52
|
+
submitting.current = false;
|
|
53
|
+
setSaving(false);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<section>
|
|
59
|
+
<Link to="/notes">返回列表</Link>
|
|
60
|
+
{query.isPending && <p role="status">正在加载…</p>}
|
|
61
|
+
{query.isError && (
|
|
62
|
+
<p role="alert">
|
|
63
|
+
无法读取记录:可能不存在、会话失效或不在可见范围。
|
|
64
|
+
<button onClick={() => void query.refetch()}>重试</button>
|
|
65
|
+
</p>
|
|
66
|
+
)}
|
|
67
|
+
{record && (
|
|
68
|
+
<>
|
|
69
|
+
<h1>{record.title}</h1>
|
|
70
|
+
<p>
|
|
71
|
+
状态:{record.status};版本:{record.version}
|
|
72
|
+
</p>
|
|
73
|
+
{record.status === 'active' && (
|
|
74
|
+
<>
|
|
75
|
+
{canUpdate && (
|
|
76
|
+
<form
|
|
77
|
+
onSubmit={(event) => {
|
|
78
|
+
event.preventDefault();
|
|
79
|
+
void save('update');
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
<label>
|
|
83
|
+
标题
|
|
84
|
+
<input
|
|
85
|
+
value={title}
|
|
86
|
+
required
|
|
87
|
+
maxLength={120}
|
|
88
|
+
onChange={(event) => setDraft({ id, title: event.target.value })}
|
|
89
|
+
/>
|
|
90
|
+
</label>
|
|
91
|
+
<button disabled={saving || !title.trim()}>保存</button>
|
|
92
|
+
</form>
|
|
93
|
+
)}
|
|
94
|
+
{canArchive && (
|
|
95
|
+
<button disabled={saving} onClick={() => void save('archive')}>
|
|
96
|
+
归档
|
|
97
|
+
</button>
|
|
98
|
+
)}
|
|
99
|
+
</>
|
|
100
|
+
)}
|
|
101
|
+
</>
|
|
102
|
+
)}
|
|
103
|
+
{error && <p role="alert">{error}</p>}
|
|
104
|
+
</section>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 框架入门课的部门便笺声明,由学习者放入应用领域模块后从 domain 入口导出。
|
|
3
|
+
* 聚合维护标题、归属和状态;命令仅描述业务变化,实际权限、事务与持久化由内核执行。
|
|
4
|
+
* 本文件作为教材分发,不自动注册到空白应用;不存在文件、审批或外部 IO 副作用。
|
|
5
|
+
*/
|
|
6
|
+
import { aggregate, command, ref, scope, states, text } from '@yss123/cage-kernel';
|
|
7
|
+
import { z } from 'zod';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* 部门便笺以标题和生命周期为一致性边界,通过部门与创建人字段支持安全读取。
|
|
11
|
+
* 声明被 domain 导出后参与注册和生成;实际可见范围取决于角色授权,本声明不授予权限。
|
|
12
|
+
* 所有命令结果均须满足非空标题不变量,归档保留记录并关闭修改入口。
|
|
13
|
+
*/
|
|
14
|
+
export const Note = aggregate('Note', {
|
|
15
|
+
description: '部门便笺',
|
|
16
|
+
fields: {
|
|
17
|
+
/** 标题最多 120 字符;命令先去除两端空白,不变量拒绝空白标题。 */
|
|
18
|
+
title: text(120),
|
|
19
|
+
/** 内核新建时按当前主体填充的部门,用于服务端范围过滤。 */
|
|
20
|
+
deptId: ref('Dept'),
|
|
21
|
+
/** 内核新建时记录的创建人,可用于本人范围;不接受客户端指定。 */
|
|
22
|
+
createdBy: ref('User'),
|
|
23
|
+
},
|
|
24
|
+
states: states('active', 'archived', { terminal: ['archived'] }),
|
|
25
|
+
scope: scope.deptSubtree('deptId'),
|
|
26
|
+
invariants: { titleNotBlank: (record) => record.title.trim().length > 0 },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* 创建 active 便笺,输入只有去除两端空白后的标题,草稿直接作为业务结果返回。
|
|
31
|
+
* 内核填写归属并执行 Note.create 授权、审计及事务;空标题、无能力或不合格范围会拒绝写入。
|
|
32
|
+
* 不发布额外业务事件,也不接受客户端指定归属字段。
|
|
33
|
+
*/
|
|
34
|
+
export const createNote = command(Note, {
|
|
35
|
+
name: 'create',
|
|
36
|
+
description: '创建便笺',
|
|
37
|
+
from: 'new',
|
|
38
|
+
to: 'active',
|
|
39
|
+
input: z.object({ title: z.string().trim().min(1).max(120) }),
|
|
40
|
+
handler: (draft) => draft,
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 修改 active 便笺标题;id 用于内核按范围锁定记录,页面另传已读取的 expectedVersion。
|
|
45
|
+
* to: null 保留状态,返回值只包含标题变化;归档或版本冲突由内核拒绝,handler 不直接写库。
|
|
46
|
+
*/
|
|
47
|
+
export const updateNote = command(Note, {
|
|
48
|
+
name: 'update',
|
|
49
|
+
description: '修改便笺标题',
|
|
50
|
+
from: 'active',
|
|
51
|
+
to: null,
|
|
52
|
+
input: z.object({ id: z.string().uuid(), title: z.string().trim().min(1).max(120) }),
|
|
53
|
+
handler: (_draft, input) => ({ title: input.title }),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* 归档 active 便笺,输入沿用已有对象命令的默认 id,返回草稿以保留标题与归属。
|
|
58
|
+
* 内核检查 Note.archive、范围与版本,再设置 archived;已有归档记录的修改和重复归档会拒绝。
|
|
59
|
+
* 本动作不删除历史记录,不发布需 worker 处理的业务事件。
|
|
60
|
+
*/
|
|
61
|
+
export const archiveNote = command(Note, {
|
|
62
|
+
name: 'archive',
|
|
63
|
+
description: '归档便笺',
|
|
64
|
+
from: 'active',
|
|
65
|
+
to: 'archived',
|
|
66
|
+
handler: (draft) => draft,
|
|
67
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 入门课的便笺列表与创建页面,通过生成契约读写服务端记录,不维护本地业务仓储。
|
|
3
|
+
* 由 app 路由在已登录且具备 Note.read 的页面内挂载;服务端仍执行全部权限与范围判断。
|
|
4
|
+
* 表单状态只保留未提交标题,失败不清空;创建成功后重新查询列表,详情使用独立 URL。
|
|
5
|
+
*/
|
|
6
|
+
import { useRef, useState, type FormEvent } from 'react';
|
|
7
|
+
import { Link } from 'react-router-dom';
|
|
8
|
+
import { note, useNoteList } from '@app/contracts';
|
|
9
|
+
import { useCan } from '@yss123/cage-client';
|
|
10
|
+
|
|
11
|
+
/** 显示当前主体可见的第一页便笺并提供创建入口;分页扩展应按路由规范放入 URL 参数。 */
|
|
12
|
+
export function NotesPage() {
|
|
13
|
+
const query = useNoteList();
|
|
14
|
+
const canCreate = useCan('Note.create');
|
|
15
|
+
const [title, setTitle] = useState('');
|
|
16
|
+
const [error, setError] = useState('');
|
|
17
|
+
const [saving, setSaving] = useState(false);
|
|
18
|
+
const submitting = useRef(false);
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 提交一次创建,立即锁住重复点击;业务失败保留输入,成功后从安全接口重新读取。
|
|
22
|
+
* 此锁只限制当前页面的并发点击,不承诺跨刷新重试去重;业务需恢复同次请求时另保存幂等键。
|
|
23
|
+
*/
|
|
24
|
+
async function create(event: FormEvent<HTMLFormElement>) {
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
if (submitting.current) return;
|
|
27
|
+
submitting.current = true;
|
|
28
|
+
setSaving(true);
|
|
29
|
+
setError('');
|
|
30
|
+
try {
|
|
31
|
+
const result = await note.create({ title });
|
|
32
|
+
if (!result.ok) {
|
|
33
|
+
setError(`创建失败:${result.code}`);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
setTitle('');
|
|
37
|
+
await query.refetch();
|
|
38
|
+
} catch {
|
|
39
|
+
setError('请求未完成,请重新读取列表确认结果后再决定是否重试。');
|
|
40
|
+
} finally {
|
|
41
|
+
submitting.current = false;
|
|
42
|
+
setSaving(false);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<section>
|
|
48
|
+
<h1>部门便笺</h1>
|
|
49
|
+
{canCreate && (
|
|
50
|
+
<form onSubmit={create}>
|
|
51
|
+
<label>
|
|
52
|
+
标题
|
|
53
|
+
<input
|
|
54
|
+
value={title}
|
|
55
|
+
maxLength={120}
|
|
56
|
+
required
|
|
57
|
+
onChange={(event) => setTitle(event.target.value)}
|
|
58
|
+
/>
|
|
59
|
+
</label>
|
|
60
|
+
<button disabled={saving || !title.trim()}>{saving ? '正在保存…' : '创建'}</button>
|
|
61
|
+
</form>
|
|
62
|
+
)}
|
|
63
|
+
{error && <p role="alert">{error}</p>}
|
|
64
|
+
{query.isPending && <p role="status">正在加载…</p>}
|
|
65
|
+
{query.isError && (
|
|
66
|
+
<p role="alert">
|
|
67
|
+
读取失败,请检查会话和读取权限。<button onClick={() => void query.refetch()}>重试</button>
|
|
68
|
+
</p>
|
|
69
|
+
)}
|
|
70
|
+
{query.data && (
|
|
71
|
+
<>
|
|
72
|
+
<p>
|
|
73
|
+
共 {query.data.total} 条,当前显示 {query.data.items.length} 条。
|
|
74
|
+
</p>
|
|
75
|
+
<ul>
|
|
76
|
+
{query.data.items.map((record) => (
|
|
77
|
+
<li key={record.id}>
|
|
78
|
+
<Link to={`/notes/${encodeURIComponent(record.id)}`}>{record.title}</Link>({record.status})
|
|
79
|
+
</li>
|
|
80
|
+
))}
|
|
81
|
+
</ul>
|
|
82
|
+
{query.data.items.length === 0 && <p>当前可见范围内没有便笺。</p>}
|
|
83
|
+
</>
|
|
84
|
+
)}
|
|
85
|
+
</section>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 标准采购范例:从声明到业务页面
|
|
2
2
|
|
|
3
|
-
[返回学习入口](README.md) · [业务任务配方](recipes.md)
|
|
3
|
+
[返回学习入口](../README.md) · [业务任务配方](../guides/recipes.md)
|
|
4
4
|
|
|
5
5
|
选择 `--template purchase` 创建的是可运行的采购应用,下面的路径相对应用根目录;选择 `--template blank` 时,同一份范例仅作为参考源码保存在 `docs/examples/purchase-admin`,下面路径相对该参考目录,它不参与当前应用的编译、注册或迁移。框架能力来自 npm 依赖。先沿一条真实链路读代码,再把需要的模式应用到自己的业务。
|
|
6
6
|
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
| `packages/domain/src/index.ts` | 哪些声明真正被注册 | 保留组织模型,导出新增业务,避免导出名冲突 |
|
|
12
12
|
| `packages/domain/src/modules/inventory/inventory.ts` | 最简字段、创建命令、资源锁聚合 | scope.none 不等于无需授权;创建与释放都是命令 |
|
|
13
13
|
| `packages/domain/src/modules/purchase/purchase-order.ts` | 单据生命周期、金额、明细、约束、事件 | 哪些字段由输入提供,哪些由内核维护;每个状态的允许操作 |
|
|
14
|
+
| `packages/domain/src/modules/purchase/approval-members.ts` | 采购协作名录与公开业务姓名 | 名录读权限与系统用户资料分离;成员身份不授予审批权限 |
|
|
14
15
|
| `packages/domain/src/workflows/purchase-inventory.ts` | 同事务建锁、异步释放 | acquires/releases 的配对及 map 返回 null 的原因 |
|
|
15
|
-
| `packages/domain/src/modules/purchase/approvals.ts` |
|
|
16
|
+
| `packages/domain/src/modules/purchase/approvals.ts` | 启动/取消审批、专业会签、财务候选与任务动作 | routable、部门负责人、字段选人、候选领取、结论命令 |
|
|
16
17
|
| `packages/domain/src/integrations/erp.ts` | 出站推单、成功回写、入站回调 | 白名单、secret 名、schema、幂等与 HMAC |
|
|
17
18
|
| `packages/domain/src/integrations/intelligence.ts` | 消费框架托管模型和本体助理 | Admin 配置连接,通用工具由框架生成 |
|
|
18
19
|
| `packages/domain/src/modules/purchase/queries.ts` | 按状态汇总 | reads、安全视图、命名参数 |
|
|
@@ -32,7 +33,9 @@
|
|
|
32
33
|
→ 材料不足:returnForChanges → draft,补充用途后重新提交、从头审核
|
|
33
34
|
→ 疑难材料:在本节点创建人工接管待办
|
|
34
35
|
→ 材料清楚:通过本节点,创建经理待办
|
|
35
|
-
→
|
|
36
|
+
→ 部门经理审批
|
|
37
|
+
→ 申请启用专业复核时:所选专业人员全部会签
|
|
38
|
+
→ 金额 > 50000 时:财务共享候选任务,由 finance 成员领取后处理
|
|
36
39
|
→ 流程完成 → 调用 approve
|
|
37
40
|
→ 职责分离、额度、库存检查
|
|
38
41
|
→ approved + OrderApproved
|
|
@@ -45,11 +48,12 @@
|
|
|
45
48
|
|
|
46
49
|
submit 返回成功只表示单据和事件已提交,不表示待办、ERP 或其他异步工作已经完成。worker 未运行时,单据可以已经处于待审批,但没有待办。
|
|
47
50
|
|
|
48
|
-
###
|
|
51
|
+
### 为什么分开单据、库存与协作名录
|
|
49
52
|
|
|
50
53
|
- `PurchaseOrder` 管单据、审批结论和采购状态。
|
|
51
54
|
- `Inventory` 是物料台账,提供库存检查的数据。
|
|
52
55
|
- `InventoryLock` 记录某张订单的资源占用,使用订单 id 作为锁 id,让释放目标明确。
|
|
56
|
+
- `ApprovalMember` 保存采购参与者可读取的公开业务姓名和专业说明。它引用真实用户,但不开放 `User.name` 等系统用户资料,也不代替角色授权。
|
|
53
57
|
|
|
54
58
|
审批通过和建锁放在同一事务,是为了避免“单据已批准却没拿到资源”。ERP 网络请求放到 outbox,是因为远端调用无法与本地 PostgreSQL 共同回滚。
|
|
55
59
|
|
|
@@ -67,23 +71,33 @@ submit 返回成功只表示单据和事件已提交,不表示待办、ERP 或
|
|
|
67
71
|
|
|
68
72
|
## 首次体验所需业务配置
|
|
69
73
|
|
|
70
|
-
以下体验需要采购模板;空白模板不会出现采购页面和审批配置。先按[开发步骤](workflow.md)准备已有数据库并启动 API、worker、Web。首次管理员凭证按 API 提示获取,不把凭证写到仓库。以下是业务配置,不是需要写进框架内核的逻辑。
|
|
74
|
+
以下体验需要采购模板;空白模板不会出现采购页面和审批配置。先按[开发步骤](../getting-started/workflow.md)准备已有数据库并启动 API、worker、Web。首次管理员凭证按 API 提示获取,不把凭证写到仓库。以下是业务配置,不是需要写进框架内核的逻辑。
|
|
71
75
|
|
|
72
76
|
| 对象 | 配置要点 |
|
|
73
77
|
| --- | --- |
|
|
74
|
-
| 采购员 |
|
|
78
|
+
| 采购员 | 创建/查看/提交/取消采购单等所需能力,所属部门,以及采购单数据范围;配置专业复核需 `PurchaseOrder.setReviewers` 和 `ApprovalMember.read` |
|
|
75
79
|
| 智能助理使用人 | 显式授予 `Sys.ai.use`;编辑用途还需 `PurchaseOrder.explainPurpose` |
|
|
76
|
-
| 模型管理者 | `Sys.ai.models.manage`;在 `/admin/#/models` 配置并启用 `main`,部署主密钥见[智能能力指南](intelligence.md) |
|
|
80
|
+
| 模型管理者 | `Sys.ai.models.manage`;在 `/admin/#/models` 配置并启用 `main`,部署主密钥见[智能能力指南](../guides/intelligence.md) |
|
|
77
81
|
| 助理性格管理者 | `Sys.ai.personalities.manage`;在 `/admin/#/personalities` 编辑预设、自定义性格、设置默认并试聊 |
|
|
78
82
|
| AI 预审角色 | 创建 code 为 `purchase_ai_reviewer` 的角色,授予 `PurchaseOrder.read/returnForChanges` 及覆盖申请范围的数据权限;本例 AI 仅预审,不执行最终 approve |
|
|
79
83
|
| 经理 | `PurchaseOrder.read/approve/reject/returnForChanges`,适当数据范围,`approve` 的 `ceiling` 额度参数 |
|
|
84
|
+
| 专业协作人 | 真实账号及同样的审批能力、范围、额度;由名录管理员通过 `ApprovalMember.create` 登记公开业务姓名、专业说明;需要在任务中选人时授予 `ApprovalMember.read` |
|
|
85
|
+
| 名录管理员 | 显式授予 `ApprovalMember.create/update/retire/read`;登记、更新资料或退役不修改用户角色,不自动给采购参与者授予 `User.read` |
|
|
80
86
|
| 部门 | 将采购员所属部门的负责人设为经理;不能为空 |
|
|
81
|
-
| 财务 | 角色 **code** 为 `finance
|
|
87
|
+
| 财务 | 角色 **code** 为 `finance`,给至少两位实际用户分配以体验共享候选池;配置审批所需权限、范围及足够额度;领取后才能处理 |
|
|
82
88
|
| 库存台账 | 预先登记与采购明细相同的 sku,数量满足演示检查 |
|
|
83
89
|
| 连接器环境 | fake 阶段使用 worker 注册的 fake;真实环境另配 host 对应密钥及回调签名密钥 |
|
|
84
90
|
|
|
85
91
|
经理与提单人必须是不同用户。配置“负责人”决定选谁;配置角色能力与范围决定此人能否完成结论命令,两者都需要。金额 **大于** 50,000 才追加财务步骤,恰好 50,000 不满足 gt 条件。
|
|
86
92
|
|
|
93
|
+
专业复核默认关闭,兼容旧单据的空字段;开启后需选择 1–5 位不同且非提单人的有效名录成员。`setReviewers` 只允许编辑草稿;创建可暂存未选完整的草稿,但 `submit` 会重新核对名单,拒绝保存后退役的成员。名录退役不修改已经提交的名单快照,也不撤销在途任务;账号停用和实际审批权限继续由审批引擎核验。名录的用户引用不能换绑,已退役记录保留且不支持重新启用。
|
|
94
|
+
|
|
95
|
+
`ApprovalMember` 使用 `scope.none()`,没有部门或本人字段。名录管理员和需要选人的采购参与者都必须显式获得该聚合的 `all` 数据范围;仅授予 `ApprovalMember.read` 仍看不到成员,草稿的专业名单校验也会因读范围不足拒绝。这里的 `all` 始终受当前租户隔离,不要求也不授予 `User.read`。
|
|
96
|
+
|
|
97
|
+
经理、专业会签和财务节点均显式启用转办、委派与前置/后置/并行加签,节点政策随每次提交保存。转办交接处理责任;委派处理完成后归还原审批人;加签增加必须完成的签署任务,前置签署完成前原任务等待,后置签署在原任务通过后才开放。任务动作和决定都应携带页面当前 `revision`,冲突后刷新待办,不自动重试旧决定。AI 人工接管继续采用 AI 节点自身政策,不能因为后续经理节点启用动作而越权操作。
|
|
98
|
+
|
|
99
|
+
逐项体验时用不同采购单分别记录:普通经理审批、专业双人会签、恰好 50,000 元跳过财务、超过 50,000 元财务领取/释放/再领取、转办、委派归还、三种加签,以及退回重提和申请人取消。每一步同时查看采购状态、当前可操作/等待任务及动作历史;专业会签只通过一人或必签尚未完成时采购单应仍待审批,最终通过才产生库存锁。浏览器验收需切换真实账号进行,自动测试通过不能替代页面验证。
|
|
100
|
+
|
|
87
101
|
可分别准备“采购办公用品”、说明实际使用场景的完整用途、含“紧急采购”的用途,体验退回、通过本节点和转人工。真实模型的判定应按实际样例验收;故障默认转人工,不能当作正常审查通过。详情页显示历史理由、修改建议及审核轮次;用途编辑框的未保存内容会经 SDK 一起传给助理,服务端重新核对当前单据与权限。
|
|
88
102
|
|
|
89
103
|
拥有 `Sys.ai.use` 的用户在所有业务页面右下角都有助理入口,点击打开悬浮对话窗,收起后继续执行当前任务。工作台、采购列表/详情、库存和待办分别提供页面语义;列表带筛选与可见记录,详情带单据引用和未保存用途。应用壳只挂载一个 `AssistantScope`,业务页面通过 `AssistantPage` 注册上下文,所以站内切页保留对话,但下一条消息使用新页面的快照。不同页面拥有独立的 `AiPageContext` 身份,旧任务要求补充页面时不能提交新页面代替。登录页和独立的 `/admin/` 不挂载业务助理。
|
|
@@ -96,11 +110,11 @@ submit 返回成功只表示单据和事件已提交,不表示待办、ERP 或
|
|
|
96
110
|
|
|
97
111
|
`apps/web` 中对字段和按钮的中文名称属于业务文案。图谱名称来自 `cage.config.json` 的 `graph.labels`,比如 `command:PurchaseOrder.submit` 对应“提交审批”。替换业务时别忘了更新这些名称,不要改英文能力标识来做翻译。
|
|
98
112
|
|
|
99
|
-
导出按钮是调用通用异步接口的一个例子;框架不规定所有业务都要放相同的按钮。导入、导出、任务列表与错误报告应按客户页面需要接入,参考[开发步骤](workflow.md)中的客户端方法。
|
|
113
|
+
导出按钮是调用通用异步接口的一个例子;框架不规定所有业务都要放相同的按钮。导入、导出、任务列表与错误报告应按客户页面需要接入,参考[开发步骤](../getting-started/workflow.md)中的客户端方法。
|
|
100
114
|
|
|
101
115
|
## 把示例改成自己的业务
|
|
102
116
|
|
|
103
|
-
先按[目录规范](project-structure.md)新增自己的领域模块和前端模块,完成一条完整链路,再逐步移除不需要的采购导出、路由、反应、连接器和页面引用。`@app/domain`、`@app/contracts` 是应用内包名,可以保留;npm 框架包也不需要重命名。
|
|
117
|
+
先按[目录规范](../reference/project-structure.md)新增自己的领域模块和前端模块,完成一条完整链路,再逐步移除不需要的采购导出、路由、反应、连接器和页面引用。`@app/domain`、`@app/contracts` 是应用内包名,可以保留;npm 框架包也不需要重命名。
|
|
104
118
|
|
|
105
119
|
若数据库尚未投入使用,可为新应用审查和规划初始结构;只要迁移已执行或已发布,就不能通过覆盖 `0001_init.sql` 来假装采购表从未存在。用新迁移表达后续变更,并明确已有数据处理方式。
|
|
106
120
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 架构与执行模型
|
|
2
2
|
|
|
3
|
-
[返回学习入口](README.md) · [下一篇:原语](primitives.md)
|
|
3
|
+
[返回学习入口](../README.md) · [下一篇:原语](../reference/primitives.md)
|
|
4
4
|
|
|
5
5
|
## 业务声明是一份可执行规格
|
|
6
6
|
|
|
@@ -34,7 +34,7 @@ domain/index.ts 导出的声明
|
|
|
34
34
|
|
|
35
35
|
一个采购单可以有明细列表,也可以引用库存等其他聚合。跨聚合写入要调用对方的命令,通过反应表达关系,不能拿到对方数据库表直接更新。
|
|
36
36
|
|
|
37
|
-
对象关联统一写在 fields:ref 为本表外键,hasMany 依据目标外键反查,manyToMany 自动生成中间表。多对多链接属于来源对象的命令事务,但不授权修改目标内容;公开关联通过两侧安全视图分页读取。完整模式见[对象关系](relations.md)。
|
|
37
|
+
对象关联统一写在 fields:ref 为本表外键,hasMany 依据目标外键反查,manyToMany 自动生成中间表。多对多链接属于来源对象的命令事务,但不授权修改目标内容;公开关联通过两侧安全视图分页读取。完整模式见[对象关系](../guides/relations.md)。
|
|
38
38
|
|
|
39
39
|
## 一次写入的完整路径
|
|
40
40
|
|
|
@@ -69,7 +69,7 @@ worker → 消费 outbox → 异步反应 / 审批 / 连接器
|
|
|
69
69
|
|
|
70
70
|
资源标签 `acquires: 'inventory.lock'`、`releases: 'inventory.lock'` 为图规则提供证据。它们不会自动加锁或释放,真正效果来自 `acquireLock/releaseLock` 命令。
|
|
71
71
|
|
|
72
|
-
有界批量步骤可用 `reactionEach`:声明目标命令、最大项数、唯一排序键和映射,按稳定顺序串行调用。`order` 表达同一事件的步骤依赖,同事务链仍整体回滚。具体声明见[原语指南](primitives.md#有界集合与步骤顺序)。
|
|
72
|
+
有界批量步骤可用 `reactionEach`:声明目标命令、最大项数、唯一排序键和映射,按稳定顺序串行调用。`order` 表达同一事件的步骤依赖,同事务链仍整体回滚。具体声明见[原语指南](../reference/primitives.md#有界集合与步骤顺序)。
|
|
73
73
|
|
|
74
74
|
出站分成外部执行与短事务回写两个阶段。框架先领取本地执行租约,用 `${tenantId}:${declaredKey}` 作为远端幂等键,可靠保存回执后再在短事务调用成功命令;回写失败可复用已保存回执。远端必须实际支持幂等,框架不能承诺任意外部系统恰好一次。不可安全重试的写入结果未知时隔离并要求核对,不能把超时当作远端未执行。
|
|
75
75
|
|
|
@@ -141,7 +141,7 @@ APP、READ、MIGRATE 指向**同一个已经存在的数据库**,用不同账
|
|
|
141
141
|
|
|
142
142
|
## 本体图为什么有这些连线
|
|
143
143
|
|
|
144
|
-
图谱抽取命令状态迁移与事件、反应调用、审批 start/cancelOn、约束/路由字段、查询读依赖和外部映射;另以虚线标记按标识推导的所属聚合。关系解释区分显式声明、规则推导与文本匹配,箭头要按各自含义阅读。开发前可用[本体上下文工具](ontology-context.md)检索相关业务与源码。
|
|
144
|
+
图谱抽取命令状态迁移与事件、反应调用、审批 start/cancelOn、约束/路由字段、查询读依赖和外部映射;另以虚线标记按标识推导的所属聚合。关系解释区分显式声明、规则推导与文本匹配,箭头要按各自含义阅读。开发前可用[本体上下文工具](../tools/ontology-context.md)检索相关业务与源码。
|
|
145
145
|
|
|
146
146
|
`cage analyze` 将图写到 `.cage/ontology.json`;`cage graph` 提供浏览器工作台。中文显示名写在 `cage.config.json` 的 `graph.labels`,例如 `"command:PurchaseOrder.submit": "提交审批"`。显示名不更改能力标识、源码符号或表名。
|
|
147
147
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# 第一条完整业务链
|
|
2
|
+
|
|
3
|
+
[学习入口](../README.md) · [框架开发 Skill](../skills/cage-development/SKILL.md) · [原语参考](../reference/primitives.md)
|
|
4
|
+
|
|
5
|
+
本课教会你把声明、契约、页面和持久数据连接起来。以“部门便笺”解释方法:创建便笺、查看列表与详情、修改标题、归档。这个对象只用于教学;开发真实业务时替换对象含义和规则,不需要向用户项目添加便笺。
|
|
6
|
+
|
|
7
|
+
## 先确定业务,再选择声明
|
|
8
|
+
|
|
9
|
+
一条便笺有标题、所属部门和创建人,状态为 active 或 archived。创建、修改标题、归档都是命令;列表和详情使用生成的读取接口。标题不能为空是不变量,归档后不能修改由 update 的 from 表达。
|
|
10
|
+
|
|
11
|
+
归属字段使用 `deptId: ref('Dept')` 与 `createdBy: ref('User')`,内核新建时根据当前主体填写;`scope.deptSubtree('deptId')` 描述范围字段。实际授权仍由角色配置决定,普通用户不能通过提交任意部门 ID 把记录放进其他部门。公司、项目组等自定义归属不能仅改一个字段名字就假定沿用同样规则,应核对当前支持的范围模型。
|
|
12
|
+
|
|
13
|
+
完整领域代码见[便笺声明](../examples/note/note.ts)。依次阅读聚合、createNote、updateNote、archiveNote,注意命令导出名不同于 `name` 指定的能力名称。
|
|
14
|
+
|
|
15
|
+
## 让声明进入应用
|
|
16
|
+
|
|
17
|
+
把本课声明放入应用的 `packages/domain/src/modules/note/note.ts`,在模块 index.ts 导出它,再从 domain 总入口导出模块。保留原有组织模型及其他业务;不要让不同模块的 `export *` 带来重名导出。
|
|
18
|
+
|
|
19
|
+
在应用根目录执行:
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
pnpm exec cage check
|
|
23
|
+
pnpm gen
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
检查生成的 Note 契约、图节点、路由信息及迁移草稿。应能找到 `note.create`、`note.update`、`note.archive`、`note.list` 和 `note.get`。这里验证的是注册和生成;尚未证明数据库里有表或用户有权执行。
|
|
27
|
+
|
|
28
|
+
数据库初次准备按[开发步骤](workflow.md#第一次启动应用)进行;已有项目复用现有配置。审查新迁移后执行 `pnpm migrate`,再按当前环境启动/重启 API 与 worker。不要改已执行迁移,也不要为学习案例覆盖业务数据库配置。
|
|
29
|
+
|
|
30
|
+
## 给真实主体配置权限
|
|
31
|
+
|
|
32
|
+
在独立验收环境准备有能力的主体、同部门另一主体和范围外主体。为所需角色明确配置 `Note.read`、`Note.create`、`Note.update`、`Note.archive` 及对应数据范围。按业务分别选择本人、部门或部门及下级等范围;创建一个角色名称不会自动获得这些能力。
|
|
33
|
+
|
|
34
|
+
`scope.none()` 缺少部门/创建人范围表达,不能拿它替代“仅看本部门”。当前框架合并多角色能力与范围;只切换前端门户不会把后端权限自动缩减到所选角色。产品确需切换执行身份时,先确认当前公开协议是否支持该语义,不能通过本地 role 变量伪装授权。
|
|
35
|
+
|
|
36
|
+
## 页面调用真实契约
|
|
37
|
+
|
|
38
|
+
[列表与新建页面](../examples/note/notes-page.tsx)演示创建成功后重新读取服务端列表;[详情页面](../examples/note/note-detail-page.tsx)从 URL 读取 ID,直接加载服务端记录,修改和归档带上读取到的版本。它们是可编译的完整教学组件,样式由应用自行设计。
|
|
39
|
+
|
|
40
|
+
将组件放到 `apps/web/src/modules/note/pages`,从模块 index.ts 公开导出。再在已有 app 路由组合层导入模块入口,在登录后的 Outlet 内加入以下路由片段:
|
|
41
|
+
|
|
42
|
+
```tsx
|
|
43
|
+
<Route path="notes" element={<RequireCapability capability="Note.read" home="/workspace"><NotesPage /></RequireCapability>} />
|
|
44
|
+
<Route path="notes/:id" element={<RequireCapability capability="Note.read" home="/workspace"><NoteDetailPage /></RequireCapability>} />
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
片段中的 Route、RequireCapability 和两个页面要从当前应用真实入口导入;保留已有会话恢复、退出清理和 404。新增菜单使用 Link 指向 `/notes`。完整导航要求见[路由与浏览器验收](../guides/routing.md)。
|
|
48
|
+
|
|
49
|
+
创建按钮检查 `Note.create` 只是界面提示。提交后检查 `result.ok`,成功才清空输入并重新读取;失败展示错误并保留输入。详情请求可能返回范围外的 NOT_FOUND,不能显示“该记录已删除”这种未经证明的结论。编辑冲突后重新读取版本并让用户核对,不静默覆盖他人修改。
|
|
50
|
+
|
|
51
|
+
## 沿操作观察结果
|
|
52
|
+
|
|
53
|
+
| 操作 | 预期观察 | 若失败,优先检查 |
|
|
54
|
+
| --- | --- | --- |
|
|
55
|
+
| 有权用户创建便笺 | 真实请求成功,服务端返回 ID,独立 list/get 可读 | 契约调用、迁移、能力与归属字段 |
|
|
56
|
+
| 新标签页打开详情 URL | 重新请求后显示同一条记录 | URL ID、会话恢复、读取范围 |
|
|
57
|
+
| 另一位有同部门范围的用户查看 | 服务端读取同一条记录 | 实际部门、角色范围、接口结果 |
|
|
58
|
+
| 范围外主体直接请求详情 | 请求拒绝或安全地返回不可见 | 服务端范围,不能只看菜单是否隐藏 |
|
|
59
|
+
| 两个页面使用同一旧版本修改 | 后一次得到冲突,不覆盖新值 | expectedVersion 和页面刷新策略 |
|
|
60
|
+
| 归档后再次修改 | 业务状态拒绝;页面重新读取为 archived | from/to 与真实状态 |
|
|
61
|
+
|
|
62
|
+
运行受影响包的类型检查和构建、生成一致性与必要业务测试,再按浏览器配方操作。未配置测试账号、未连接独立测试库或没有浏览器能力时,写清未执行项;不要用编译通过替代本表结果。
|
|
63
|
+
|
|
64
|
+
## 从本课迁移到其他业务
|
|
65
|
+
|
|
66
|
+
先替换对象、字段、归属与动作,保留“声明 → 导出 → 生成 → 运行配置 → 契约 → 观察结果”的方法。只有需求需要时再增加能力:审批学习[审批单元](../guides/approvals.md),附件学习[文件单元](../guides/files.md),关联学习[关系单元](../guides/relations.md),跨对象联动参考[事务选择](architecture.md#同事务与异步的选择)。完整组合范例见[采购导读](../examples/purchase-example.md),无需一次复制全部采购实现。
|