openxiangda 2.0.0-alpha.99 → 2.0.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 (106) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +23 -20
  3. package/bin/distribution/commands.js +55 -0
  4. package/bin/distribution/launcher.js +49 -0
  5. package/bin/distribution/migrate.js +60 -0
  6. package/bin/distribution/releases.js +52 -0
  7. package/bin/distribution/skills.js +80 -0
  8. package/bin/distribution/update.js +68 -0
  9. package/bin/distribution/workspace.js +85 -0
  10. package/bin/run.js +9 -11
  11. package/dist/browser/AuthoritativeSelector.d.ts +3 -2
  12. package/dist/browser/AuthoritativeSelector.d.ts.map +1 -1
  13. package/dist/browser/AuthoritativeSelector.js +39 -24
  14. package/dist/browser/AuthoritativeSelector.js.map +1 -1
  15. package/dist/browser/components/platform-fields/MobileFieldControls.d.ts.map +1 -1
  16. package/dist/browser/components/platform-fields/MobileFieldControls.js +2 -2
  17. package/dist/browser/components/platform-fields/MobileFieldControls.js.map +1 -1
  18. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts +4 -2
  19. package/dist/browser/components/platform-fields/ResourceReferenceField.d.ts.map +1 -1
  20. package/dist/browser/components/platform-fields/ResourceReferenceField.js +2 -2
  21. package/dist/browser/components/platform-fields/ResourceReferenceField.js.map +1 -1
  22. package/dist/browser/components/platform-fields/rich-text-value.d.ts.map +1 -1
  23. package/dist/browser/components/platform-fields/rich-text-value.js +11 -1
  24. package/dist/browser/components/platform-fields/rich-text-value.js.map +1 -1
  25. package/dist/browser/components/resource/RecordDetailFrame.js +1 -1
  26. package/dist/browser/components/resource/RecordDetailFrame.js.map +1 -1
  27. package/dist/browser/components/resource/SurfaceFields.d.ts +3 -2
  28. package/dist/browser/components/resource/SurfaceFields.d.ts.map +1 -1
  29. package/dist/browser/components/resource/SurfaceFields.js +14 -7
  30. package/dist/browser/components/resource/SurfaceFields.js.map +1 -1
  31. package/dist/browser/components/resource/resource-import.d.ts +16 -1
  32. package/dist/browser/components/resource/resource-import.d.ts.map +1 -1
  33. package/dist/browser/components/resource/resource-import.js +58 -34
  34. package/dist/browser/components/resource/resource-import.js.map +1 -1
  35. package/dist/browser/components/resource/useResourceFormDrafts.d.ts.map +1 -1
  36. package/dist/browser/components/todo/ApplicationTodoCenterPage.d.ts.map +1 -1
  37. package/dist/browser/components/todo/ApplicationTodoCenterPage.js +1 -2
  38. package/dist/browser/components/todo/ApplicationTodoCenterPage.js.map +1 -1
  39. package/dist/browser/components/workflow/StandardWorkflowPages.d.ts.map +1 -1
  40. package/dist/browser/components/workflow/StandardWorkflowPages.js +54 -52
  41. package/dist/browser/components/workflow/StandardWorkflowPages.js.map +1 -1
  42. package/dist/browser/platform-client.d.ts +3 -2
  43. package/dist/browser/platform-client.d.ts.map +1 -1
  44. package/dist/browser/platform-client.js +69 -18
  45. package/dist/browser/platform-client.js.map +1 -1
  46. package/dist/browser/record-detail.css +3 -2
  47. package/dist/browser/runtime.d.ts.map +1 -1
  48. package/dist/browser/runtime.js +26 -2
  49. package/dist/browser/runtime.js.map +1 -1
  50. package/dist/browser/workflow-launch.d.ts +4 -1
  51. package/dist/browser/workflow-launch.d.ts.map +1 -1
  52. package/dist/browser/workflow-launch.js +32 -0
  53. package/dist/browser/workflow-launch.js.map +1 -1
  54. package/dist/core.d.ts +1 -1
  55. package/dist/core.d.ts.map +1 -1
  56. package/dist/core.js.map +1 -1
  57. package/documentation/AGENTS.md +26 -0
  58. package/documentation/administration.md +27 -0
  59. package/documentation/application-foundation.md +162 -0
  60. package/documentation/appspec.md +152 -0
  61. package/documentation/backend.md +132 -0
  62. package/documentation/concepts.md +61 -0
  63. package/documentation/data-authz.md +62 -0
  64. package/documentation/delivery.md +110 -0
  65. package/documentation/development.md +32 -0
  66. package/documentation/field-components.md +236 -0
  67. package/documentation/frontend.md +269 -0
  68. package/documentation/getting-started.md +66 -0
  69. package/documentation/interaction-patterns.md +56 -0
  70. package/documentation/manifest.json +120 -0
  71. package/documentation/product-design.md +142 -0
  72. package/documentation/public-access.md +167 -0
  73. package/documentation/reference/cli.md +27 -0
  74. package/documentation/reference/mcp.md +649 -0
  75. package/documentation/testing.md +63 -0
  76. package/documentation/upgrading.md +39 -0
  77. package/documentation/workflow-events.md +181 -0
  78. package/launcher-skill/openxiangda/SKILL.md +24 -0
  79. package/package.json +72 -9
  80. package/releases/2.0.0.json +50 -0
  81. package/skills/manifest.json +2 -2
  82. package/skills/openxiangda-v2/SKILL.md +64 -51
  83. package/skills/openxiangda-v2/agents/openai.yaml +2 -2
  84. package/skills/openxiangda-v2/references/administration.md +27 -0
  85. package/skills/openxiangda-v2/references/application-foundation.md +162 -0
  86. package/skills/openxiangda-v2/references/appspec.md +132 -47
  87. package/skills/openxiangda-v2/references/backend.md +101 -248
  88. package/skills/openxiangda-v2/references/cli.md +27 -0
  89. package/skills/openxiangda-v2/references/concepts.md +61 -0
  90. package/skills/openxiangda-v2/references/data-authz.md +36 -388
  91. package/skills/openxiangda-v2/references/delivery.md +110 -49
  92. package/skills/openxiangda-v2/references/development.md +32 -0
  93. package/skills/openxiangda-v2/references/field-components.md +236 -0
  94. package/skills/openxiangda-v2/references/frontend.md +254 -280
  95. package/skills/openxiangda-v2/references/getting-started.md +66 -0
  96. package/skills/openxiangda-v2/references/interaction-patterns.md +56 -0
  97. package/skills/openxiangda-v2/references/mcp.md +649 -0
  98. package/skills/openxiangda-v2/references/product-design.md +142 -0
  99. package/skills/openxiangda-v2/references/public-access.md +92 -84
  100. package/skills/openxiangda-v2/references/testing.md +45 -56
  101. package/skills/openxiangda-v2/references/upgrading.md +39 -0
  102. package/skills/openxiangda-v2/references/workflow-events.md +143 -285
  103. package/skills/openxiangda-v2/references/architecture.md +0 -9
  104. package/skills/openxiangda-v2/references/commands.md +0 -21
  105. package/skills/openxiangda-v2/references/discovery.md +0 -15
  106. package/skills/openxiangda-v2/references/workspace.md +0 -62
