@yss123/cage-cli 0.3.2 → 0.4.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 (134) hide show
  1. package/README.md +1 -1
  2. package/dist/analyze.js +3 -0
  3. package/dist/analyze.js.map +1 -1
  4. package/dist/capabilities.js +10 -0
  5. package/dist/capabilities.js.map +1 -1
  6. package/dist/config.d.ts +4 -0
  7. package/dist/config.js.map +1 -1
  8. package/dist/context.js +2 -2
  9. package/dist/context.js.map +1 -1
  10. package/dist/doctor.js +1 -1
  11. package/dist/doctor.js.map +1 -1
  12. package/dist/findings.d.ts +1 -1
  13. package/dist/findings.js +2 -2
  14. package/dist/findings.js.map +1 -1
  15. package/dist/graph-business.d.ts +11 -8
  16. package/dist/graph-business.js +16 -16
  17. package/dist/graph-business.js.map +1 -1
  18. package/dist/graph-relations.js +3 -5
  19. package/dist/graph-relations.js.map +1 -1
  20. package/dist/impact.js +1 -4
  21. package/dist/impact.js.map +1 -1
  22. package/dist/ontology-names.d.ts +17 -0
  23. package/dist/ontology-names.js +42 -0
  24. package/dist/ontology-names.js.map +1 -0
  25. package/dist/sync.d.ts +5 -1
  26. package/dist/sync.js +16 -9
  27. package/dist/sync.js.map +1 -1
  28. package/graph-ui/app.js +3 -2
  29. package/graph-ui/business.js +11 -3
  30. package/graph-ui/graph-business.js +16 -16
  31. package/graph-ui/graph-relations.js +3 -5
  32. package/package.json +6 -6
  33. package/templates/app/.agents/skills/cage-development/SKILL.md +1 -1
  34. package/templates/app/AGENTS.md +12 -9
  35. package/templates/app/README.md +2 -2
  36. package/templates/app/apps/api/package.json +2 -2
  37. package/templates/app/apps/web/package.json +3 -1
  38. package/templates/app/apps/web/src/app/styles/global.css +12 -11
  39. package/templates/app/apps/web/src/main.tsx +18 -7
  40. package/templates/app/apps/web/src/shared/lib/error-message.ts +1 -1
  41. package/templates/app/apps/web/src/shared/ui/approval-member-picker.tsx +1 -1
  42. package/templates/app/cage.config.json +2 -1
  43. package/templates/app/docs/README.md +6 -4
  44. package/templates/app/docs/development/README.md +21 -5
  45. package/templates/app/docs/development/examples/note/note.ts +8 -4
  46. package/templates/app/docs/development/examples/purchase-example.md +5 -5
  47. package/templates/app/docs/development/getting-started/architecture.md +5 -5
  48. package/templates/app/docs/development/getting-started/workflow.md +17 -23
  49. package/templates/app/docs/development/guides/approvals.md +4 -4
  50. package/templates/app/docs/development/guides/business-collaboration.md +3 -1
  51. package/templates/app/docs/development/guides/frontend-ui.md +30 -0
  52. package/templates/app/docs/development/guides/intelligence.md +14 -12
  53. package/templates/app/docs/development/guides/ontology-modeling.md +83 -0
  54. package/templates/app/docs/development/guides/recipes.md +28 -14
  55. package/templates/app/docs/development/guides/relations.md +31 -8
  56. package/templates/app/docs/development/reference/commenting.md +7 -4
  57. package/templates/app/docs/development/reference/primitives.md +5 -3
  58. package/templates/app/docs/development/skills/cage-development/SKILL.md +30 -20
  59. package/templates/app/docs/development/tools/diagnostics.md +4 -1
  60. package/templates/app/docs/development/tools/ontology-context.md +1 -1
  61. package/templates/app/docs/development/tools/troubleshooting.md +3 -3
  62. package/templates/app/docs/errors/CAGE-APPR-007.md +1 -1
  63. package/templates/app/docs/errors/CAGE-GRANT-001.md +18 -12
  64. package/templates/app/docs/errors/CAGE-GRANT-002.md +2 -2
  65. package/templates/app/docs/errors/CAGE-GRANT-003.md +3 -3
  66. package/templates/app/docs/errors/CAGE-ONTOLOGY-001.md +52 -0
  67. package/templates/app/docs/errors/CAGE-STATE-003.md +1 -1
  68. package/templates/app/docs/errors/CAGE-SYNC-001.md +2 -2
  69. package/templates/app/docs/errors/README.md +7 -1
  70. package/templates/app/docs/errors/runtime.md +1 -1
  71. package/templates/app/package.json +3 -3
  72. package/templates/app/packages/contracts/package.json +1 -1
  73. package/templates/app/packages/contracts/src/aggregates/role/index.ts +2 -2
  74. package/templates/app/packages/contracts/src/aggregates/user/index.ts +3 -3
  75. package/templates/app/packages/domain/package.json +5 -5
  76. package/templates/app/packages/domain/src/modules/purchase/approval-members.ts +1 -1
  77. package/templates/app/packages/domain/src/modules/purchase/approvals.ts +2 -2
  78. package/templates/app/packages/domain/src/modules/purchase/intelligence.test.ts +4 -4
  79. package/templates/app/packages/domain/src/modules/purchase/purchase-order.test.ts +10 -17
  80. package/templates/app/packages/domain/src/modules/purchase/purchase-order.ts +4 -5
  81. package/templates/app/packages/domain/src/workflows/purchase-process.ts +1 -1
  82. package/templates/blank/.agents/skills/cage-development/SKILL.md +1 -1
  83. package/templates/blank/AGENTS.md +12 -9
  84. package/templates/blank/apps/api/package.json +1 -1
  85. package/templates/blank/apps/web/package.json +3 -1
  86. package/templates/blank/apps/web/src/app/styles/global.css +6 -5
  87. package/templates/blank/apps/web/src/main.tsx +18 -7
  88. package/templates/blank/cage.config.json +1 -0
  89. package/templates/blank/docs/README.md +6 -4
  90. package/templates/blank/docs/development/README.md +21 -5
  91. package/templates/blank/docs/development/examples/note/note.ts +8 -4
  92. package/templates/blank/docs/development/examples/purchase-example.md +5 -5
  93. package/templates/blank/docs/development/getting-started/architecture.md +5 -5
  94. package/templates/blank/docs/development/getting-started/workflow.md +17 -23
  95. package/templates/blank/docs/development/guides/approvals.md +4 -4
  96. package/templates/blank/docs/development/guides/business-collaboration.md +3 -1
  97. package/templates/blank/docs/development/guides/frontend-ui.md +30 -0
  98. package/templates/blank/docs/development/guides/intelligence.md +14 -12
  99. package/templates/blank/docs/development/guides/ontology-modeling.md +83 -0
  100. package/templates/blank/docs/development/guides/recipes.md +28 -14
  101. package/templates/blank/docs/development/guides/relations.md +31 -8
  102. package/templates/blank/docs/development/reference/commenting.md +7 -4
  103. package/templates/blank/docs/development/reference/primitives.md +5 -3
  104. package/templates/blank/docs/development/skills/cage-development/SKILL.md +30 -20
  105. package/templates/blank/docs/development/tools/diagnostics.md +4 -1
  106. package/templates/blank/docs/development/tools/ontology-context.md +1 -1
  107. package/templates/blank/docs/development/tools/troubleshooting.md +3 -3
  108. package/templates/blank/docs/errors/CAGE-APPR-007.md +1 -1
  109. package/templates/blank/docs/errors/CAGE-GRANT-001.md +18 -12
  110. package/templates/blank/docs/errors/CAGE-GRANT-002.md +2 -2
  111. package/templates/blank/docs/errors/CAGE-GRANT-003.md +3 -3
  112. package/templates/blank/docs/errors/CAGE-ONTOLOGY-001.md +52 -0
  113. package/templates/blank/docs/errors/CAGE-STATE-003.md +1 -1
  114. package/templates/blank/docs/errors/CAGE-SYNC-001.md +2 -2
  115. package/templates/blank/docs/errors/README.md +7 -1
  116. package/templates/blank/docs/errors/runtime.md +1 -1
  117. package/templates/blank/docs/examples/purchase-admin/apps/web/src/app/styles/global.css +12 -11
  118. package/templates/blank/docs/examples/purchase-admin/apps/web/src/main.tsx +18 -7
  119. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/lib/error-message.ts +1 -1
  120. package/templates/blank/docs/examples/purchase-admin/apps/web/src/shared/ui/approval-member-picker.tsx +1 -1
  121. package/templates/blank/docs/examples/purchase-admin/cage.config.json +2 -1
  122. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/role/index.ts +2 -2
  123. package/templates/blank/docs/examples/purchase-admin/packages/contracts/src/aggregates/user/index.ts +3 -3
  124. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approval-members.ts +1 -1
  125. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/approvals.ts +2 -2
  126. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/intelligence.test.ts +4 -4
  127. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.test.ts +10 -17
  128. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/modules/purchase/purchase-order.ts +4 -5
  129. package/templates/blank/docs/examples/purchase-admin/packages/domain/src/workflows/purchase-process.ts +1 -1
  130. package/templates/blank/package.json +3 -3
  131. package/templates/blank/packages/contracts/package.json +1 -1
  132. package/templates/blank/packages/contracts/src/aggregates/role/index.ts +2 -2
  133. package/templates/blank/packages/contracts/src/aggregates/user/index.ts +3 -3
  134. package/templates/blank/packages/domain/package.json +5 -5