@@ -0,0 +1,61 @@
1
+ # 核心架构
2
+
3
+ ```mermaid
4
+ flowchart LR
5
+ Repo["应用 Git 仓库"] --> CI["项目 CLI / MCP"]
6
+ CI --> Package["不可变 AppPackage"]
7
+ Package --> Control["平台控制面"]
8
+ Control --> Deploy["DeploymentRun"]
9
+ Deploy --> Web["前端静态包"]
10
+ Deploy --> Backend["按需启用的 NestJS 容器"]
11
+ Deploy --> Config["Data/AuthZ/Workflow/Event 配置版本"]
12
+ Backend --> Data["统一 Data API"]
13
+ Backend --> Kernel["Workflow Kernel v2"]
14
+ Backend --> Events["事件投递服务"]
15
+ ```
16
+
17
+ ## 工程边界
18
+
19
+ - Git 仓库是应用源码与声明的事实来源。
20
+ - AppPackage 是交付边界,包含前端摘要、后端镜像摘要、配置包摘要和契约版本。
21
+ - 平台是运行状态的事实来源,持久保存应用版本、部署运行、检查点与环境激活状态。
22
+ - AI、CLI、MCP 都是控制面客户端,不负责持有发布状态。
23
+
24
+ ## 运行档位
25
+
26
+ 纯 CRUD 和标准审批无需应用后端。需要后端时使用平台可控的运行方式:每应用独立容器,共享 Kubernetes 集群、节点池、网关和可观测基础设施。后续可以用资源配额形成共享档与独享档,但不建设多应用共用 Node 进程。
27
+
28
+ ## 数据边界
29
+
30
+ 首期不为应用创建独立数据库。业务后端通过统一 Data API 访问平台数据;Data API 提供资源化查询、字段策略、行级授权、并发修订和受限事务批处理。这样保留统一治理,又不限制应用后端表达业务逻辑。
31
+
32
+ 应用后端声明具名 Operation 时,不应重新手写用户、部门、资源引用和文件字段协议。`openxiangda/config` 提供 `resourceRecordSchema`、`schemaRef`、`composeJsonSchema` 和 `composeAppOperationSchemas`:它们从同一 Resource declaration 和公共 `FIELD_VALUE_SCHEMAS` 投影请求/响应 JSON Schema,只允许有界的本地 `$defs`/`$ref`,不会改变 Data API、权限或业务事务 owner。
33
+
34
+ ```ts
35
+ import {
36
+ composeAppOperationSchemas,
37
+ resourceRecordSchema,
38
+ schemaRef,
39
+ } from 'openxiangda/config';
40
+
41
+ const schemas = composeAppOperationSchemas({
42
+ request: {
43
+ type: 'object',
44
+ additionalProperties: false,
45
+ required: ['record'],
46
+ properties: { record: schemaRef('InstrumentRecord') },
47
+ },
48
+ response: schemaRef('InstrumentRecord'),
49
+ definitions: {
50
+ InstrumentRecord: resourceRecordSchema(instruments, {
51
+ fields: ['name', 'owner'],
52
+ }),
53
+ },
54
+ });
55
+ ```
56
+
57
+ ## 版本列车
58
+
59
+ OpenXiangda 2.0 使用兼容版本列车,而不是要求所有 npm 包共享同一个版本号。各包按职责独立递增;应用只直接安装精确版本的 `openxiangda` 根包并提交锁文件,内部物理依赖由根包确定,不能使用范围或 `latest`。AppPackage 记录 AppPackage、configuration bundle、contract bundle 和 compiler contract 的原子兼容四元组;平台通过唯一的 `capabilities.configurationCompatibility` 契约声明完整可接受组合、验证端点与 validator 能力版本。CLI 必须把真实生成的 configuration/contract bundle 交给目标平台只读预检,并在应用生产构建、后端镜像构建和制品上传前拒绝不兼容组合;平台部署准备阶段继续权威复验,不允许删字段或向下协商。
60
+
61
+ AppPackage 的 `compatibility.requiredPlatformCapabilities` 也是 compiler 输出:每项固定包含 `code`、`contractVersion` 和只覆盖该能力相关规范化声明的 `usageDigest`。平台 `features[code]` 必须处于 `available` 且 `contractVersion` 精确相等;`preview`、缺失或版本不同都不能部署。应用配置没有 `platform.requiredCapabilities`,构建 API 也没有追加入口;不得手改 AppPackage 或用字符串能力名绕过 compiler。摘要不包含运行期业务数据或 secret 值,平台部署准备仍须根据原始 config/contract bytes 权威重算。CLI、MCP、Skills 和文档随相关包变更发布,不因无关包升级而强制全量重发。
@@ -0,0 +1,62 @@
1
+ # Data API 与权限
2
+
3
+ 授权由四层组成:页面/操作 capability、行谓词、字段 read,以及字段 create/update。前端只消费平台返回的最终访问结果来隐藏按钮、只读输入和剔除 payload;平台每次请求重新执行权威校验。
4
+
5
+ 以下仅以仪器管理应用举例,不是平台默认模型或角色。该示例的行规则是:学校管理员不受行谓词限制;学院管理员按记录 `collegeId` 匹配;仪器管理员按 `instrumentAdminIds` 包含当前用户匹配。不要增加影子范围字段。
6
+
7
+ `collegeId` 是应用 `colleges` Native Resource 的记录 UUID。学院 scope dimension 通过
8
+ `valueSource.kind=native_resource` 绑定同一资源,选择器只展示平台按当前 membership 与
9
+ create/update 闭包返回的值。人员和部门保存平台目录真实 ID;部门不等于学院,也不能作为
10
+ 学院范围的隐式来源。
11
+
12
+ 字段策略支持 `read`、`create`、`update` 和 `mask`。显式空数组拒绝,能力数组采用 all-of。无权更新字段不仅 disabled,还必须从更新 payload 删除。
13
+
14
+ ```bash
15
+ pnpm openxiangda check
16
+ ```
17
+
18
+ 角色成员、维度授权和平台管理员由平台管理面维护,不属于应用开发 CLI。
19
+
20
+ 自定义 PC/移动页面需要维护当前应用角色时,使用
21
+ `openxiangda/core` 的 `loadRoleManagementCatalog`、
22
+ `listRoleMemberships`、`searchRoleManagementUsers`、成员 mutation 与
23
+ role-management-grant mutation。应用/平台超级管理员可以把全部角色或明确的
24
+ 目标角色集合委托给一个业务角色;普通业务管理者只有同时具备
25
+ `management.delegate` 时,才能把自己已有的目标角色和动作子集继续委托。
26
+ 平台按当前用户角色并集重算,接口不接受 actor、tenant、active role 或
27
+ impersonation token。mutation 必须携带 UUID `operationId`、`reason`,更新/撤销还必须
28
+ 携带最新 `expectedRevision`;409 后重新加载,不能猜 revision。使用前读取当前角色管理目录,详见[管理入口](./administration.md)。
29
+
30
+ 匿名外部访问不属于 RBAC 角色或 current-user 行策略。公开表单、续填、附件、重复校验和同一
31
+ 浏览器的本人记录访问只通过[`frontend.publicAccess` 专用合同](./public-access.md)开放;平台继续在
32
+ 专用端点和 PostgreSQL/RLS 中强制匿名主体、字段、策略及提交回执边界。
33
+
34
+ 数值边界直接声明在字段上,`min`/`max` 为闭区间,并且只允许用于
35
+ `number.integer` 和 `number.decimal`。跨字段约束声明在资源的
36
+ `invariants` 中;每条约束只能比较同一记录的两个已声明字段,最多 20 条,
37
+ 由平台在 create/update/increment 的最终候选记录上统一执行。
38
+
39
+ ```ts
40
+ {
41
+ code: 'sessions',
42
+ name: '场次',
43
+ fields: [
44
+ { code: 'startAt', type: 'datetime', label: '开始', required: true },
45
+ { code: 'endAt', type: 'datetime', label: '结束', required: true },
46
+ { code: 'capacity', type: 'number.integer', label: '容量', min: 0 },
47
+ { code: 'occupied', type: 'number.integer', label: '已占用', min: 0 },
48
+ ],
49
+ invariants: [
50
+ { code: 'time-order', expression: { leftField: 'startAt', operator: 'lt', rightField: 'endAt' } },
51
+ { code: 'capacity-not-exceeded', expression: { leftField: 'capacity', operator: 'gte', rightField: 'occupied' } },
52
+ ],
53
+ }
54
+ ```
55
+
56
+ `date-range` 与 `datetime-range` 必须显式声明 `rangeBoundary`,取值为 closed 或 half-open。选择半开区间 `[start, end)` 时相邻时间段不冲突;闭区间端点相接可能重叠。
57
+
58
+ 业务 uuid 字段与系统 id 不同:可选业务 UUID 省略时为空,必填字段需调用方提供合法值,平台不会替业务 UUID 自动生成默认值。
59
+
60
+ 业务分派需要目标人员具有指定角色时,使用[事务中的角色条件](./backend.md#role-member),
61
+ 由平台在写入事务中核对当前有效成员。候选查询、页面隐藏、应用管理员身份和历史
62
+ 角色列表都不能替代这一规则,也不应在应用中复制一份权限状态。
@@ -0,0 +1,110 @@
1
+ # 部署、生产晋级与恢复
2
+
3
+ 应用开发者从工作区执行 `pnpm openxiangda`。平台负责应用版本、运行状态和恢复决定。工具链自身的 npm 发布由平台维护者负责,应用项目无需复制发包脚本或平台验证矩阵。
4
+
5
+ ## 测试部署
6
+
7
+ 发布前,先将本轮源码、生成契约及必要记录合入并推送仓库的远端默认主分支,然后从干净且同步的主分支工作区发布。工具从 origin 的远端 HEAD 识别主分支,不把任务分支的 upstream 当作主线。未提交、未推送、未合并或落后主线的问题会在构建和上传前返回;工具不会自动合并分支或覆盖其他会话的改动。
8
+
9
+ 开发开始时先同步主线并读取项目现状,开发完成包括提交、推送与主线整合。每个工作区保持一个写者;需要并行时使用独立目录并明确各任务范围。日常 dev/check 仍可验证未提交源码。没有 Git 远端的项目应先建立并绑定仓库再发布。
10
+
11
+ 准备部署时直接执行 deploy,它已经包含兼容性预检、生成、检查、测试和构建。只想检查代码时使用 [check](./testing.md),无需在 deploy 前重复运行全套检查。
12
+
13
+ 测试发布还会核对 [AppSpec](./appspec.md) 的需求依据、架构、权限、性能预算和验收计划。缺失时给出具体记录位置,先补实际设计;首次测试部署不要求预先完成线上业务验收。
14
+
15
+ ```bash
16
+ pnpm openxiangda deploy --dry-run --json
17
+ pnpm openxiangda deploy
18
+ pnpm openxiangda status --json
19
+ pnpm openxiangda logs <deployment-id> --json
20
+ ```
21
+
22
+ 默认目标为 `test`,平台内部标识为 `preproduction`。只读预览不生成构建产物、不上传制品、不提交 DeploymentRun;因此预览成功不能证明代码已通过检查。正式检查使用本地完整校验,再通过目标平台的 `configurationCompatibility` 核对同源规则、密钥、已有物理模型和登录提供方等只读条件;失败时停止后续步骤。只有启用了自定义 Nest 后端的应用才需要构建后端镜像及对应 Docker 环境。
23
+
24
+ 生成的不可变 AppVersion 绑定前端、可选后端、配置契约和制品摘要。默认提交后持续跟踪同一运行,直到平台成功、失败或取消,最多观察 15 分钟。`--no-wait` 只提交,适用于已有状态跟踪器的自动化;此时返回运行 ID 不代表部署完成。
25
+
26
+ 构建、上传及平台执行都会显示当前阶段和耗时,长步骤每 10 秒反馈一次。平台的准备、部署、切换和健康检查状态来自原运行。观察超时或连接中断不会取消部署或重建候选,使用下面的命令继续跟踪:
27
+
28
+ ```bash
29
+ pnpm openxiangda status <deployment-id> --watch
30
+ ```
31
+
32
+ 网络响应不确定时先查询原运行,不凭本地输出创建重复部署。平台部署成功后,仍需执行真实角色的业务验收。
33
+
34
+ 相同源码候选重试时,工具会重新核对本地验证证据、封存清单和制品字节,复用仍有效的构建结果及后端镜像;已上传内容按摘要查询并复用。只有环境条件改变时不需要重建源码制品。输出损坏、输入变化或缓存缺失时自动回到正式检查和构建。缓存位于 `.openxiangda/build/`,不是新的部署状态源;提交响应不确定时使用原候选和幂等键,已有失败运行按其 recovery 恢复。
35
+
36
+ ## 测试环境验收
37
+
38
+ 至少记录应用版本、目标环境、真实角色、复现数据、预期和实际结果。按改动范围检查页面、权限、业务规则和失败路径;详见[校验与验收](./testing.md)。
39
+
40
+ | 证据 | 能说明什么 |
41
+ | --- | --- |
42
+ | 本地 check 成功 | 本次声明兼容,检查、测试和构建通过 |
43
+ | 包密封完成 | 存在可识别的不可变候选版本 |
44
+ | DeploymentRun 成功 | 平台完成该版本的部署流程 |
45
+ | 真实角色的页面与业务操作通过 | 对应场景在目标环境可用 |
46
+ | 生产晋级成功并回读 | 生产使用指定测试版本;仍需核对实际入口与关键业务 |
47
+
48
+ 构建成功、提交成功和真实业务验收是不同证据,报告时分别给出实际状态。
49
+
50
+ ## 生产晋级
51
+
52
+ 生产必须复用已成功部署到测试环境的同一版本,不能从当前源码直接重建:
53
+
54
+ 先按真实操作保存 `appspec/verification/<测试运行ID>.json` 并提交、推送到主线。晋级会从测试源码提交读取原验收计划,核对报告的运行 ID、包摘要、AC 场景与性能证据;主线后来的需求不改变已测范围。
55
+
56
+ 该版本的源码提交必须仍包含在权威远端主分支中。主分支后来有新提交,不会改变本次晋级的制品。若任务分支采用 squash/rebase 合并,应在最终主线提交上重新冻结并验证测试候选,不能继续晋级合并前的提交。
57
+
58
+ ```bash
59
+ pnpm openxiangda deploy --environment production --from <test-deployment-id> --dry-run --json
60
+ pnpm openxiangda deploy --environment production --from <test-deployment-id>
61
+ pnpm openxiangda status --json
62
+ ```
63
+
64
+ 预览会精确读取指定测试运行的封存配置并核对生产密钥、模型和登录条件,返回源运行、版本与摘要。主线后来变化或版本较旧,不会使预检改用当前源码或最近版本列表。测试运行失败、版本缺失或条件不符时直接失败。平台在真正晋级时再次权威校验。生产参数不接受测试环境的 `environmentId` 或 `idempotencyKey`。已有生产发布授权时可继续执行;授权不明确时先准备版本、预览及验收证据,再确认具体发布对象。
65
+
66
+ ## 失败、重试与回滚
67
+
68
+ `logs` 返回首个失败 `rootFailure`、最近失败 `latestFailure`、候选状态、尝试账本与 `recovery`。无失败时对应字段为 null。按平台给出的 `recovery.nextCommand` 处理;只在 `recovery.cancelAllowed` 为真时取消。已激活的运行不能用 cancel 撤销。
69
+
70
+ ```bash
71
+ pnpm openxiangda retry <deployment-id>
72
+ pnpm openxiangda cancel <deployment-id>
73
+ pnpm openxiangda rollback --to <app-version-id>
74
+ ```
75
+
76
+ 环境版本回滚不保证撤销数据库业务写入;数据修复需要单独计划与验证。暂停与恢复默认作用于测试环境;生产必须显式选择:
77
+
78
+ ```bash
79
+ pnpm openxiangda stop
80
+ pnpm openxiangda start
81
+ pnpm openxiangda stop --environment production
82
+ ```
83
+
84
+ stop 保留数据与配置,start 从当前不可变版本恢复。不要把暂停、取消、回滚当作同一种操作。
85
+
86
+ ## 自动化与错误定位
87
+
88
+ CLI 的 `--json` 输出单个 `openxiangda.cli-result/v2` 对象;失败包含 code、message、retryable、remediation、nextCommand,以及适用的 pointer/details。自动化依据 code 和结构化字段决策,不解析中文描述。
89
+
90
+ `check`、`deploy` 和持续状态观察的结果带 `data.execution`,包含本次操作 ID、总耗时及各阶段状态和耗时。阶段进度写到 stderr,保持 `--json` 的 stdout 可解析;`--json-events` 则通过 `command.status` 返回同源结构化进度。MCP 客户端提供 `progressToken` 时收到标准进度通知;不订阅通知仍能从最终结果读取阶段摘要。MCP `deploy_app.wait` 默认 true,`deployment_status.watch` 可继续观察原运行。
91
+
92
+ 兼容性错误会列出当前工具链与目标平台的版本、能力和契约要求。按定位修复声明或升级目标平台,不删除真实业务要求、改写摘要或绕过权限来让预检通过。应用所需能力由规范化声明派生,应用不能手写一份能力列表冒充平台支持。
93
+
94
+ `OPENXIANGDA_CONFIGURATION_VALIDATOR_MISMATCH` 表示工具链与平台的校验实现不配套,应按发布说明升级对应版本;它会在构建、镜像推送和制品上传前出现。模型类型不能原地替换时,按提示设计新字段及数据转换;必需密钥缺失时配置目标环境后继续,不修改源码伪装问题已解决。
95
+
96
+ MCP 的 check_app、deployment_plan、deploy_app 使用与 CLI 相同的环境与生产晋级参数规则。完整参数以[CLI](./reference/cli.md)与[MCP](./reference/mcp.md)为准。
97
+
98
+ ## 构建前运行配额
99
+
100
+ `deploy --dry-run`(MCP `deployment_plan`)会只读查询目标 TEST 的运行配额,输出 `runtimeCapacity` 的核验时间、所需增量、各配额剩余量和缺口。`sufficient: false` 表示当前不足;`null` 表示无需新增或未核验,必须结合 `basis` 与 `capacity.checked` 阅读。专用命名空间未检查不能当成资源充足。
101
+
102
+ 正式 deploy 在检查脚本和镜像构建前预检;平台缺少配套能力或无法核验时明确停止。配额快照不预留资源,实际执行再次检查。已有可验证密封候选会携带摘要和幂等键,平台识别 `existing-run` 时返回原运行,不把它当作新副本;观察或恢复原运行使用 status/retry。不要为绕过配额创建新包或切换目标环境。
103
+
104
+ ## TEST 单副本维护替换
105
+
106
+ 平台配额只允许一个后端副本时,可显式选择维护替换。它会停止当前 TEST 后端,期间应用不可用;成功后激活新版本,失败时由原 DeploymentRun 恢复旧后端。恢复尚未完成时继续占用原运行,status/logs 显示恢复阶段与首个失败,不允许用新部署或取消跳过恢复。
107
+
108
+ 先运行 `openxiangda deploy --environment test --strategy maintenance-replace --dry-run --json` 查看前驱版本、Head revision 和停止后的容量估算,再使用相同参数去掉 `--dry-run` 提交。计划不预留资源。只有已存在、身份匹配的单副本 TEST 后端才可使用;前端应用、新应用和 production 不支持。默认仍为 rolling,不会因配额不足自动停止实例。
109
+
110
+ 策略属于部署幂等请求。默认维护幂等键含策略,显式幂等键不能在不同策略之间复用。已有运行通过 status/retry 恢复;持续恢复中的运行保持 preparing/maintenance-recovery-required,平台会重试恢复,恢复失败时保留原运行与错误。
@@ -0,0 +1,32 @@
1
+ # 需求与开发流程
2
+
3
+ 先确认用户要完成的任务,再选择数据模型、页面和权限。使用项目锁定的 `pnpm openxiangda`,读取 `context --json` 与相关专题。现有项目的代码和实时契约优先于其他项目的样例。
4
+
5
+ ## 新应用先完成设计基线
6
+
7
+ 从模糊想法开始时,按[对话发现与产品设计](./product-design.md)分析已有资料、提出可解释的模块建议,用少量自然语言问题持续沟通确认。完整首发范围的 PRD、旅程、页面交互、原型、权限和架构设计齐备后,再制定实施计划和编写业务实现。用户已给出完整材料时先核对矛盾和遗漏,不重复访谈;已有确认持续有效。
8
+
9
+ ## 根据任务决定工作量 {#risk}
10
+
11
+ | 变化 | 需要明确的内容 | 验证 |
12
+ | --- | --- | --- |
13
+ | 格式、无行为重构 | 保留现有行为,无需创建需求记录 | 受影响静态检查和现有测试 |
14
+ | 文案、字段展示、局部规则 | 业务含义、影响页面和预期结果 | 对应数据和交互 |
15
+ | 跨模型、权限、状态变化 | 用户角色、正反场景、数据和恢复边界 | 真实角色、API 和浏览器 |
16
+ | 身份、迁移、并发、外部副作用 | 所有者、失败与幂等、资源边界、回滚和架构决定 | 所涉及契约的专项验证 |
17
+
18
+ 技术命名、可逆布局等在已有要求内决定。新的业务含义、权限扩大或尚未授权的外部操作需要用户决定;已经明确授权的范围不重复询问。用户只要求分析时,不自动创建应用或发布。
19
+
20
+ ## 选择平台能力 {#capabilities}
21
+
22
+ - 普通数据管理:通过 `defineDataModel`、`defineApplicationModule` 和显式 CRUD 视图声明;模型不自动生成菜单或写权限。
23
+ - PC/移动页面:先复用平台组件和标准页面,再使用受支持的页面、插槽与导航扩展。详见[前端](./frontend.md)。
24
+ - 无平台账号的外部表单:使用[匿名公开访问](./public-access.md),不用普通 RBAC 角色冒充匿名主体。
25
+ - 标准审批、待办与通知:按需声明平台能力,见[工作流](./workflow-events.md)。
26
+ - 真实事务或外部集成:使用[按需后端](./backend.md),不为每张表重写 CRUD 控制器。
27
+
28
+ ## 实施与交接 {#iteration}
29
+
30
+ 开发使用 `pnpm openxiangda dev`,过程中运行必要的聚焦测试。交接前按[检查与验收](./testing.md)验证;授权发布后按[交付](./delivery.md)部署。失败保留错误码、位置和原始候选,依据平台恢复指令继续。
31
+
32
+ [AppSpec](./appspec.md)保存业务意图、设计与交付记录,不复制字段 Schema、生成契约和部署状态。新应用维护具体设计和评审,复杂度随业务展开;既有小变更沿用有效设计,仅修订受影响记录,无行为变化可引用已有记录。每轮先读取当前规则、澄清业务、评估架构、权限和性能,随后实施与验证,发布后更新当前规格及交接。测试部署前需要设计和验收计划,生产晋级前需要绑定测试运行及包摘要的实际验收报告。
@@ -0,0 +1,236 @@
1
+ # OpenXiangda 2.0 字段组件协议
2
+
3
+ OpenXiangda 2.0 只声明业务语义字段。字段的 TypeScript 值、JSON Schema、
4
+ PostgreSQL 物理列、索引、查询运算符、权限路径和桌面/移动组件都由编译器从同一份声明生成,
5
+ 应用不能另外声明存储类型或第二套字段元数据。
6
+
7
+ 完整的架构约束、验收矩阵和进度证据见
8
+ [数据与权限](./data-authz.md)。
9
+
10
+ ## 字段与存储
11
+
12
+ | 语义类型 | 标准组件 | Data API 存储/读取值 | PostgreSQL |
13
+ | --- | --- | --- | --- |
14
+ | `text.short` | 单行、邮箱、手机号 | `string` | `varchar(length)` |
15
+ | `text.long` | 多行文本 | `string` | `text` |
16
+ | `text.rich` | 富文本 | 清洗后的 HTML `string` | `text` |
17
+ | `number.integer` | 整数 | `number` | `bigint` |
18
+ | `number.decimal` | 小数、金额、百分比 | `number` | `numeric(p,s)` |
19
+ | `boolean` | 是/否选择 | `boolean` | `boolean` |
20
+ | `date` | 日期 | `YYYY-MM-DD` | `date` |
21
+ | `time` | 时间,可声明分钟或秒精度 | `HH:mm:ss` | `time(0)` |
22
+ | `datetime` | 日期时间 | RFC3339 instant | `timestamptz` |
23
+ | `date-range` | 日期范围 | `{start,end}` | `daterange` |
24
+ | `datetime-range` | 日期时间范围 | `{start,end}` | `tstzrange` |
25
+ | `option.single` | 静态单选下拉、单选按钮 | `{label,value,...}` | `jsonb` |
26
+ | `option.multiple` | 静态多选下拉、复选框 | `{label,value,...}[]` | `jsonb` |
27
+ | `cascade.single` | 单路径级联 | `{label,value,...}[]` | `jsonb` |
28
+ | `cascade.multiple` | 多路径级联 | `{label,value,...}[][]` | `jsonb` |
29
+ | `user.single` | 成员单选 | 完整成员快照或 `null` | `jsonb` |
30
+ | `user.multiple` | 成员多选 | 完整成员快照数组 | `jsonb` |
31
+ | `department.single` | 部门单选 | 完整部门/路径快照或 `null` | `jsonb` |
32
+ | `department.multiple` | 部门多选 | 完整部门/路径快照数组 | `jsonb` |
33
+ | `resource-ref.single` | 动态下拉、单选按钮、资源选择 | 资源 `{label,value,resourceCode,snapshot}` | `jsonb` |
34
+ | `resource-ref.multiple` | 动态多选、复选框、资源选择 | 资源快照数组 | `jsonb` |
35
+ | `file` | 附件 | `DataFileRef[]` | `jsonb` |
36
+ | `image` | 图片 | 带尺寸、缩略图和预览地址的 `DataImageRef[]` | `jsonb` |
37
+ | `signature` | 手写业务签名 | 托管 PNG、签署人、时间、轨迹和哈希 | `jsonb` |
38
+ | `address` | 行政区划地址 | 行政区划标签路径、详细地址和完整地址 | `jsonb` |
39
+ | `location` | 精确定位 | 钉钉/浏览器 WGS84 经纬度和只读服务快照 | `jsonb` |
40
+ | `json` | JSON 编辑器 | 有界 JSON | `jsonb` |
41
+ | `serial-number` | 流水号只读框 | 平台生成 `string` | `varchar(255)` |
42
+ | `uuid` | UUID 业务字段 | 校验 UUID 格式,可按权限修改 | `uuid` |
43
+ | `subtable` | 子表 | 普通子资源行集合 | 独立子表 |
44
+
45
+ 单值空值统一使用 `null`;多值、附件和图片统一使用 `[]`。`subtable` 不在父表保存
46
+ JSON,而是通过标准 Data API 事务维护普通子资源。
47
+
48
+ 布尔字段没有默认值时显示“未选择”,不会把未填写当成“否”。PC 和移动端可以直接选择“否”并提交 `false`;`false` 是有效值,不是必填校验中的空值。只有明确声明默认值时才初始化对应布尔值。
49
+
50
+ ## 图片、缩略图和附件读取
51
+
52
+ 文件本体保存于平台对象存储,业务字段保存 `DataFileRef[]` 或 `DataImageRef[]`,不把 Base64 图片、文件字节、浏览器 `blob:` 地址写入业务字段。`previewUrl` 和 `thumbnailUrl` 是平台按当前应用、环境和文件权限生成的读取地址,不是永久公开 OSS 地址;不要自行替换域名、删除环境参数或拼接对象存储路径。
53
+
54
+ 图片上传完成后,平台保留原文件,并生成最长边 480 像素、保持比例、不放大小图的 WebP 缩略图,编码质量参数为 82。列表、活动卡片、小封面和头像优先用缩略图;大图预览和下载按需读取原文件。当前原生文件端点只支持原文件和 `variant=thumbnail`,不能自行拼接 `width`、`quality` 等未声明参数。附件字段中的图片不保证具有缩略图,需要缩略图能力时声明 `image` 字段。
55
+
56
+ 标准图片/附件展示可以直接使用 Field Kit:
57
+
58
+ ```tsx
59
+ import { AttachmentFileList } from 'openxiangda/field-kit';
60
+
61
+ <AttachmentFileList
62
+ files={record.cover ?? []}
63
+ resourceCode="activities"
64
+ imageTiles
65
+ mobile={isMobile}
66
+ />
67
+ ```
68
+
69
+ 组件按文件引用选择缩略图,预览和下载经过平台接口。自定义活动封面同样先选择 `cover.thumbnailUrl`;只有平台引用没有缩略图时才回退 `cover.previewUrl`。不要写 `previewUrl || thumbnailUrl`,否则小卡片也会下载完整原图。保留真实宽高或稳定的封面比例,非首屏图片使用懒加载;不得在列表渲染时预取所有原图。
70
+
71
+ 受限文件的缓存必须保留权限核验。配套平台支持私有条件缓存时,浏览器可以保存文件响应,再次访问由平台先核对当前权限,内容未变化返回 304,从本地复用字节;`no-cache` 表示复用前校验,和 `no-store` 禁止保存不同。缺少可靠实体标识或请求失败时仍禁止缓存。不应由应用添加长期免校验缓存、公开 CDN 缓存或跨账号 Blob 缓存来绕过该规则。公开长期缓存需要独立、明确的公开发布契约,不能仅因字段名叫封面就视为公开。
72
+
73
+ 验收时同时记录原图/缩略图字节、列表实际请求的变体、重复访问传输量和权限撤销结果。仅看到 `blob:` 地址不能判断用了 Base64,HTTP 200 也不能证明命中了缓存。
74
+
75
+ ## 声明示例
76
+
77
+ ```ts
78
+ {
79
+ code: 'status',
80
+ type: 'option.single',
81
+ label: '状态',
82
+ widget: 'radio',
83
+ required: true,
84
+ indexed: true,
85
+ filter: true,
86
+ options: [
87
+ { label: '草稿', value: 'draft', color: 'default' },
88
+ { label: '已提交', value: 'submitted', color: 'blue' },
89
+ ],
90
+ }
91
+ ```
92
+
93
+ 前端提交并读取完整快照,例如 `{label:'草稿',value:'draft'}`。后端信任显示快照,
94
+ 只做有界结构校验;查询和权限以 `value` 为稳定比较键。选项后来改名不会改变历史记录的显示值。
95
+
96
+ 动态选项使用同应用资源引用:
97
+
98
+ `source.labelField` 必须指向目标资源的 `text.short` 或 `text.long` 字段。流水号字段可以放进
99
+ `searchFields`、`descriptionFields` 或 `snapshotFields`,但不能作为显示标签。
100
+
101
+ ```ts
102
+ {
103
+ code: 'customer',
104
+ type: 'resource-ref.single',
105
+ label: '客户',
106
+ widget: 'select',
107
+ indexed: true,
108
+ filter: true,
109
+ source: {
110
+ kind: 'resource',
111
+ resourceCode: 'customers',
112
+ labelField: 'name',
113
+ searchFields: ['name', 'code'],
114
+ descriptionFields: ['code'],
115
+ snapshotFields: ['code', 'level'],
116
+ pageSize: 20,
117
+ loadMode: 'search',
118
+ },
119
+ }
120
+ ```
121
+
122
+ 来源端点接受当前表单绑定值、关键字和游标;页大小由 `source.pageSize` 固定。
123
+ 标准流程的具名动作发起表单由 `WorkflowSubmissionPage` 自动传递 `launch: { workflowCode, operationCode }`。
124
+ 平台核对当前环境已部署的流程、动作、输入字段映射和当前用户动作权限;不要求额外授予宿主表单 CRUD 权限。
125
+ 普通 CRUD 表单不传此绑定,继续检查对应创建/修改权限。自定义选择器可通过 `searchResource` 的同名选项传递
126
+ 已声明的发起绑定,不能用它扩大目标资源的读取范围或执行动作。
127
+ 使用此功能的编译包自动要求平台能力 `workflow.named-input-sources` 的 `1.0.0` 版本;先检查目标平台能力,配套升级后再发布。
128
+ 平台按来源资源的字段权限和 PostgreSQL RLS 查询并返回完整资源快照。来源记录改名或删除后,
129
+ 已经保存的 `{label,value,resourceCode,snapshot}` 仍可直接展示,不需要再次查询。
130
+
131
+ 成员和部门同样保存完整显示快照:
132
+
133
+ ```ts
134
+ { code: 'owner', type: 'user.single', label: '负责人', required: true }
135
+ { code: 'participants', type: 'user.multiple', label: '参与人' }
136
+ { code: 'college', type: 'department.single', label: '学院', indexed: true, filter: true }
137
+ { code: 'supportDepartments', type: 'department.multiple', label: '协作部门' }
138
+ ```
139
+
140
+ 成员快照可包含头像、工号、职务、手机号、邮箱和所属部门;部门快照可包含完整路径、
141
+ 路径节点和父部门。凭证、Token 和认证秘密永远不能进入快照。
142
+
143
+ 仅选择时间时使用 `time`,并显式决定精度:
144
+
145
+ ```ts
146
+ { code: 'reminderMinute', type: 'time', label: '提醒时间', timePrecision: 'minute' }
147
+ { code: 'checkpointSecond', type: 'time', label: '检查时间', timePrecision: 'second' }
148
+ ```
149
+
150
+ 定位只支持钉钉定位或浏览器 Geolocation 采集 WGS84 经纬度。组件没有地址输入、
151
+ 手工定位或地图选点;服务商返回的地址/POI 只能作为该坐标的只读显示快照。
152
+
153
+ ## 移动选择交互
154
+
155
+ `MobileSurfaceFieldControl` 为成员、部门、级联和动态资源字段提供统一的移动弹层。
156
+ 直接使用目录选择组件时也可传 `mobile`;显式移动界面不会因窗口较宽而切回 PC 控件。
157
+ 应用继续声明同一份业务字段,使用平台组件即可,无需自己拼目录树或搜索接口。
158
+
159
+ - 成员按部门逐层浏览,部门支持逐层选择和下钻;搜索、部门层级和成员列表均按页读取。
160
+ - 级联通过路径导航选择末级项;多选或较多候选支持完整路径搜索,多选保留每条完整路径快照。
161
+ - 当前已选项独立显示,可以移除或清空;切换路径、搜索和翻页不丢失尚未确认的选择。
162
+ - 单选和多选均点击“确定”才写回表单;“取消”或“关闭”放弃本次弹层修改。候选读取失败可重试。
163
+
164
+ 界面使用平台封装的 Ant Design Mobile 组件;样式与弹层留在移动组件作用域内。
165
+ 存储快照、来源过滤和权限仍遵循上述标准契约,不为移动端增加第二套数据或权限接口。
166
+
167
+ ## 查询与权限
168
+
169
+ 列表、聚合、导出、动态来源和事务断言共用 `openxiangda.data-query/v2` 的有界 `where`:
170
+
171
+ ```ts
172
+ {
173
+ schemaVersion: 'openxiangda.data-query/v2',
174
+ where: {
175
+ and: [
176
+ { field: 'status', operator: 'eq', value: 'submitted' },
177
+ { field: 'customer', path: 'snapshot.level', operator: 'eq', value: 'A' },
178
+ ],
179
+ },
180
+ order: [{ field: 'status', direction: 'asc' }],
181
+ limit: 20,
182
+ }
183
+ ```
184
+
185
+ 客户端不能发送 SQL、PostgREST 表达式或任意 JSONPath。编译器只接受字段类型允许的运算符
186
+ 和已声明的快照路径,所有值都使用 SQL 参数。标量索引使用 BTREE,单快照 `value/label`
187
+ 使用表达式 BTREE,多值/JSON 使用 GIN,范围使用 GiST,模糊搜索使用 trigram GIN。
188
+
189
+ 资源 capability 决定能否执行 read/create/update/delete;数据策略决定该角色能操作哪些行;
190
+ 字段策略决定字段可读、可创建和可更新范围。当前用户字段和学院等业务范围都由同一声明生成
191
+ PostgreSQL RLS,列表、详情、聚合、导出、来源查询、审计和事务不能绕过。
192
+
193
+ ## 验证
194
+
195
+ 只检查应用时运行统一入口,随后按实际变化补充真实浏览器验收:
196
+
197
+ ```bash
198
+ pnpm openxiangda check
199
+
200
+ ```
201
+
202
+ 平台维护者负责字段存储、查询运算符、索引和 RLS 的内核回归。应用开发者验证自己声明的字段、角色和业务交互,详见[检查与验收](./testing.md)。
203
+
204
+ ## 移动附件和图片
205
+
206
+ 移动附件显示图标、文件名、大小和预览/下载/移除按钮;图片使用缩略图与加号上传格。
207
+ 文件数量和大小限制沿用字段声明。上传失败保留文件并显示完整错误,支持重试;移除上传中的文件后,
208
+ 迟到的结果不会回填表单。关闭编辑器同样使旧上传结果失效,已发出的请求可能继续在
209
+ 服务端完成。移除字段引用不等于删除存储文件。
210
+
211
+ 图片使用作用域内的移动图片预览,支持手势浏览、缩放和关闭;普通文件继续使用平台
212
+ 预览路由。文件值仍为 `DataFileRef[]`,上传与下载继续由原有平台接口鉴权。
213
+
214
+
215
+ ## 移动字段呈现与分组
216
+
217
+ 移动字段自身提供无边框输入、上下标签、行分隔和错误提示;标准表单与自定义页面使用同一
218
+ 字段组件。页面负责把相关字段组织在浅色背景上的白色分组中,不另设移动主题配置。
219
+
220
+ - 单选、复选直接展示选项;下拉单选/复选使用可搜索的底部弹层,多选展示已选数量。
221
+ - 日期使用月历,日期时间可切换时间滚轮;区间依次选择开始和结束,可返回上一步修改。
222
+ - 地址采用地区路径逐层选择,详细地址单独输入。定位沿用当前可用的钉钉或浏览器能力。
223
+ - 子表单直接展开行内字段,支持折叠、添加和删除;父表提交校验所有行,包括折叠的行。
224
+ 权限、最大行数、原子事务和已存行的 revision 继续由原有资源契约约束。
225
+ - 签名在底部画布手写并保存;图片、签名和附件仍通过托管文件接口上传与鉴权读取。
226
+ - 手机富文本编辑降级为多行文本。未修改时保留已有 HTML;修改后将纯文本转为转义后的
227
+ 段落 HTML,继续使用原有 `text.rich` 数据类型。
228
+
229
+ 评分是整数的可选控件,默认五颗星,声明示例:
230
+
231
+ ```ts
232
+ { code: 'score', type: 'number.integer', label: '评分', widget: 'rating', min: 0, max: 5 }
233
+ ```
234
+
235
+ `rating` 需要支持该控件的编译器、前端包与服务端 Surface 校验组合;存储、查询和校验仍为
236
+ 整数。它不会修改已有 `number.integer` 字段的默认数值输入控件。