@@ -1,18 +1,29 @@
1
1
  /**
2
- * 采购 Web 的浏览器启动入口:创建共享查询缓存,将应用路由挂到 root DOM 节点。
3
- * 契约客户端负责网络与会话协议,页面只在 Provider 内消费 hooks;这里不注册领域或启动后端。
4
- * 全局样式在此加载,账号切换时的缓存清理由 useSession 和应用壳负责。
2
+ * 采购 Web 的浏览器启动入口:装配 Ant Design 中文主题、共享查询缓存并挂载应用路由。
3
+ * 页面通过 Provider 消费 UI 上下文、生成契约客户端和查询 hooks;入口不注册领域或启动后端。
4
+ * AntD reset 与业务全局样式在此加载,账号切换时的缓存清理由 useSession 和应用壳负责。
5
5
  */
6
- // 唯一浏览器启动入口:挂载 React、全局 Provider 和样式;业务从 app 组合进入。
6
+ // 唯一浏览器启动入口:挂载 React、Ant Design 与查询 Provider;业务从 app 组合进入。
7
7
  import { createRoot } from 'react-dom/client';
8
8
  import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
9
+ import { App as AntdApp, ConfigProvider } from 'antd';
10
+ import zhCN from 'antd/locale/zh_CN';
9
11
  import { App } from './app/app';
12
+ import 'antd/dist/reset.css';
10
13
  import './app/styles/global.css';
11
14
 
12
15
  const queryClient = new QueryClient();
13
16
 
17
+ /**
18
+ * ConfigProvider 提供简体中文文案与统一绿色主题;AntdApp 为 message、modal、notification 等反馈 API 提供上下文。
19
+ * QueryClientProvider 继续承载生成查询 hooks 的缓存;Provider 均只创建一次并覆盖整套路由。
20
+ */
14
21
  createRoot(document.getElementById('root')!).render(
15
- <QueryClientProvider client={queryClient}>
16
- <App />
17
- </QueryClientProvider>,
22
+ <ConfigProvider locale={zhCN} theme={{ token: { colorPrimary: '#173e35', borderRadius: 4 } }}>
23
+ <AntdApp>
24
+ <QueryClientProvider client={queryClient}>
25
+ <App />
26
+ </QueryClientProvider>
27
+ </AntdApp>
28
+ </ConfigProvider>,
18
29
  );
@@ -16,7 +16,7 @@ export function errorMessage(r: Extract<CageResult<unknown, string>, { ok: false
16
16
  INVALID_INPUT: '请检查填写内容',
17
17
  CONFLICT: '数据或任务已更新,请刷新后重试',
18
18
  UNAUTHENTICATED: '登录已过期',
19
- CONSTRAINT_VIOLATED: '未满足审批条件,请检查审批人或额度',
19
+ CONSTRAINT_VIOLATED: '未满足业务条件,请检查操作人及具体原因',
20
20
  STOCK_SHORT: '库存不足',
21
21
  REVIEWERS_REQUIRED: '请为专业会签选择 1–5 位协作人',
22
22
  REVIEWERS_NOT_REQUIRED: '取消专业会签时请清空复核人员',
@@ -46,6 +46,6 @@ export function ApprovalMemberPicker({ value, onChange, max = 5, exclude = [], d
46
46
  {query.data && entries.length === 0 && <p className="muted">当前页没有匹配人员,请调整筛选或联系名录维护人。</p>}
47
47
  {(query.data?.total ?? 0) > 200 && <div className="actions"><button type="button" disabled={page === 1} onClick={() => setPage(page - 1)}>上一页</button>
48
48
  <span>第 {page} 页</span><button type="button" disabled={page * 200 >= query.data!.total} onClick={() => setPage(page + 1)}>下一页</button></div>}
49
- <small className="muted">名录只提供选人信息,处理权限、数据范围和最终额度仍以服务端校验为准。</small>
49
+ <small className="muted">名录只提供选人信息,处理权限和数据范围仍以服务端校验为准。</small>
50
50
  </fieldset>;
51
51
  }
@@ -5,6 +5,7 @@
5
5
  "typecheck": ["packages/domain", "packages/contracts", "apps/api", "apps/web"],
6
6
  "graph": {
7
7
  "title": "采购业务图谱",
8
+ "naming": "required",
8
9
  "labels": {
9
10
  "module:purchase": "采购管理",
10
11
  "module:inventory": "库存管理",
@@ -125,7 +126,7 @@
125
126
  "state:Role.archived": "已停用",
126
127
  "field:Role.code": "角色编码",
127
128
  "field:Role.name": "角色名称",
128
- "field:Role.capabilities": "能力点与额度",
129
+ "field:Role.capabilities": "能力点",
129
130
  "field:Role.scopes": "数据访问范围",
130
131
  "capability:Role.read": "查看角色",
131
132
  "capability:Role.export": "导出角色",
@@ -31,7 +31,7 @@ export const RoleInputs = {
31
31
  };
32
32
 
33
33
  /** Role.archive 可能返回的错误码 */
34
- export type RoleArchiveError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
34
+ export type RoleArchiveError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
35
35
  /** Role.create 可能返回的错误码 */
36
36
  export type RoleCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT';
37
37
  /** Role.grantCapability 可能返回的错误码 */
@@ -39,7 +39,7 @@ export type RoleGrantCapabilityError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOM
39
39
  /** Role.rename 可能返回的错误码 */
40
40
  export type RoleRenameError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
41
41
  /** Role.revokeCapability 可能返回的错误码 */
42
- export type RoleRevokeCapabilityError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
42
+ export type RoleRevokeCapabilityError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
43
43
  /** Role.setScope 可能返回的错误码 */
44
44
  export type RoleSetScopeError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
45
45
 
@@ -38,11 +38,11 @@ export type UserAssignRolesError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_
38
38
  /** User.create 可能返回的错误码 */
39
39
  export type UserCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT';
40
40
  /** User.disable 可能返回的错误码 */
41
- export type UserDisableError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
41
+ export type UserDisableError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
42
42
  /** User.enable 可能返回的错误码 */
43
- export type UserEnableError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
43
+ export type UserEnableError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
44
44
  /** User.setPassword 可能返回的错误码 */
45
- export type UserSetPasswordError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
45
+ export type UserSetPasswordError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
46
46
  /** User.update 可能返回的错误码 */
47
47
  export type UserUpdateError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
48
48
 
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 采购协作名录:由业务管理员登记可参与采购复核的账号、公开业务姓名和专业领域,供草稿选人及任务操作使用。
3
3
  * 聚合通过标准命令维护,浏览器读取 safe.approval_member;名录与 sys.user 的私密资料、角色授权分离。
4
- * 名录在同租户内向具 ApprovalMember.read 且显式获 all 范围的采购参与者公开;加入名录不授予审批权限、范围或额度。
4
+ * 名录在同租户内向具 ApprovalMember.read 且显式获 all 范围的采购参与者公开;加入名录不授予审批权限或范围。
5
5
  */
6
6
  import { aggregate, command, ref, scope, states, text } from '@yss123/cage-kernel';
7
7
  import { z } from 'zod';
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * 采购实际审批路由:AI 预审、部门审核、可选专业会签和大额财务候选受理,共用一个采购流程实例。
3
3
  * 人工节点显式开放转办、委派和前后/并行加签;步骤/政策随提交保存,旧实例保持自己的快照。
4
- * 路由只决定任务分配与签序,最终结论仍执行领域命令的职责分离、额度与库存约束。
4
+ * 路由只决定任务分配与签序,最终结论仍执行领域命令的职责分离与库存约束。
5
5
  */
6
6
  import { approval } from '@yss123/cage-kernel';
7
7
  import { approver, eq, gt, route, step, when } from '@yss123/cage-approval';
@@ -28,7 +28,7 @@ export const poApproval = approval(PurchaseOrder, {
28
28
  * needsSpecialistReview=true 才增加专业会签,名单来自提交快照且全部人必须通过;金额严格大于 50000 元才增加财务节点。
29
29
  * AI 只读取 reason/amount/items,pass 仅推进到经理,return 回到草稿,manual 在当前节点交部门负责人。
30
30
  * 模型失败同样转人工,最多四轮/五次工具调用/120 秒;不以失败兜底为自动批准。
31
- * 财务共享一项未分配任务,finance 当前成员须先领取;所有人工动作仍需真实命令能力、范围、额度和职责分离。
31
+ * 财务共享一项未分配任务,finance 当前成员须先领取;所有人工动作仍需真实命令能力、范围和职责分离。
32
32
  * AI 人工接管保持原节点规则;经理、专业和财务才开放三项管理动作,新实例自动保存允许的加签位置。
33
33
  */
34
34
  export const poRoute = route('po-approval', [
@@ -25,7 +25,7 @@ beforeAll(async () => {
25
25
  env = await createTestEnv({ domain: { ...purchase, intelligence }, migrations: readMigrations(fileURLToPath(new URL('../../../../../db/migrations/', import.meta.url))) });
26
26
  buyer = await env.user({ caps: ['PurchaseOrder.read', 'PurchaseOrder.create', 'PurchaseOrder.explainPurpose', 'PurchaseOrder.submit', 'PurchaseOrder.setReviewers', 'ApprovalMember.read'],
27
27
  scopes: { ApprovalMember: 'all' } });
28
- reviewer = await env.user({ caps: ['PurchaseOrder.read', 'PurchaseOrder.approve', 'PurchaseOrder.returnForChanges'], scopes: { PurchaseOrder: 'dept' }, params: { 'PurchaseOrder.approve': { ceiling: 50000 } } });
28
+ reviewer = await env.user({ caps: ['PurchaseOrder.read', 'PurchaseOrder.approve', 'PurchaseOrder.returnForChanges'], scopes: { PurchaseOrder: 'dept' } });
29
29
  await env.kernel.execute('Dept.update', { id: env.rootDeptId, leaderId: reviewer.id }, env.system());
30
30
  await env.kernel.execute('Inventory.create', { sku: 'AI-01', available: 100 }, env.system());
31
31
  const role = await env.kernel.execute('Role.create', { code: 'purchase_ai_reviewer', name: '材料预审角色' }, env.system());
@@ -39,12 +39,12 @@ beforeAll(async () => {
39
39
  afterAll(() => env?.close());
40
40
 
41
41
  /**
42
- * 通过组织和业务命令登记同部门协作人;10 万元额度只供本隔离库验证大额流程,名录本身不授予能力。
42
+ * 通过组织和业务命令登记同部门协作人;名录本身不授予能力。
43
43
  * 专业和财务共享这些真实审批能力,财务候选身份由额外的 finance 角色单独赋予。
44
44
  */
45
45
  async function collaborator(specialty: string) {
46
46
  const user = await env.user({ caps: ['PurchaseOrder.read', 'PurchaseOrder.approve', 'PurchaseOrder.reject', 'PurchaseOrder.returnForChanges'],
47
- scopes: { PurchaseOrder: 'dept' }, params: { 'PurchaseOrder.approve': { ceiling: 100000 } } });
47
+ scopes: { PurchaseOrder: 'dept' } });
48
48
  await env.kernel.execute('ApprovalMember.create', { userId: user.id, displayName: specialty, specialty }, env.system());
49
49
  return user;
50
50
  }
@@ -145,7 +145,7 @@ it('经理后开启所选专业人员 all 会签,全部通过才批准并锁
145
145
  expect((await env.query('select status from core.inventory_lock where id=$1', [order.id]))[0].status).toBe('held');
146
146
  });
147
147
 
148
- /** gt 阈值必须保持严格大于;恰好 5 万元时经理的同额 ceiling 足够,不创建财务候选任务。 */
148
+ /** gt 阈值必须保持严格大于;恰好 5 万元时不创建财务候选任务。 */
149
149
  it('恰好 50000 元经理通过后直接批准,不产生财务候选待办', async () => {
150
150
  decision = 'pass'; const order = await submit({ amount: '50000.00' });
151
151
  await approveBy(reviewer, order.id);
@@ -12,7 +12,7 @@ let buyer: TestUser;
12
12
  let manager: TestUser;
13
13
  /**
14
14
  * 夹具通过真实命令建立部门负责人和库存,不绕过领域规则直接造业务行。
15
- * 申请人和经理能力分开,经理额度限定为 50000,确保后续断言真的经过职责分离与额度分支。
15
+ * 申请人和经理能力分开,以验证职责分离、业务批准和库存联动。
16
16
  */
17
17
  beforeAll(async () => {
18
18
  env = await createTestEnv({
@@ -24,19 +24,18 @@ beforeAll(async () => {
24
24
  });
25
25
  manager = await env.user({
26
26
  caps: ['PurchaseOrder.approve'],
27
- params: { 'PurchaseOrder.approve': { ceiling: 50000 } },
28
27
  });
29
28
  await env.kernel.execute('Dept.update', { id: env.rootDeptId, leaderId: manager.id }, env.system());
30
29
  await env.kernel.execute('Inventory.create', { sku: 'A-01', available: 100 }, env.system());
31
30
  });
32
31
  afterAll(() => env?.close());
33
32
 
34
- /** 创建并提交一张 1200 元、两件 A-01 的采购单;只推进领域状态,不在此助手中消费 outbox。 */
35
- async function submitted() {
33
+ /** 创建并提交两件 A-01 的采购单;金额默认 1200 元,不在此助手中消费 outbox。 */
34
+ async function submitted(amount = '1200.00') {
36
35
  const ctx = await buyer.ctx();
37
36
  const order = await env.kernel.execute(
38
37
  'PurchaseOrder.create',
39
- { amount: '1200.00', items: [{ sku: 'A-01', qty: 2 }] },
38
+ { amount, items: [{ sku: 'A-01', qty: 2 }] },
40
39
  ctx,
41
40
  );
42
41
  return env.kernel.execute('PurchaseOrder.submit', { id: order.id }, ctx);
@@ -126,21 +125,15 @@ it.each(['cancelled', 'received'] as const)(
126
125
  },
127
126
  );
128
127
 
129
- /** 低额度反例检查硬约束阻止批准,且同事务反应没有留下半成功的占用记录。 */
130
- it('审批额度不够时保留待审批状态,不创建库存锁', async () => {
131
- const order = await submitted();
132
- const lowLimit = await env.user({
133
- caps: ['PurchaseOrder.approve'],
134
- params: { 'PurchaseOrder.approve': { ceiling: 500 } },
135
- });
128
+ /** 领域命令不按角色金额限制批准;更大金额的额外步骤由采购流程声明负责。 */
129
+ it('有批准能力的经理不需要额度参数即可执行大额采购结论', async () => {
130
+ const order = await submitted('60000.00');
136
131
  const process = env.kernel.processCtx({
137
132
  process: 'po-approval',
138
133
  tenantId: env.tenantId,
139
- onBehalfOf: lowLimit.id,
134
+ onBehalfOf: manager.id,
140
135
  capabilities: ['PurchaseOrder.approve'],
141
136
  });
142
- expect((await env.callAs(process, 'PurchaseOrder.approve', { id: order.id })).code).toBe(
143
- 'CONSTRAINT_VIOLATED',
144
- );
145
- expect(await env.query('select id from core.inventory_lock where id = $1', [order.id])).toHaveLength(0);
137
+ await env.kernel.execute('PurchaseOrder.approve', { id: order.id }, process);
138
+ expect((await env.query('select status from core.inventory_lock where id = $1', [order.id]))[0].status).toBe('held');
146
139
  });
@@ -1,12 +1,11 @@
1
1
  /**
2
2
  * 采购单声明集中列出申请材料、专业复核名单、状态、范围、命令和事件,由 API、worker 和生成器统一消费。
3
3
  * 草稿可编辑专业复核名单,提交重新核对协作名录;handler 仅读取声明依赖,数据库写入、版本与审计由内核完成。
4
- * 审批结论仍以 process 命令执行职责分离、额度和库存检查;选入名录或路由不授予权限。
4
+ * 审批结论仍以 process 命令执行职责分离和库存检查;选入名录或路由不授予权限。
5
5
  */
6
6
  import {
7
7
  aggregate,
8
8
  bool,
9
- ceiling,
10
9
  command,
11
10
  emit,
12
11
  event,
@@ -127,7 +126,7 @@ export const create = command(PurchaseOrder, {
127
126
  /**
128
127
  * 在 draft 中调整本单专业复核需求和名单;需要 PurchaseOrder.setReviewers,不能直接修改待审批快照。
129
128
  * 输入保存前核对 1–5 位不同且非提单人的 active 名录成员;关闭时用 false/[],失败整次回滚。
130
- * 提交还会重新查询名录,以拒绝保存后退役的成员;名录不授予对单据的读取、批准能力或额度。
129
+ * 提交还会重新查询名录,以拒绝保存后退役的成员;名录不授予对单据的读取或批准能力。
131
130
  */
132
131
  export const setReviewers = command(PurchaseOrder, {
133
132
  name: 'setReviewers', description: '设置采购单专业复核需求和人员', from: 'draft', to: null,
@@ -179,7 +178,7 @@ export const submit = command(PurchaseOrder, {
179
178
  });
180
179
 
181
180
  /**
182
- * 审批终结命令必须同时满足创建人与审批人分离、当前角色额度,以及声明的库存只读检查。
181
+ * 审批终结命令必须同时满足创建人与审批人分离,以及声明的库存只读检查。
183
182
  * stock 是 reads 绑定的受范围约束仓储;找不到 SKU 或登记数量不足时返回 STOCK_SHORT 并回滚。
184
183
  * 该检查不扣减 available;真正占用由 OrderApproved 的同事务反应创建 InventoryLock。
185
184
  * 范例展示资源配对,不实现多订单累计扣减、仓库出入库或价格核算。
@@ -188,7 +187,7 @@ export const approve = command(PurchaseOrder, {
188
187
  from: 'pending_approval',
189
188
  to: 'approved',
190
189
  invokedBy: 'process',
191
- constraints: [sod.notSameAs('createdBy'), ceiling('amount')],
190
+ constraints: [sod.notSameAs('createdBy')],
192
191
  reads: { stock: Inventory },
193
192
  emits: [OrderApproved],
194
193
  handler: async (o, _input, { stock }) => {
@@ -18,7 +18,7 @@ export const purchaseLifecycle = businessProcess('purchase-lifecycle', {
18
18
  approval: { title: '采购审批', description: 'AI 先核查材料;通过后部门经理审批,金额超过五万元时增加财务审批。疑难材料由本步骤转人工。', actor: 'AI、部门经理、财务', kind: 'decision', action: poApproval },
19
19
  returned: { title: '退回修改', description: '材料不足退回草稿,保留本轮审核理由。', actor: '审批流程', action: returnForChanges },
20
20
  revise: { title: '补充采购用途', description: '依据建议补充用途,再次提交后从 AI 预审开始。', actor: '申请人', action: explainPurpose },
21
- approved: { title: '批准并锁定库存', description: '审批结论通过权限、额度和库存校验后,同事务锁定所需库存。', actor: '审批流程与系统', action: approve,
21
+ approved: { title: '批准并锁定库存', description: '审批结论通过权限和库存校验后,同事务锁定所需库存。', actor: '审批流程与系统', action: approve,
22
22
  related: [{ object: InventoryLock, label: '同事务锁库存', via: acquireLock }] },
23
23
  received: { title: '确认收货', description: '接收外部收货通知并更新采购单,异步释放库存锁。', actor: '外部系统与系统', kind: 'end', action: markReceived,
24
24
  related: [{ object: InventoryLock, label: '收货后释放占用', via: releaseLock }] },
@@ -11,9 +11,9 @@
11
11
  "build:app": "pnpm --filter @app/domain build && pnpm --filter @app/api build && pnpm --filter @app/web build"
12
12
  },
13
13
  "devDependencies": {
14
- "@yss123/cage-cli": "0.3.2",
15
- "@yss123/cage-testkit": "0.3.2",
16
- "@yss123/cage-lint": "0.3.2",
14
+ "@yss123/cage-cli": "0.4.0",
15
+ "@yss123/cage-testkit": "0.4.0",
16
+ "@yss123/cage-lint": "0.4.0",
17
17
  "@types/node": "^24.3.0",
18
18
  "typescript": "~5.9.3",
19
19
  "vitest": "^4.1.12",
@@ -10,7 +10,7 @@
10
10
  "typecheck": "tsc -p tsconfig.json --noEmit"
11
11
  },
12
12
  "dependencies": {
13
- "@yss123/cage-client": "0.3.2",
13
+ "@yss123/cage-client": "0.4.0",
14
14
  "zod": "^4.1.5"
15
15
  }
16
16
  }
@@ -31,7 +31,7 @@ export const RoleInputs = {
31
31
  };
32
32
 
33
33
  /** Role.archive 可能返回的错误码 */
34
- export type RoleArchiveError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
34
+ export type RoleArchiveError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
35
35
  /** Role.create 可能返回的错误码 */
36
36
  export type RoleCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT';
37
37
  /** Role.grantCapability 可能返回的错误码 */
@@ -39,7 +39,7 @@ export type RoleGrantCapabilityError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOM
39
39
  /** Role.rename 可能返回的错误码 */
40
40
  export type RoleRenameError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
41
41
  /** Role.revokeCapability 可能返回的错误码 */
42
- export type RoleRevokeCapabilityError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
42
+ export type RoleRevokeCapabilityError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
43
43
  /** Role.setScope 可能返回的错误码 */
44
44
  export type RoleSetScopeError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
45
45
 
@@ -38,11 +38,11 @@ export type UserAssignRolesError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_
38
38
  /** User.create 可能返回的错误码 */
39
39
  export type UserCreateError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT';
40
40
  /** User.disable 可能返回的错误码 */
41
- export type UserDisableError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
41
+ export type UserDisableError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
42
42
  /** User.enable 可能返回的错误码 */
43
- export type UserEnableError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
43
+ export type UserEnableError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
44
44
  /** User.setPassword 可能返回的错误码 */
45
- export type UserSetPasswordError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
45
+ export type UserSetPasswordError = 'CONFLICT' | 'CONSTRAINT_VIOLATED' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
46
46
  /** User.update 可能返回的错误码 */
47
47
  export type UserUpdateError = 'CONFLICT' | 'DOMAIN_ERROR' | 'FORBIDDEN' | 'INVALID_INPUT' | 'INVALID_TRANSITION' | 'NOT_FOUND';
48
48
 
@@ -14,11 +14,11 @@
14
14
  "typecheck": "tsc -p tsconfig.json --noEmit"
15
15
  },
16
16
  "dependencies": {
17
- "@yss123/cage-kernel": "0.3.2",
18
- "@yss123/cage-admin": "0.3.2",
19
- "@yss123/cage-approval": "0.3.2",
20
- "@yss123/cage-connectors": "0.3.2",
21
- "@yss123/cage-ai": "0.3.2",
17
+ "@yss123/cage-kernel": "0.4.0",
18
+ "@yss123/cage-admin": "0.4.0",
19
+ "@yss123/cage-approval": "0.4.0",
20
+ "@yss123/cage-connectors": "0.4.0",
21
+ "@yss123/cage-ai": "0.4.0",
22
22
  "zod": "^4.1.5"
23
23
  }
24
24
  }