openxiangda 2.31.0 → 2.31.1
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/documentation/backend.md +44 -0
- package/documentation/declarations-cheatsheet.md +2 -0
- package/documentation/frontend.md +6 -0
- package/documentation/getting-started.md +7 -7
- package/documentation/managed-concurrency-frontend.md +196 -0
- package/documentation/managed-concurrency.md +2 -0
- package/documentation/manifest.json +12 -6
- package/package.json +26 -27
- package/releases/2.31.1.json +38 -0
- package/skills/manifest.json +1 -1
- package/skills/openxiangda-v2/SKILL.md +6 -4
- package/skills/openxiangda-v2/references/backend.md +44 -0
- package/skills/openxiangda-v2/references/declarations-cheatsheet.md +2 -0
- package/skills/openxiangda-v2/references/frontend.md +6 -0
- package/skills/openxiangda-v2/references/getting-started.md +7 -7
- package/skills/openxiangda-v2/references/managed-concurrency-frontend.md +196 -0
- package/skills/openxiangda-v2/references/managed-concurrency.md +2 -0
package/documentation/backend.md
CHANGED
|
@@ -249,6 +249,50 @@ operations: [{
|
|
|
249
249
|
引用 1–16 个已声明资源;`sideEffects` 最多 20 条——只读必须为零,写操作至少一条具体副作用;
|
|
250
250
|
`concurrency` 可选 `none | revision`,`timeoutMs` 限 100–30000。
|
|
251
251
|
|
|
252
|
+
### 面向普通用户的 Agent 任务
|
|
253
|
+
|
|
254
|
+
`ai` 使操作进入技术能力目录;只有显式声明 `ai.agent` 才把操作交给普通用户的业务 Agent 发现。`visibility: 'task'` 是可办理的任务,`visibility: 'support'` 是该任务需要的只读查询。平台仍按当前用户的应用权限过滤,声明本身不授予权限。生成的资源 CRUD、没有 `agent` 的操作和 `support` 操作都不是普通用户首页的独立任务。
|
|
255
|
+
|
|
256
|
+
以下示例中,应用自己负责地点搜索与预约规则。给模型和用户的是“地点名称”,稳定地点引用只作为操作参数,用户无需填写地点编码。
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
const locations = {
|
|
260
|
+
code: 'reservation.locations', method: 'GET', path: '/api/reservations/locations',
|
|
261
|
+
capability: 'app:visitor-app:reservation:locations',
|
|
262
|
+
requestSchema: { type: 'object', properties: { keyword: { type: 'string' } } },
|
|
263
|
+
responseSchema: { type: 'object' },
|
|
264
|
+
ai: {
|
|
265
|
+
name: '查找可预约地点', description: '按名称搜索当前用户可选择的地点',
|
|
266
|
+
risk: 'read', resources: ['reservations'], sideEffects: [],
|
|
267
|
+
agent: { visibility: 'support' },
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
const enroll = {
|
|
271
|
+
code: 'reservation.enroll', method: 'POST', path: '/api/reservations/enroll',
|
|
272
|
+
capability: 'app:visitor-app:reservation:enroll',
|
|
273
|
+
requestSchema: {
|
|
274
|
+
type: 'object', required: ['location'],
|
|
275
|
+
properties: { location: { type: 'string' } },
|
|
276
|
+
},
|
|
277
|
+
responseSchema: { type: 'object' },
|
|
278
|
+
ai: {
|
|
279
|
+
name: '发起访客预约', description: '校验访客信息、地点与时间后创建预约',
|
|
280
|
+
risk: 'write', resources: ['reservations'], sideEffects: ['创建访客预约'],
|
|
281
|
+
agent: {
|
|
282
|
+
visibility: 'task',
|
|
283
|
+
examples: ['帮我预约访客', '明天下午邀请张老师来访'],
|
|
284
|
+
aliases: ['发起邀约'],
|
|
285
|
+
supportOperations: ['reservation.locations'],
|
|
286
|
+
inputLookups: { location: 'reservation.locations' },
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
两个 `capability` 必须在 `authz.capabilities` 中声明为 `kind: 'backend'`,再授予相应角色。`supportOperations` 和 `inputLookups` 引用同一应用中的操作 code;被引用操作必须声明 `visibility: 'support'`、`risk: 'read'` 且使用 GET。`inputLookups` 的键必须存在于任务的 `requestSchema.properties`。示例问法最多 12 条、别名最多 20 条、辅助操作最多 12 个、字段绑定最多 32 个;运行时辅助查询仍应分页、有界,并返回业务标签与稳定引用。
|
|
293
|
+
|
|
294
|
+
业务规则必须在后端操作中验证;提示词、卡片预填和前端校验不能取代权限与业务校验。完整输入、缺失输入、同名地点、权限不足、重复提交和结果未知都应有应用测试与回执核对。任务声明随 AppVersion 的 AI Catalog 一起发布,不能另建手工目录。交互卡片与自动执行仅在平台 Agent Host 支持后启用;本声明本身不改变现有 MCP Facade 的写入确认协议。
|
|
295
|
+
|
|
252
296
|
宿主(平台 AI 网关)用该目录装配 MCP Facade,应用不自己实现协议:
|
|
253
297
|
|
|
254
298
|
- **单应用 Facade**(`createApplicationAiMcpServer`):每个能力一个工具,只读能力直接以
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
| --- | --- |
|
|
56
56
|
| 写操作的 `ai.sideEffects` 至少一条具体副作用 | `ai: { name: '受理派单', ..., risk: 'write', sideEffects: ['更新报修单状态为处理中', '写入一条派工记录'] }` |
|
|
57
57
|
| GET 操作的 `ai.risk` 只能是 `read`;写操作不能是 `read` | `risk: 'read'` ↔ `method: 'GET'` |
|
|
58
|
+
| 面向普通用户的 Agent 只发现显式声明的业务任务;底层 CRUD 不会自动成为任务 | `ai.agent: { visibility: 'task', examples: ['帮我发起报修'], aliases: ['提交报修'] }` |
|
|
59
|
+
| 任务输入需要地点、人员等业务选择时,绑定同应用的只读辅助操作,用户不填内部编码 | `agent: { visibility: 'task', supportOperations: ['repair.locations'], inputLookups: { location: 'repair.locations' } }`;被引用的 GET 操作须有 `agent: { visibility: 'support' }`,`location` 须是请求 Schema 字段 |
|
|
58
60
|
| controller 路由必须绑定 `@OpenXiangdaOperation(appOperations.<code>)`,普通 CRUD 不写 controller | check 门禁会拒绝未绑定路由 |
|
|
59
61
|
| 事务守卫 `errorCode` 必须匹配 `^OPENXIANGDA_[A-Z0-9_]{1,96}$` | `errorCode: 'OPENXIANGDA_REPAIR_REQUEST_NOT_PENDING'` |
|
|
60
62
|
|
|
@@ -47,6 +47,12 @@ Agent 可以在标准后台内部优化布局、视觉和交互,但后台仍
|
|
|
47
47
|
|
|
48
48
|
在页面设计或实现说明中写清选用组件、理由与必要边界。已有需求内的技术选型由开发者完成,无需让用户逐个指定依赖。ECharts 的按需导入方式见[官方说明](https://echarts.apache.org/handbook/en/basics/import/);后台无需为图表增加手机适配,用户移动图表只按已确认需求处理。
|
|
49
49
|
|
|
50
|
+
## 热点读取与限量提交 {#managed-concurrency}
|
|
51
|
+
|
|
52
|
+
报名、预约、抢票和限量申领按[并发能力的前端接入](./managed-concurrency-frontend.md)选择页面形式:详情使用命名缓存读取,关键动作使用受管命令,按钮与状态面板共享同一操作快照。保持应用现有页面归属与组件体系;身份、排队、事务和配额由平台负责。
|
|
53
|
+
|
|
54
|
+
先填写再提交、放行后短确认和预占后确认具有不同的输入与有效期语义;不要把通用表单 `onSubmit` 或整页 JSX 包装器当作自动防超卖方案。当前可用 API、代码示例、恢复文案及待补齐的契约均在该专题说明。
|
|
55
|
+
|
|
50
56
|
## 数据和权限
|
|
51
57
|
|
|
52
58
|
`modules/` 中的业务模型由编译器派生 DataResource 契约,`openxiangda.config.ts` 声明页面
|
|
@@ -68,10 +68,10 @@ MCP 服务随项目根包一起安装,AI 客户端的 stdio 连接仍需配置
|
|
|
68
68
|
以下命令的版本占位符由随包资料替换为该根包的精确版本。网站源码阅读者应先确认要使用的发行版本。
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
pnpm dlx openxiangda@2.31.
|
|
72
|
-
pnpm dlx openxiangda@2.31.
|
|
73
|
-
pnpm dlx openxiangda@2.31.
|
|
74
|
-
pnpm dlx openxiangda@2.31.
|
|
71
|
+
pnpm dlx openxiangda@2.31.1 skill install --force
|
|
72
|
+
pnpm dlx openxiangda@2.31.1 auth status --base-url <平台地址> --json
|
|
73
|
+
pnpm dlx openxiangda@2.31.1 login --cwd my-app --base-url https://platform.example.com
|
|
74
|
+
pnpm dlx openxiangda@2.31.1 create my-app --base-url https://platform.example.com
|
|
75
75
|
cd my-app
|
|
76
76
|
pnpm openxiangda context --json
|
|
77
77
|
pnpm openxiangda dev
|
|
@@ -174,9 +174,9 @@ MCP 的 `docs_read` 可以读取本说明,当前没有独立的源码操作 MC
|
|
|
174
174
|
无需本地工作区,使用本 Skill 随包精确版本或已安装的对应 CLI:
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
pnpm dlx openxiangda@2.31.
|
|
178
|
-
pnpm dlx openxiangda@2.31.
|
|
179
|
-
pnpm dlx openxiangda@2.31.
|
|
177
|
+
pnpm dlx openxiangda@2.31.1 auth status --base-url <平台> --json
|
|
178
|
+
pnpm dlx openxiangda@2.31.1 source resolve <仓库URL> --base-url <平台> --json
|
|
179
|
+
pnpm dlx openxiangda@2.31.1 source clone <仓库URL> <新目录> --base-url <平台> --json
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
登录缺失或站点不匹配时,先按该平台执行 login。resolve 根据平台已经登记的绑定返回
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# 并发能力的前端接入
|
|
2
|
+
|
|
3
|
+
适用于活动报名、限量申领、抢票和预约。本文使用 `openxiangda@2.31.0` 引入的公开 API;目标平台必须支持 `data.managed-concurrency@1.0.0`。先按[缓存、排队与整数配额](./managed-concurrency.md)声明读取、命令、配额和权限,再选择页面接入形式。
|
|
4
|
+
|
|
5
|
+
推荐体验是正常浏览内容,在用户明确提交时按需等待,并持续核对同一次申请的结果。低负载时可以很快完成,不需要人为添加等待页。
|
|
6
|
+
|
|
7
|
+
## 选择接入形式 {#patterns}
|
|
8
|
+
|
|
9
|
+
| 形式 | 适用场景 | 用户流程 | 当前接入方式 |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| 热点内容直接浏览 | 详情、场次、活动说明 | 浏览 → 按需刷新 | `client.read`,应用处理读取状态 |
|
|
12
|
+
| 按钮发起、原地等待 | 一键报名、限量申领 | 点击 → 排队 → 处理 → 结果 | `useManagedCommand` + `ManagedCommandStatus` |
|
|
13
|
+
| 先填写、再排队提交 | 有选项的预约和申请 | 填写校验 → 确认内容 → 排队提交 | 有界参数组合现有 hook;复杂资料需要额外冻结契约 |
|
|
14
|
+
| 放行后短确认 | 输入已确定、加载成本较高的确认步骤 | 进入 → 等待 → 短确认 → 提交 | `autoAccept: false` + `ManagedCommandGate` + `submit()` |
|
|
15
|
+
| 预占后确认 | 暂留名额、时段预约 | 预占 → 核实有效期 → 确认或释放 | 应用声明预占/确认/释放命令,配合 `client.allocation` |
|
|
16
|
+
| 返回后恢复原申请 | 刷新、断网、查看本人结果 | 恢复原请求 → 核对 → 结果 | hook 挂载恢复,或 `client.result` 查询已知操作 |
|
|
17
|
+
|
|
18
|
+
常见报名页面可以组合前三种:详情走缓存,用户确认活动和选项后提交,按钮附近展开状态;成功后进入本人记录。PC 状态区、抽屉和移动端底部区域可以使用不同外观,共用同一个操作状态。
|
|
19
|
+
|
|
20
|
+
## 页面与平台的分工 {#ownership}
|
|
21
|
+
|
|
22
|
+
页面负责内容、选项、字段校验、业务文案和结果路由。平台负责当前用户身份、准入、幂等、事务、配额和权威结果。身份使用声明中的可信 `actor`,不使用用户填写的工号决定记录归属。
|
|
23
|
+
|
|
24
|
+
平台身份就绪后创建 `createManagedConcurrencyClient()`,并保持实例稳定。它绑定当前应用、环境和主体;身份/环境变化时重新挂载对应页面子树,不能复用旧客户端或旧视图。
|
|
25
|
+
|
|
26
|
+
一次操作只使用一个 `useManagedCommand`。按钮、状态条和弹层消费同一个快照,不要各自调用 hook 创建观察循环。当前 SDK 通过 `openxiangda/react` 和 `openxiangda/mobile` 导出相同的受管并发能力;普通字段仍复用[Field Kit](./field-components.md),界面遵循[前端组件与页面归属](./frontend.md)。
|
|
27
|
+
|
|
28
|
+
不要把受管模型继续绑定到普通 CRUD 提交按钮,也不要传入任意 `async onSubmit` 并假设它会自动获得排队和原子保证。最终动作必须是已发布的受管命令。
|
|
29
|
+
|
|
30
|
+
## 热点详情读取 {#cached-read}
|
|
31
|
+
|
|
32
|
+
以下示例对应源码 `examples/managed-concurrency/declaration.ts` 中的 `offer` 命名读取。`offerId` 必须是该应用真实资源的 UUID:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createManagedConcurrencyClient } from 'openxiangda/react';
|
|
36
|
+
|
|
37
|
+
type Client = ReturnType<typeof createManagedConcurrencyClient>;
|
|
38
|
+
type Offer = { id: string; title: string; closesAt: string };
|
|
39
|
+
|
|
40
|
+
export async function readOffer(
|
|
41
|
+
client: Client,
|
|
42
|
+
offerId: string,
|
|
43
|
+
signal?: AbortSignal,
|
|
44
|
+
) {
|
|
45
|
+
return client.read<Offer>('offer', { id: offerId }, signal);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
将读取接入应用已有的异步查询状态:初次加载显示局部骨架,空数组显示无可展示内容,失败提供局部重试。组件卸载或资源变化时中止旧请求,迟到响应不能覆盖新资源。同页多个观察者可复用应用已有查询层,查询键包含 `client.scope`、读取 code 和规范化参数,身份变化时清除旧主体视图;当前没有内置的 `useManagedRead` hook。
|
|
50
|
+
|
|
51
|
+
`freshness: 'stale'` 表示返回的是允许使用的旧内容,并不证明后台刷新已经成功。保留内容并提示“当前展示最近一次可用信息”;按新鲜期和有界退避刷新,超过 `staleUntil` 后不能继续无限展示。手动刷新仍调用同一命名读取,不绕回普通 CRUD。
|
|
52
|
+
|
|
53
|
+
详情与余量使用不同读取声明。页面倒计时本地显示,不能每秒重查活动;资格、开放时间和扣减仍由服务端决定。缓存余量不保证获票;允许补量或释放的业务,也不能因旧余量为零永久阻止新的有效申请。等待期间不要重复读取详情或表单。
|
|
54
|
+
|
|
55
|
+
## 一键申请与原结果恢复 {#one-click}
|
|
56
|
+
|
|
57
|
+
示例对应官方声明中的 `claim` 命令。父级在身份就绪后挂载,用户、环境或 `offerId` 变化时重新挂载本组件:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useMemo } from 'react';
|
|
61
|
+
import {
|
|
62
|
+
createManagedConcurrencyClient,
|
|
63
|
+
useManagedCommand,
|
|
64
|
+
ManagedCommandStatus,
|
|
65
|
+
} from 'openxiangda/react';
|
|
66
|
+
|
|
67
|
+
export function ClaimAction({ offerId }: { offerId: string }) {
|
|
68
|
+
const client = useMemo(() => createManagedConcurrencyClient(), []);
|
|
69
|
+
const command = useManagedCommand({
|
|
70
|
+
client,
|
|
71
|
+
command: 'claim',
|
|
72
|
+
input: { id: offerId },
|
|
73
|
+
storageKey: `claim:${offerId}`,
|
|
74
|
+
});
|
|
75
|
+
// start / resume 的失败进入快照;保留原请求,按错误原因恢复。
|
|
76
|
+
const observe = (action: () => Promise<void>) => {
|
|
77
|
+
void action().catch(() => {});
|
|
78
|
+
};
|
|
79
|
+
return (
|
|
80
|
+
<section>
|
|
81
|
+
<button disabled={command.state !== 'idle'}
|
|
82
|
+
onClick={() => observe(() => command.start())}>
|
|
83
|
+
申请
|
|
84
|
+
</button>
|
|
85
|
+
<ManagedCommandStatus snapshot={command} />
|
|
86
|
+
{command.state === 'error' && (
|
|
87
|
+
<div>
|
|
88
|
+
<p>暂时无法确认结果,请核对原申请。</p>
|
|
89
|
+
<button onClick={() => observe(() => command.resume())}>
|
|
90
|
+
核对原申请
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
{command.state === 'succeeded' && <p>可查看本次申请记录。</p>}
|
|
95
|
+
</section>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
挂载只恢复已有意图;用户点击 `start()` 才创建新意图。默认放行后自动受理,适合点击已经明确表达提交意愿的动作。不要在 `useEffect` 中调用 `start()` 自动报名,不要手写循环换请求键重试。
|
|
101
|
+
|
|
102
|
+
等待只轮询票据,受理后查询原命令结果;SDK 遵守建议间隔并退避。按钮禁用只是体验,服务端仍执行幂等和业务去重。成功时可从 `receipt.result.items` 定位已创建的业务记录;不要在成功回调里再次写订单、扣名额或发通知。导航和视图刷新应允许重复执行。
|
|
103
|
+
|
|
104
|
+
## 填写后再提交 {#form-submit}
|
|
105
|
+
|
|
106
|
+
先校验字段,向用户展示确认摘要,再将输入固定并启动命令。受管 hook 使用固定后的输入,不要直接绑定每次键入都变化的表单对象。提交后刷新页面,要从本次意图的稳定资料恢复相同输入,再挂载 hook;不能用空表单覆盖原请求。
|
|
107
|
+
|
|
108
|
+
排队后需要修改资料时,先请求取消并核实结果,再允许编辑和创建新意图。取消与受理、执行、成功可能竞争;服务端返回已经成功时,应展示原结果,不能继续当作草稿编辑或假定取消成功。组件卸载或关闭面板仅停止观察,不取消命令。
|
|
109
|
+
|
|
110
|
+
当前命令参数仅支持 UUID、枚举字符串和有限整数。场次、规格等可以按声明接入;配额 `units` 由命令声明固定,传入整数参数不会自动改变扣减数量。姓名、任意备注、附件及整份表单 JSON 不属于当前参数范围。
|
|
111
|
+
|
|
112
|
+
复杂资料需要先定义并验证“草稿 → 不可变资料版本 → 命令引用版本 ID”的契约,核实作者、目标资源、版本和字段权限,并阻止冻结资料被普通 CRUD 改写。只传 `draftId` 而允许原稿继续变化不满足要求。该通用冻结机制和表单桥接尚未提供,不能把任意表单保存接口包进 hook 就当作已支持;草稿保存本身也需要独立预算。
|
|
113
|
+
|
|
114
|
+
## 放行后的短确认 {#confirmation-gate}
|
|
115
|
+
|
|
116
|
+
此形式要求排队前已确定命令参数,用户明确请求进入确认步骤。沿用前例的身份/client 生命周期,创建命令时设置 `autoAccept: false`;入口按钮调用 `start()`,获准后的确认按钮调用 `submit()`:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// command 已由 useManagedCommand 创建,并设置 autoAccept: false。
|
|
120
|
+
// Confirmation 是应用组件;它只核对已经固定的输入。
|
|
121
|
+
<ManagedCommandGate snapshot={command}>
|
|
122
|
+
{() => (
|
|
123
|
+
<Confirmation
|
|
124
|
+
onConfirm={() => void command.submit().catch(handleActionError)}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</ManagedCommandGate>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`ManagedCommandGate` 从与 hook 相同的公开入口导入。上例是前例中的组合片段,`Confirmation` 和 `handleActionError` 由应用定义;错误处理必须保留原请求,不能直接清除并重提。耗时请求和懒加载应在子树内发生,父组件提前发请求会失去保护。
|
|
131
|
+
|
|
132
|
+
`admitted` 是短期受理许可,不是名额预占,也不是长时间填写表单的许可证。当前 Gate 只按快照状态决定是否挂载,没有内建到期倒计时;应用按 `waiting.expiresAt` 提示并核实,最终提交时服务端校验许可,SDK 沿原请求恢复。
|
|
133
|
+
|
|
134
|
+
“先进入页面,再任意决定最终输入”需要与命令输入解耦的页面准入契约,当前 Gate 不提供这个语义。它在应用启动、身份就绪后工作,不能替代静态资源、登录、启动元数据和整个站点入口的保护。
|
|
135
|
+
|
|
136
|
+
## 预占与最终确认 {#reservation}
|
|
137
|
+
|
|
138
|
+
应用先声明 `quota.action: 'allocate'`、`mode: 'reserved'` 和 `reservationSeconds`,另声明确认/释放命令。三个动作分别使用稳定恢复键,并按[配额生命周期](./managed-concurrency.md#配额和发布边界)绑定原分配 ID。
|
|
139
|
+
|
|
140
|
+
预占命令 `succeeded` 只证明预占事务曾成功。先读取 `receipt.result.allocation.id`,再调用 `client.allocation(allocationId)` 核实当前状态,界面显示“已暂留名额”。确认或释放后也要核对当前分配,不能用旧成功回执证明仍持有名额。
|
|
141
|
+
|
|
142
|
+
进入页面、回到前台和临近到期时有界核实。`expiresAt` 用于展示估算倒计时;当前回执没有 `serverNow`,浏览器时间不能作为最终过期依据。归零显示“正在核实有效期”,确认和到期竞争以服务端结果为准。关闭窗口不自动释放。
|
|
143
|
+
|
|
144
|
+
同池/主体永久去重;释放后不会自动允许同一主体重申同一池。需要下一轮申请时使用新的权威资源/池,不删除分配记录或换请求键绕过限制。
|
|
145
|
+
|
|
146
|
+
## 状态文案与用户动作 {#states}
|
|
147
|
+
|
|
148
|
+
| 状态 | 界面含义 | 动作规则 |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| `idle` | 可以申请 | 明确点击后 `start()` |
|
|
151
|
+
| `waiting` | 正在等待进入,尚未获得名额 | 等待;可申请退出排队 |
|
|
152
|
+
| `admitted` | 已轮到您,可以确认 | 手动确认形式调用 `submit()` |
|
|
153
|
+
| `accepted` / `executing` / `retry_wait` | 已收到申请,正在处理 | 查询原结果;按服务端规则申请取消 |
|
|
154
|
+
| `recovering` | 正在核对原申请 | 保留原键,继续退避观察 |
|
|
155
|
+
| `error` | 暂时无法确认或无法继续 | 按原因恢复登录、处理存储或 `resume()`;不统一显示报名失败 |
|
|
156
|
+
| `succeeded` | 权威事务已提交 | 按业务显示报名成功或暂留名额;预占另核实分配 |
|
|
157
|
+
| `rejected` | 明确未通过 | 区分售罄、资格、截止等业务原因 |
|
|
158
|
+
| `expired` / `cancelled` | 等待或命令到期、已取消 | 区分票据与命令终态,明确结束后才 `clear()` |
|
|
159
|
+
|
|
160
|
+
排队位置只在 `waiting.position` 存在时显示,不换算成保证等待时长或保证名额;缺少位置时显示等待文案,不画虚假百分比。临时队列故障后位置可能变化。
|
|
161
|
+
|
|
162
|
+
`ManagedCommandStatus` 支持 `errorMessages` 映射回执错误。产品层同时检查 `snapshot.errorCode` 与 `receipt.errorCode`,补齐身份变化、存储不可用、恢复输入冲突等原因。用户文案不暴露数据库、锁或票据内部信息。
|
|
163
|
+
|
|
164
|
+
`cancel()` 的 Promise 失败也要明确展示“取消结果尚未确认”,保留原意图并恢复查询;不要吞掉取消错误后宣布取消成功。关闭状态面板、离开页面与取消申请使用不同按钮和文案。状态变化使用无障碍提示,抽屉/底部区域遵循现有组件的焦点管理。
|
|
165
|
+
|
|
166
|
+
## 刷新、断网与我的申请 {#recovery}
|
|
167
|
+
|
|
168
|
+
默认恢复信息保存在 `sessionStorage`,按应用、环境、主体、命令和 `storageKey` 隔离。同标签页刷新可以恢复;`storageKey` 必须稳定,不能在每次渲染时随机生成。存储不可用时明确停止或处理,不能悄悄改用易丢失的内存继续提交。
|
|
169
|
+
|
|
170
|
+
已知 `operationId` 或原 `requestKey` 时,可以用 `client.result` 查询原结果。已成功业务记录可通过本人权限下的普通数据查询展示。当前没有“列出我的所有进行中命令”的接口;关闭标签后找回、跨设备继续和完整处理中列表需要额外的受管操作索引,不能把浏览器存储当作全平台事实。
|
|
171
|
+
|
|
172
|
+
不要把超时解释为提交失败,也不要在结果未知时 `clear()`。重试、恢复与返回入口都应该指向原意图。配额永久去重与界面请求键是不同边界:清除一个已终结的界面状态不代表可以再次获得同一池名额。
|
|
173
|
+
|
|
174
|
+
## 当前组件与后续封装边界 {#available-components}
|
|
175
|
+
|
|
176
|
+
当前可用:`createManagedConcurrencyClient`、`useManagedCommand`、`ManagedCommandStatus`、`ManagedCommandGate`,以及客户端的读取、结果、取消和分配状态方法。
|
|
177
|
+
|
|
178
|
+
`useManagedRead`、`ManagedCommandButton`、`ManagedCommandPanel`、`useManagedFormSubmission`、`useManagedAllocation`、`AllocationStatus` 是建议的后续封装名称,当前不是公开导出。不要照这些名称编写 import。应用现在可以使用已有 hook 与平台普通 UI 组件组合界面,后续标准封装仍应复用同一状态机和传输。
|
|
179
|
+
|
|
180
|
+
复杂资料冻结、跨设备命令索引、独立页面准入及精确倒计时需要相应的平台契约,不能仅通过前端外观补齐。SDK 版本、平台部署和实际业务容量需要分别确认。
|
|
181
|
+
|
|
182
|
+
## 接入验收 {#acceptance}
|
|
183
|
+
|
|
184
|
+
| 操作 | 应观察到的结果 |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| 打开详情或重渲染 | 不自动报名;详情与余量按声明读取,不因排队反复查表 |
|
|
187
|
+
| 连点、刷新、提交响应丢失 | 沿用原意图,恢复原结果,不生成重复业务记录 |
|
|
188
|
+
| 排队时编辑资料 | 固定原输入,取消尚未核实前不切换到另一次提交 |
|
|
189
|
+
| 关闭状态区后返回 | 继续查看原操作,不宣称已取消 |
|
|
190
|
+
| 取消与受理/成功同时发生 | 显示服务端原结果;错误时保留待核实状态 |
|
|
191
|
+
| 身份切换、存储被禁用 | 不串用旧主体资料,不静默丢弃恢复能力 |
|
|
192
|
+
| 网络或依赖异常 | 退避观察,保留原键,不绕回数据库或快速换键重提 |
|
|
193
|
+
| 许可到期、预占到期 | 分别核实准入与分配,互不冒充 |
|
|
194
|
+
| 键盘、屏幕阅读器、窄屏 | 状态可读,焦点可控,关闭与取消语义清楚 |
|
|
195
|
+
|
|
196
|
+
这些检查与目标环境的吞吐、P95/P99、授权查询成本和故障恢复验证一起完成;页面演示和 SDK 单元测试不能代替真实业务验收。
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
`data.concurrency` 是可选的平台声明,用于限量申领、抢票和预约。平台负责缓存回源、等待资格、持久受理、受控 Native 事务及整数配额;应用通过 `openxiangda/react` 或 `openxiangda/mobile` 调用。平台必须声明能力 `data.managed-concurrency@1.0.0`。旧平台会在编译/发布能力检查阶段拒绝此声明。
|
|
4
4
|
|
|
5
|
+
页面开发参见[并发能力的前端接入](./managed-concurrency-frontend.md):按热点详情、一键申请、填完再提交、短确认入口、预占确认和原结果恢复选择交互方式,并核对当前 API 与尚未提供的封装边界。
|
|
6
|
+
|
|
5
7
|
## 声明与权限
|
|
6
8
|
|
|
7
9
|
完整且由双编译器测试验证的示例位于源码 `examples/managed-concurrency/declaration.ts`。调用 `defineOpenXiangdaApp(concurrencyExample())` 即可编译。把示例中的 `data.concurrency` 合入应用配置,并按实际业务配置模型和参与权限。
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": "openxiangda.documentation/v1",
|
|
3
|
-
"version": "2.31.
|
|
3
|
+
"version": "2.31.1",
|
|
4
4
|
"topics": [
|
|
5
5
|
{
|
|
6
6
|
"id": "getting-started",
|
|
7
7
|
"title": "安装与开始开发",
|
|
8
8
|
"file": "getting-started.md",
|
|
9
|
-
"sha256": "
|
|
9
|
+
"sha256": "108af379386c6802d4b775e2f3d0f08fe71b7109e8d365548299b2fb1cf55b9d"
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"id": "product-design",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"id": "declarations-cheatsheet",
|
|
37
37
|
"title": "声明速查:一次写对 config",
|
|
38
38
|
"file": "declarations-cheatsheet.md",
|
|
39
|
-
"sha256": "
|
|
39
|
+
"sha256": "17357e46cfea90ecb9752d33bc378e2c901df6260aca0981ea5710ec82dbbd4e"
|
|
40
40
|
},
|
|
41
41
|
{
|
|
42
42
|
"id": "application-foundation",
|
|
@@ -60,7 +60,7 @@
|
|
|
60
60
|
"id": "frontend",
|
|
61
61
|
"title": "页面与标准组件扩展",
|
|
62
62
|
"file": "frontend.md",
|
|
63
|
-
"sha256": "
|
|
63
|
+
"sha256": "93db2bef516307475e86c70a4146055f0ada86673f88b70b19a588d8ea035c68"
|
|
64
64
|
},
|
|
65
65
|
{
|
|
66
66
|
"id": "field-components",
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
"id": "backend",
|
|
91
91
|
"title": "按需后端与业务动作",
|
|
92
92
|
"file": "backend.md",
|
|
93
|
-
"sha256": "
|
|
93
|
+
"sha256": "12312dd666b8a7991282acf78a839c7af336aefa007a08645f5e30b18e67b8b4"
|
|
94
94
|
},
|
|
95
95
|
{
|
|
96
96
|
"id": "decimal-reservations",
|
|
@@ -102,7 +102,13 @@
|
|
|
102
102
|
"id": "managed-concurrency",
|
|
103
103
|
"title": "缓存、排队与整数配额",
|
|
104
104
|
"file": "managed-concurrency.md",
|
|
105
|
-
"sha256": "
|
|
105
|
+
"sha256": "21d7e1e6ce3f230f88013cd6522ce6195bd4079d55af6d0b74a525c209fd5601"
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
"id": "managed-concurrency-frontend",
|
|
109
|
+
"title": "并发能力的前端接入",
|
|
110
|
+
"file": "managed-concurrency-frontend.md",
|
|
111
|
+
"sha256": "294fa5655a9f0673ec871d204ad53a15bf8dfc8a9a91a2975d8eafe58f402469"
|
|
106
112
|
},
|
|
107
113
|
{
|
|
108
114
|
"id": "administration",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openxiangda",
|
|
3
|
-
"version": "2.31.
|
|
3
|
+
"version": "2.31.1",
|
|
4
4
|
"description": "OpenXiangda 2.0 的统一命令、应用 SDK、MCP 与中文 AI 技能资料。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -60,13 +60,13 @@
|
|
|
60
60
|
"antd-mobile": "5.42.3",
|
|
61
61
|
"dayjs": "1.11.18",
|
|
62
62
|
"docx-preview": "0.3.7",
|
|
63
|
-
"openxiangda-cli": "2.6.
|
|
64
|
-
"openxiangda-contracts": "2.
|
|
65
|
-
"openxiangda-devkit-core": "2.
|
|
63
|
+
"openxiangda-cli": "2.6.3",
|
|
64
|
+
"openxiangda-contracts": "2.29.0",
|
|
65
|
+
"openxiangda-devkit-core": "2.32.0",
|
|
66
66
|
"openxiangda-legacy": "npm:openxiangda@1.0.269",
|
|
67
|
-
"openxiangda-mcp": "2.0.
|
|
68
|
-
"openxiangda-nest": "2.7.
|
|
69
|
-
"openxiangda-skill-kit": "2.3.
|
|
67
|
+
"openxiangda-mcp": "2.0.53",
|
|
68
|
+
"openxiangda-nest": "2.7.6",
|
|
69
|
+
"openxiangda-skill-kit": "2.3.27",
|
|
70
70
|
"xlsx": "https://github.com/1377385356/openxiangda/releases/download/vendor-mirror/xlsx-0.20.3.tgz"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
@@ -132,42 +132,41 @@
|
|
|
132
132
|
},
|
|
133
133
|
"openxiangdaRelease": {
|
|
134
134
|
"schemaVersion": "openxiangda.release-notes/v1",
|
|
135
|
-
"version": "2.31.
|
|
135
|
+
"version": "2.31.1",
|
|
136
136
|
"status": "reviewed",
|
|
137
|
-
"title": "OpenXiangda 2.31.
|
|
138
|
-
"summary": "
|
|
137
|
+
"title": "OpenXiangda 2.31.1:应用业务 Agent 任务声明",
|
|
138
|
+
"summary": "应用可以在现有 AI 操作上显式声明面向普通用户的业务任务、常见问法、只读辅助能力和输入字段查询绑定。编译器把这些信息并入同一个 AppVersion AI Catalog,平台按当前用户权限发现和执行。",
|
|
139
139
|
"newFeatures": [
|
|
140
|
-
"
|
|
141
|
-
"
|
|
142
|
-
"
|
|
143
|
-
"
|
|
140
|
+
"ai.agent.visibility 区分可独立办理的 task 与任务引用的只读 support;未声明的底层 CRUD 不自动成为普通用户任务。",
|
|
141
|
+
"任务可以声明 examples、aliases、supportOperations 和 inputLookups;编译器校验辅助操作只读、引用闭合和请求字段存在。",
|
|
142
|
+
"docs/backend.md、声明速查和 OpenXiangda 2.0 Skill 提供接入流程及访客预约示例。",
|
|
143
|
+
"并发能力前端指南覆盖热点读取、申请、排队、预占确认与原结果恢复,并标明已提供 API 和待实现封装。"
|
|
144
144
|
],
|
|
145
145
|
"fixes": [],
|
|
146
146
|
"affectedUsers": [
|
|
147
|
-
"
|
|
147
|
+
"希望把业务操作接入平台助手或应用 Agent 的 OpenXiangda 2.0 应用开发者。",
|
|
148
|
+
"需要实现缓存、排队或限额场景前端的应用开发者。"
|
|
148
149
|
],
|
|
149
150
|
"upgradeSteps": [
|
|
150
|
-
"升级到 openxiangda@2.31.
|
|
151
|
-
"
|
|
152
|
-
"
|
|
153
|
-
"
|
|
151
|
+
"升级到 openxiangda@2.31.1 并更新锁文件,先实现完整业务操作、授权和只读辅助查询,再按 docs/backend.md 声明 ai.agent。",
|
|
152
|
+
"在测试环境运行 check 与完整输入、缺失输入、同名对象、权限不足和重复提交场景;发布应用版本后核对 AI Catalog 摘要与当前环境 Head。",
|
|
153
|
+
"平台服务端须部署支持显式业务任务发现的版本;npm 包发布本身不会升级平台或自动公开旧应用操作。",
|
|
154
|
+
"并发前端接入按 docs/managed-concurrency-frontend.md 选择模式,先核对已导出的 API 与目标平台能力。"
|
|
154
155
|
],
|
|
155
156
|
"knownLimitations": [
|
|
156
|
-
"
|
|
157
|
-
"
|
|
158
|
-
"
|
|
159
|
-
"同一配额池和主体永久去重,释放后不会自动重新报名;新一轮业务使用新的权威资源或配额池。",
|
|
160
|
-
"资源保护上限不代表生产吞吐保证;公开包升级不会自动升级客户平台。"
|
|
157
|
+
"本次包仅增加业务任务元数据与编译校验;交互卡片资源、持久 Interaction 和 MCP Apps Host 由后续平台与应用批次交付。",
|
|
158
|
+
"现有独立 MCP Facade 的写入预览与确认协议保持原样;平台自有 Agent 的自动写入由平台执行层和权限边界负责。",
|
|
159
|
+
"旧应用的底层 CRUD 不会自动转换为普通用户任务,需应用维护者完成业务语义与辅助能力接入。"
|
|
161
160
|
],
|
|
162
161
|
"issues": [],
|
|
163
162
|
"compatibility": {
|
|
164
163
|
"node": ">=24",
|
|
165
164
|
"workspaceGenerations": "v2",
|
|
166
|
-
"platform": "
|
|
165
|
+
"platform": "平台业务 Agent 任务发现需配套部署支持 ai.agent 元数据的服务端版本。",
|
|
167
166
|
"v1": "独立 V1 引擎和工作区不受影响。"
|
|
168
167
|
},
|
|
169
|
-
"sha256": "
|
|
170
|
-
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.
|
|
168
|
+
"sha256": "37c61ac4c9b17a8ca688cc9e1f38f4ed106754a7342e1a6aa9b2095e954a5118",
|
|
169
|
+
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.1"
|
|
171
170
|
},
|
|
172
171
|
"scripts": {
|
|
173
172
|
"build": "node ../../scripts/prune-package-dist.mjs && tsc -p tsconfig.json && node scripts/copy-assets.mjs",
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": "openxiangda.release-notes/v1",
|
|
3
|
+
"version": "2.31.1",
|
|
4
|
+
"status": "reviewed",
|
|
5
|
+
"title": "OpenXiangda 2.31.1:应用业务 Agent 任务声明",
|
|
6
|
+
"summary": "应用可以在现有 AI 操作上显式声明面向普通用户的业务任务、常见问法、只读辅助能力和输入字段查询绑定。编译器把这些信息并入同一个 AppVersion AI Catalog,平台按当前用户权限发现和执行。",
|
|
7
|
+
"newFeatures": [
|
|
8
|
+
"ai.agent.visibility 区分可独立办理的 task 与任务引用的只读 support;未声明的底层 CRUD 不自动成为普通用户任务。",
|
|
9
|
+
"任务可以声明 examples、aliases、supportOperations 和 inputLookups;编译器校验辅助操作只读、引用闭合和请求字段存在。",
|
|
10
|
+
"docs/backend.md、声明速查和 OpenXiangda 2.0 Skill 提供接入流程及访客预约示例。",
|
|
11
|
+
"并发能力前端指南覆盖热点读取、申请、排队、预占确认与原结果恢复,并标明已提供 API 和待实现封装。"
|
|
12
|
+
],
|
|
13
|
+
"fixes": [],
|
|
14
|
+
"affectedUsers": [
|
|
15
|
+
"希望把业务操作接入平台助手或应用 Agent 的 OpenXiangda 2.0 应用开发者。",
|
|
16
|
+
"需要实现缓存、排队或限额场景前端的应用开发者。"
|
|
17
|
+
],
|
|
18
|
+
"upgradeSteps": [
|
|
19
|
+
"升级到 openxiangda@2.31.1 并更新锁文件,先实现完整业务操作、授权和只读辅助查询,再按 docs/backend.md 声明 ai.agent。",
|
|
20
|
+
"在测试环境运行 check 与完整输入、缺失输入、同名对象、权限不足和重复提交场景;发布应用版本后核对 AI Catalog 摘要与当前环境 Head。",
|
|
21
|
+
"平台服务端须部署支持显式业务任务发现的版本;npm 包发布本身不会升级平台或自动公开旧应用操作。",
|
|
22
|
+
"并发前端接入按 docs/managed-concurrency-frontend.md 选择模式,先核对已导出的 API 与目标平台能力。"
|
|
23
|
+
],
|
|
24
|
+
"knownLimitations": [
|
|
25
|
+
"本次包仅增加业务任务元数据与编译校验;交互卡片资源、持久 Interaction 和 MCP Apps Host 由后续平台与应用批次交付。",
|
|
26
|
+
"现有独立 MCP Facade 的写入预览与确认协议保持原样;平台自有 Agent 的自动写入由平台执行层和权限边界负责。",
|
|
27
|
+
"旧应用的底层 CRUD 不会自动转换为普通用户任务,需应用维护者完成业务语义与辅助能力接入。"
|
|
28
|
+
],
|
|
29
|
+
"issues": [],
|
|
30
|
+
"compatibility": {
|
|
31
|
+
"node": ">=24",
|
|
32
|
+
"workspaceGenerations": "v2",
|
|
33
|
+
"platform": "平台业务 Agent 任务发现需配套部署支持 ai.agent 元数据的服务端版本。",
|
|
34
|
+
"v1": "独立 V1 引擎和工作区不受影响。"
|
|
35
|
+
},
|
|
36
|
+
"sha256": "37c61ac4c9b17a8ca688cc9e1f38f4ed106754a7342e1a6aa9b2095e954a5118",
|
|
37
|
+
"url": "https://github.com/1377385356/openxiangda/releases/tag/v2.31.1"
|
|
38
|
+
}
|
package/skills/manifest.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
{
|
|
5
5
|
"name": "openxiangda-v2",
|
|
6
6
|
"description": "使用 OpenXiangda 2.0 从模糊业务想法、已有资料或具体变更出发,通过对话发现模块、完成详细产品设计,由当前 AI Agent 按需用 Image 2.5 等图片能力形成视觉参考,直接实现真实页面并在浏览器修正,再检查和交付应用;维护 1.x 应用时使用对应的 1.x 技能。",
|
|
7
|
-
"sha256": "
|
|
7
|
+
"sha256": "a53fab63a4033b8dca5de456f17d7cc55542f95bfb14460e551377649d04ca18"
|
|
8
8
|
}
|
|
9
9
|
]
|
|
10
10
|
}
|
|
@@ -40,10 +40,10 @@ AI 接到新应用、页面或改版任务时,在同一个 OpenXiangda 工作
|
|
|
40
40
|
未创建工作区时使用本 Skill 随根包发布的精确版本:
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
|
-
pnpm dlx openxiangda@2.31.
|
|
44
|
-
pnpm dlx openxiangda@2.31.
|
|
45
|
-
pnpm dlx openxiangda@2.31.
|
|
46
|
-
pnpm dlx openxiangda@2.31.
|
|
43
|
+
pnpm dlx openxiangda@2.31.1 auth status --cwd <应用目录> --base-url <平台地址> --json
|
|
44
|
+
pnpm dlx openxiangda@2.31.1 login --cwd <应用目录> --base-url <平台地址>
|
|
45
|
+
pnpm dlx openxiangda@2.31.1 create <应用目录> --base-url <同一平台地址>
|
|
46
|
+
pnpm dlx openxiangda@2.31.1 skill install --force
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
创建前把产品要求的目标平台明确带入命令,不从旧登录态推断站点。已有工作区从原绑定恢复,平台不一致时先解决登录与目标,不改 link 文件跨站创建。
|
|
@@ -71,8 +71,10 @@ pnpm dlx openxiangda@2.31.0 skill install --force
|
|
|
71
71
|
| 外部无账号表单、续填、上传、本人记录 | [匿名公开访问](references/public-access.md) |
|
|
72
72
|
| 审批、待办、消息或事件 | [工作流与通知](references/workflow-events.md) |
|
|
73
73
|
| 自定义事务、校验或外部集成 | [按需后端](references/backend.md) |
|
|
74
|
+
| 向平台业务 Agent 开放任务、辅助查询和字段绑定 | [Agent 任务接入](references/backend.md#面向普通用户的-agent-任务);先完成业务操作、权限和规则,再声明 `ai.agent` |
|
|
74
75
|
| 主子额度、审批占用释放、撤回修订重提 | [精确金额占用](references/decimal-reservations.md) |
|
|
75
76
|
| 热点读取、抢票、排队、名额预占与原结果恢复 | [缓存、排队与配额](references/managed-concurrency.md) |
|
|
77
|
+
| 报名按钮、表单排队、短确认页、预占倒计时与前端恢复 | [并发前端接入](references/managed-concurrency-frontend.md):按六种形式选择;仅使用已导出的 API,区分待实现封装 |
|
|
76
78
|
| 管理成员或查看有效流程参数 | [应用管理](references/administration.md) |
|
|
77
79
|
| 检查、部署、生产发布与故障恢复 | [验收](references/testing.md)、[交付](references/delivery.md) |
|
|
78
80
|
| 需求记录与版本升级 | [全流程记录](references/appspec.md)、[升级](references/upgrading.md) |
|
|
@@ -249,6 +249,50 @@ operations: [{
|
|
|
249
249
|
引用 1–16 个已声明资源;`sideEffects` 最多 20 条——只读必须为零,写操作至少一条具体副作用;
|
|
250
250
|
`concurrency` 可选 `none | revision`,`timeoutMs` 限 100–30000。
|
|
251
251
|
|
|
252
|
+
### 面向普通用户的 Agent 任务
|
|
253
|
+
|
|
254
|
+
`ai` 使操作进入技术能力目录;只有显式声明 `ai.agent` 才把操作交给普通用户的业务 Agent 发现。`visibility: 'task'` 是可办理的任务,`visibility: 'support'` 是该任务需要的只读查询。平台仍按当前用户的应用权限过滤,声明本身不授予权限。生成的资源 CRUD、没有 `agent` 的操作和 `support` 操作都不是普通用户首页的独立任务。
|
|
255
|
+
|
|
256
|
+
以下示例中,应用自己负责地点搜索与预约规则。给模型和用户的是“地点名称”,稳定地点引用只作为操作参数,用户无需填写地点编码。
|
|
257
|
+
|
|
258
|
+
```ts
|
|
259
|
+
const locations = {
|
|
260
|
+
code: 'reservation.locations', method: 'GET', path: '/api/reservations/locations',
|
|
261
|
+
capability: 'app:visitor-app:reservation:locations',
|
|
262
|
+
requestSchema: { type: 'object', properties: { keyword: { type: 'string' } } },
|
|
263
|
+
responseSchema: { type: 'object' },
|
|
264
|
+
ai: {
|
|
265
|
+
name: '查找可预约地点', description: '按名称搜索当前用户可选择的地点',
|
|
266
|
+
risk: 'read', resources: ['reservations'], sideEffects: [],
|
|
267
|
+
agent: { visibility: 'support' },
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
const enroll = {
|
|
271
|
+
code: 'reservation.enroll', method: 'POST', path: '/api/reservations/enroll',
|
|
272
|
+
capability: 'app:visitor-app:reservation:enroll',
|
|
273
|
+
requestSchema: {
|
|
274
|
+
type: 'object', required: ['location'],
|
|
275
|
+
properties: { location: { type: 'string' } },
|
|
276
|
+
},
|
|
277
|
+
responseSchema: { type: 'object' },
|
|
278
|
+
ai: {
|
|
279
|
+
name: '发起访客预约', description: '校验访客信息、地点与时间后创建预约',
|
|
280
|
+
risk: 'write', resources: ['reservations'], sideEffects: ['创建访客预约'],
|
|
281
|
+
agent: {
|
|
282
|
+
visibility: 'task',
|
|
283
|
+
examples: ['帮我预约访客', '明天下午邀请张老师来访'],
|
|
284
|
+
aliases: ['发起邀约'],
|
|
285
|
+
supportOperations: ['reservation.locations'],
|
|
286
|
+
inputLookups: { location: 'reservation.locations' },
|
|
287
|
+
},
|
|
288
|
+
},
|
|
289
|
+
};
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
两个 `capability` 必须在 `authz.capabilities` 中声明为 `kind: 'backend'`,再授予相应角色。`supportOperations` 和 `inputLookups` 引用同一应用中的操作 code;被引用操作必须声明 `visibility: 'support'`、`risk: 'read'` 且使用 GET。`inputLookups` 的键必须存在于任务的 `requestSchema.properties`。示例问法最多 12 条、别名最多 20 条、辅助操作最多 12 个、字段绑定最多 32 个;运行时辅助查询仍应分页、有界,并返回业务标签与稳定引用。
|
|
293
|
+
|
|
294
|
+
业务规则必须在后端操作中验证;提示词、卡片预填和前端校验不能取代权限与业务校验。完整输入、缺失输入、同名地点、权限不足、重复提交和结果未知都应有应用测试与回执核对。任务声明随 AppVersion 的 AI Catalog 一起发布,不能另建手工目录。交互卡片与自动执行仅在平台 Agent Host 支持后启用;本声明本身不改变现有 MCP Facade 的写入确认协议。
|
|
295
|
+
|
|
252
296
|
宿主(平台 AI 网关)用该目录装配 MCP Facade,应用不自己实现协议:
|
|
253
297
|
|
|
254
298
|
- **单应用 Facade**(`createApplicationAiMcpServer`):每个能力一个工具,只读能力直接以
|
|
@@ -55,6 +55,8 @@
|
|
|
55
55
|
| --- | --- |
|
|
56
56
|
| 写操作的 `ai.sideEffects` 至少一条具体副作用 | `ai: { name: '受理派单', ..., risk: 'write', sideEffects: ['更新报修单状态为处理中', '写入一条派工记录'] }` |
|
|
57
57
|
| GET 操作的 `ai.risk` 只能是 `read`;写操作不能是 `read` | `risk: 'read'` ↔ `method: 'GET'` |
|
|
58
|
+
| 面向普通用户的 Agent 只发现显式声明的业务任务;底层 CRUD 不会自动成为任务 | `ai.agent: { visibility: 'task', examples: ['帮我发起报修'], aliases: ['提交报修'] }` |
|
|
59
|
+
| 任务输入需要地点、人员等业务选择时,绑定同应用的只读辅助操作,用户不填内部编码 | `agent: { visibility: 'task', supportOperations: ['repair.locations'], inputLookups: { location: 'repair.locations' } }`;被引用的 GET 操作须有 `agent: { visibility: 'support' }`,`location` 须是请求 Schema 字段 |
|
|
58
60
|
| controller 路由必须绑定 `@OpenXiangdaOperation(appOperations.<code>)`,普通 CRUD 不写 controller | check 门禁会拒绝未绑定路由 |
|
|
59
61
|
| 事务守卫 `errorCode` 必须匹配 `^OPENXIANGDA_[A-Z0-9_]{1,96}$` | `errorCode: 'OPENXIANGDA_REPAIR_REQUEST_NOT_PENDING'` |
|
|
60
62
|
|
|
@@ -47,6 +47,12 @@ Agent 可以在标准后台内部优化布局、视觉和交互,但后台仍
|
|
|
47
47
|
|
|
48
48
|
在页面设计或实现说明中写清选用组件、理由与必要边界。已有需求内的技术选型由开发者完成,无需让用户逐个指定依赖。ECharts 的按需导入方式见[官方说明](https://echarts.apache.org/handbook/en/basics/import/);后台无需为图表增加手机适配,用户移动图表只按已确认需求处理。
|
|
49
49
|
|
|
50
|
+
## 热点读取与限量提交 {#managed-concurrency}
|
|
51
|
+
|
|
52
|
+
报名、预约、抢票和限量申领按[并发能力的前端接入](managed-concurrency-frontend.md)选择页面形式:详情使用命名缓存读取,关键动作使用受管命令,按钮与状态面板共享同一操作快照。保持应用现有页面归属与组件体系;身份、排队、事务和配额由平台负责。
|
|
53
|
+
|
|
54
|
+
先填写再提交、放行后短确认和预占后确认具有不同的输入与有效期语义;不要把通用表单 `onSubmit` 或整页 JSX 包装器当作自动防超卖方案。当前可用 API、代码示例、恢复文案及待补齐的契约均在该专题说明。
|
|
55
|
+
|
|
50
56
|
## 数据和权限
|
|
51
57
|
|
|
52
58
|
`modules/` 中的业务模型由编译器派生 DataResource 契约,`openxiangda.config.ts` 声明页面
|
|
@@ -68,10 +68,10 @@ MCP 服务随项目根包一起安装,AI 客户端的 stdio 连接仍需配置
|
|
|
68
68
|
以下命令的版本占位符由随包资料替换为该根包的精确版本。网站源码阅读者应先确认要使用的发行版本。
|
|
69
69
|
|
|
70
70
|
```bash
|
|
71
|
-
pnpm dlx openxiangda@2.31.
|
|
72
|
-
pnpm dlx openxiangda@2.31.
|
|
73
|
-
pnpm dlx openxiangda@2.31.
|
|
74
|
-
pnpm dlx openxiangda@2.31.
|
|
71
|
+
pnpm dlx openxiangda@2.31.1 skill install --force
|
|
72
|
+
pnpm dlx openxiangda@2.31.1 auth status --base-url <平台地址> --json
|
|
73
|
+
pnpm dlx openxiangda@2.31.1 login --cwd my-app --base-url https://platform.example.com
|
|
74
|
+
pnpm dlx openxiangda@2.31.1 create my-app --base-url https://platform.example.com
|
|
75
75
|
cd my-app
|
|
76
76
|
pnpm openxiangda context --json
|
|
77
77
|
pnpm openxiangda dev
|
|
@@ -174,9 +174,9 @@ MCP 的 `docs_read` 可以读取本说明,当前没有独立的源码操作 MC
|
|
|
174
174
|
无需本地工作区,使用本 Skill 随包精确版本或已安装的对应 CLI:
|
|
175
175
|
|
|
176
176
|
```bash
|
|
177
|
-
pnpm dlx openxiangda@2.31.
|
|
178
|
-
pnpm dlx openxiangda@2.31.
|
|
179
|
-
pnpm dlx openxiangda@2.31.
|
|
177
|
+
pnpm dlx openxiangda@2.31.1 auth status --base-url <平台> --json
|
|
178
|
+
pnpm dlx openxiangda@2.31.1 source resolve <仓库URL> --base-url <平台> --json
|
|
179
|
+
pnpm dlx openxiangda@2.31.1 source clone <仓库URL> <新目录> --base-url <平台> --json
|
|
180
180
|
```
|
|
181
181
|
|
|
182
182
|
登录缺失或站点不匹配时,先按该平台执行 login。resolve 根据平台已经登记的绑定返回
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# 并发能力的前端接入
|
|
2
|
+
|
|
3
|
+
适用于活动报名、限量申领、抢票和预约。本文使用 `openxiangda@2.31.0` 引入的公开 API;目标平台必须支持 `data.managed-concurrency@1.0.0`。先按[缓存、排队与整数配额](managed-concurrency.md)声明读取、命令、配额和权限,再选择页面接入形式。
|
|
4
|
+
|
|
5
|
+
推荐体验是正常浏览内容,在用户明确提交时按需等待,并持续核对同一次申请的结果。低负载时可以很快完成,不需要人为添加等待页。
|
|
6
|
+
|
|
7
|
+
## 选择接入形式 {#patterns}
|
|
8
|
+
|
|
9
|
+
| 形式 | 适用场景 | 用户流程 | 当前接入方式 |
|
|
10
|
+
| --- | --- | --- | --- |
|
|
11
|
+
| 热点内容直接浏览 | 详情、场次、活动说明 | 浏览 → 按需刷新 | `client.read`,应用处理读取状态 |
|
|
12
|
+
| 按钮发起、原地等待 | 一键报名、限量申领 | 点击 → 排队 → 处理 → 结果 | `useManagedCommand` + `ManagedCommandStatus` |
|
|
13
|
+
| 先填写、再排队提交 | 有选项的预约和申请 | 填写校验 → 确认内容 → 排队提交 | 有界参数组合现有 hook;复杂资料需要额外冻结契约 |
|
|
14
|
+
| 放行后短确认 | 输入已确定、加载成本较高的确认步骤 | 进入 → 等待 → 短确认 → 提交 | `autoAccept: false` + `ManagedCommandGate` + `submit()` |
|
|
15
|
+
| 预占后确认 | 暂留名额、时段预约 | 预占 → 核实有效期 → 确认或释放 | 应用声明预占/确认/释放命令,配合 `client.allocation` |
|
|
16
|
+
| 返回后恢复原申请 | 刷新、断网、查看本人结果 | 恢复原请求 → 核对 → 结果 | hook 挂载恢复,或 `client.result` 查询已知操作 |
|
|
17
|
+
|
|
18
|
+
常见报名页面可以组合前三种:详情走缓存,用户确认活动和选项后提交,按钮附近展开状态;成功后进入本人记录。PC 状态区、抽屉和移动端底部区域可以使用不同外观,共用同一个操作状态。
|
|
19
|
+
|
|
20
|
+
## 页面与平台的分工 {#ownership}
|
|
21
|
+
|
|
22
|
+
页面负责内容、选项、字段校验、业务文案和结果路由。平台负责当前用户身份、准入、幂等、事务、配额和权威结果。身份使用声明中的可信 `actor`,不使用用户填写的工号决定记录归属。
|
|
23
|
+
|
|
24
|
+
平台身份就绪后创建 `createManagedConcurrencyClient()`,并保持实例稳定。它绑定当前应用、环境和主体;身份/环境变化时重新挂载对应页面子树,不能复用旧客户端或旧视图。
|
|
25
|
+
|
|
26
|
+
一次操作只使用一个 `useManagedCommand`。按钮、状态条和弹层消费同一个快照,不要各自调用 hook 创建观察循环。当前 SDK 通过 `openxiangda/react` 和 `openxiangda/mobile` 导出相同的受管并发能力;普通字段仍复用[Field Kit](field-components.md),界面遵循[前端组件与页面归属](frontend.md)。
|
|
27
|
+
|
|
28
|
+
不要把受管模型继续绑定到普通 CRUD 提交按钮,也不要传入任意 `async onSubmit` 并假设它会自动获得排队和原子保证。最终动作必须是已发布的受管命令。
|
|
29
|
+
|
|
30
|
+
## 热点详情读取 {#cached-read}
|
|
31
|
+
|
|
32
|
+
以下示例对应源码 `examples/managed-concurrency/declaration.ts` 中的 `offer` 命名读取。`offerId` 必须是该应用真实资源的 UUID:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { createManagedConcurrencyClient } from 'openxiangda/react';
|
|
36
|
+
|
|
37
|
+
type Client = ReturnType<typeof createManagedConcurrencyClient>;
|
|
38
|
+
type Offer = { id: string; title: string; closesAt: string };
|
|
39
|
+
|
|
40
|
+
export async function readOffer(
|
|
41
|
+
client: Client,
|
|
42
|
+
offerId: string,
|
|
43
|
+
signal?: AbortSignal,
|
|
44
|
+
) {
|
|
45
|
+
return client.read<Offer>('offer', { id: offerId }, signal);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
将读取接入应用已有的异步查询状态:初次加载显示局部骨架,空数组显示无可展示内容,失败提供局部重试。组件卸载或资源变化时中止旧请求,迟到响应不能覆盖新资源。同页多个观察者可复用应用已有查询层,查询键包含 `client.scope`、读取 code 和规范化参数,身份变化时清除旧主体视图;当前没有内置的 `useManagedRead` hook。
|
|
50
|
+
|
|
51
|
+
`freshness: 'stale'` 表示返回的是允许使用的旧内容,并不证明后台刷新已经成功。保留内容并提示“当前展示最近一次可用信息”;按新鲜期和有界退避刷新,超过 `staleUntil` 后不能继续无限展示。手动刷新仍调用同一命名读取,不绕回普通 CRUD。
|
|
52
|
+
|
|
53
|
+
详情与余量使用不同读取声明。页面倒计时本地显示,不能每秒重查活动;资格、开放时间和扣减仍由服务端决定。缓存余量不保证获票;允许补量或释放的业务,也不能因旧余量为零永久阻止新的有效申请。等待期间不要重复读取详情或表单。
|
|
54
|
+
|
|
55
|
+
## 一键申请与原结果恢复 {#one-click}
|
|
56
|
+
|
|
57
|
+
示例对应官方声明中的 `claim` 命令。父级在身份就绪后挂载,用户、环境或 `offerId` 变化时重新挂载本组件:
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
import { useMemo } from 'react';
|
|
61
|
+
import {
|
|
62
|
+
createManagedConcurrencyClient,
|
|
63
|
+
useManagedCommand,
|
|
64
|
+
ManagedCommandStatus,
|
|
65
|
+
} from 'openxiangda/react';
|
|
66
|
+
|
|
67
|
+
export function ClaimAction({ offerId }: { offerId: string }) {
|
|
68
|
+
const client = useMemo(() => createManagedConcurrencyClient(), []);
|
|
69
|
+
const command = useManagedCommand({
|
|
70
|
+
client,
|
|
71
|
+
command: 'claim',
|
|
72
|
+
input: { id: offerId },
|
|
73
|
+
storageKey: `claim:${offerId}`,
|
|
74
|
+
});
|
|
75
|
+
// start / resume 的失败进入快照;保留原请求,按错误原因恢复。
|
|
76
|
+
const observe = (action: () => Promise<void>) => {
|
|
77
|
+
void action().catch(() => {});
|
|
78
|
+
};
|
|
79
|
+
return (
|
|
80
|
+
<section>
|
|
81
|
+
<button disabled={command.state !== 'idle'}
|
|
82
|
+
onClick={() => observe(() => command.start())}>
|
|
83
|
+
申请
|
|
84
|
+
</button>
|
|
85
|
+
<ManagedCommandStatus snapshot={command} />
|
|
86
|
+
{command.state === 'error' && (
|
|
87
|
+
<div>
|
|
88
|
+
<p>暂时无法确认结果,请核对原申请。</p>
|
|
89
|
+
<button onClick={() => observe(() => command.resume())}>
|
|
90
|
+
核对原申请
|
|
91
|
+
</button>
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
{command.state === 'succeeded' && <p>可查看本次申请记录。</p>}
|
|
95
|
+
</section>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
挂载只恢复已有意图;用户点击 `start()` 才创建新意图。默认放行后自动受理,适合点击已经明确表达提交意愿的动作。不要在 `useEffect` 中调用 `start()` 自动报名,不要手写循环换请求键重试。
|
|
101
|
+
|
|
102
|
+
等待只轮询票据,受理后查询原命令结果;SDK 遵守建议间隔并退避。按钮禁用只是体验,服务端仍执行幂等和业务去重。成功时可从 `receipt.result.items` 定位已创建的业务记录;不要在成功回调里再次写订单、扣名额或发通知。导航和视图刷新应允许重复执行。
|
|
103
|
+
|
|
104
|
+
## 填写后再提交 {#form-submit}
|
|
105
|
+
|
|
106
|
+
先校验字段,向用户展示确认摘要,再将输入固定并启动命令。受管 hook 使用固定后的输入,不要直接绑定每次键入都变化的表单对象。提交后刷新页面,要从本次意图的稳定资料恢复相同输入,再挂载 hook;不能用空表单覆盖原请求。
|
|
107
|
+
|
|
108
|
+
排队后需要修改资料时,先请求取消并核实结果,再允许编辑和创建新意图。取消与受理、执行、成功可能竞争;服务端返回已经成功时,应展示原结果,不能继续当作草稿编辑或假定取消成功。组件卸载或关闭面板仅停止观察,不取消命令。
|
|
109
|
+
|
|
110
|
+
当前命令参数仅支持 UUID、枚举字符串和有限整数。场次、规格等可以按声明接入;配额 `units` 由命令声明固定,传入整数参数不会自动改变扣减数量。姓名、任意备注、附件及整份表单 JSON 不属于当前参数范围。
|
|
111
|
+
|
|
112
|
+
复杂资料需要先定义并验证“草稿 → 不可变资料版本 → 命令引用版本 ID”的契约,核实作者、目标资源、版本和字段权限,并阻止冻结资料被普通 CRUD 改写。只传 `draftId` 而允许原稿继续变化不满足要求。该通用冻结机制和表单桥接尚未提供,不能把任意表单保存接口包进 hook 就当作已支持;草稿保存本身也需要独立预算。
|
|
113
|
+
|
|
114
|
+
## 放行后的短确认 {#confirmation-gate}
|
|
115
|
+
|
|
116
|
+
此形式要求排队前已确定命令参数,用户明确请求进入确认步骤。沿用前例的身份/client 生命周期,创建命令时设置 `autoAccept: false`;入口按钮调用 `start()`,获准后的确认按钮调用 `submit()`:
|
|
117
|
+
|
|
118
|
+
```tsx
|
|
119
|
+
// command 已由 useManagedCommand 创建,并设置 autoAccept: false。
|
|
120
|
+
// Confirmation 是应用组件;它只核对已经固定的输入。
|
|
121
|
+
<ManagedCommandGate snapshot={command}>
|
|
122
|
+
{() => (
|
|
123
|
+
<Confirmation
|
|
124
|
+
onConfirm={() => void command.submit().catch(handleActionError)}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</ManagedCommandGate>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`ManagedCommandGate` 从与 hook 相同的公开入口导入。上例是前例中的组合片段,`Confirmation` 和 `handleActionError` 由应用定义;错误处理必须保留原请求,不能直接清除并重提。耗时请求和懒加载应在子树内发生,父组件提前发请求会失去保护。
|
|
131
|
+
|
|
132
|
+
`admitted` 是短期受理许可,不是名额预占,也不是长时间填写表单的许可证。当前 Gate 只按快照状态决定是否挂载,没有内建到期倒计时;应用按 `waiting.expiresAt` 提示并核实,最终提交时服务端校验许可,SDK 沿原请求恢复。
|
|
133
|
+
|
|
134
|
+
“先进入页面,再任意决定最终输入”需要与命令输入解耦的页面准入契约,当前 Gate 不提供这个语义。它在应用启动、身份就绪后工作,不能替代静态资源、登录、启动元数据和整个站点入口的保护。
|
|
135
|
+
|
|
136
|
+
## 预占与最终确认 {#reservation}
|
|
137
|
+
|
|
138
|
+
应用先声明 `quota.action: 'allocate'`、`mode: 'reserved'` 和 `reservationSeconds`,另声明确认/释放命令。三个动作分别使用稳定恢复键,并按[配额生命周期](managed-concurrency.md#配额和发布边界)绑定原分配 ID。
|
|
139
|
+
|
|
140
|
+
预占命令 `succeeded` 只证明预占事务曾成功。先读取 `receipt.result.allocation.id`,再调用 `client.allocation(allocationId)` 核实当前状态,界面显示“已暂留名额”。确认或释放后也要核对当前分配,不能用旧成功回执证明仍持有名额。
|
|
141
|
+
|
|
142
|
+
进入页面、回到前台和临近到期时有界核实。`expiresAt` 用于展示估算倒计时;当前回执没有 `serverNow`,浏览器时间不能作为最终过期依据。归零显示“正在核实有效期”,确认和到期竞争以服务端结果为准。关闭窗口不自动释放。
|
|
143
|
+
|
|
144
|
+
同池/主体永久去重;释放后不会自动允许同一主体重申同一池。需要下一轮申请时使用新的权威资源/池,不删除分配记录或换请求键绕过限制。
|
|
145
|
+
|
|
146
|
+
## 状态文案与用户动作 {#states}
|
|
147
|
+
|
|
148
|
+
| 状态 | 界面含义 | 动作规则 |
|
|
149
|
+
| --- | --- | --- |
|
|
150
|
+
| `idle` | 可以申请 | 明确点击后 `start()` |
|
|
151
|
+
| `waiting` | 正在等待进入,尚未获得名额 | 等待;可申请退出排队 |
|
|
152
|
+
| `admitted` | 已轮到您,可以确认 | 手动确认形式调用 `submit()` |
|
|
153
|
+
| `accepted` / `executing` / `retry_wait` | 已收到申请,正在处理 | 查询原结果;按服务端规则申请取消 |
|
|
154
|
+
| `recovering` | 正在核对原申请 | 保留原键,继续退避观察 |
|
|
155
|
+
| `error` | 暂时无法确认或无法继续 | 按原因恢复登录、处理存储或 `resume()`;不统一显示报名失败 |
|
|
156
|
+
| `succeeded` | 权威事务已提交 | 按业务显示报名成功或暂留名额;预占另核实分配 |
|
|
157
|
+
| `rejected` | 明确未通过 | 区分售罄、资格、截止等业务原因 |
|
|
158
|
+
| `expired` / `cancelled` | 等待或命令到期、已取消 | 区分票据与命令终态,明确结束后才 `clear()` |
|
|
159
|
+
|
|
160
|
+
排队位置只在 `waiting.position` 存在时显示,不换算成保证等待时长或保证名额;缺少位置时显示等待文案,不画虚假百分比。临时队列故障后位置可能变化。
|
|
161
|
+
|
|
162
|
+
`ManagedCommandStatus` 支持 `errorMessages` 映射回执错误。产品层同时检查 `snapshot.errorCode` 与 `receipt.errorCode`,补齐身份变化、存储不可用、恢复输入冲突等原因。用户文案不暴露数据库、锁或票据内部信息。
|
|
163
|
+
|
|
164
|
+
`cancel()` 的 Promise 失败也要明确展示“取消结果尚未确认”,保留原意图并恢复查询;不要吞掉取消错误后宣布取消成功。关闭状态面板、离开页面与取消申请使用不同按钮和文案。状态变化使用无障碍提示,抽屉/底部区域遵循现有组件的焦点管理。
|
|
165
|
+
|
|
166
|
+
## 刷新、断网与我的申请 {#recovery}
|
|
167
|
+
|
|
168
|
+
默认恢复信息保存在 `sessionStorage`,按应用、环境、主体、命令和 `storageKey` 隔离。同标签页刷新可以恢复;`storageKey` 必须稳定,不能在每次渲染时随机生成。存储不可用时明确停止或处理,不能悄悄改用易丢失的内存继续提交。
|
|
169
|
+
|
|
170
|
+
已知 `operationId` 或原 `requestKey` 时,可以用 `client.result` 查询原结果。已成功业务记录可通过本人权限下的普通数据查询展示。当前没有“列出我的所有进行中命令”的接口;关闭标签后找回、跨设备继续和完整处理中列表需要额外的受管操作索引,不能把浏览器存储当作全平台事实。
|
|
171
|
+
|
|
172
|
+
不要把超时解释为提交失败,也不要在结果未知时 `clear()`。重试、恢复与返回入口都应该指向原意图。配额永久去重与界面请求键是不同边界:清除一个已终结的界面状态不代表可以再次获得同一池名额。
|
|
173
|
+
|
|
174
|
+
## 当前组件与后续封装边界 {#available-components}
|
|
175
|
+
|
|
176
|
+
当前可用:`createManagedConcurrencyClient`、`useManagedCommand`、`ManagedCommandStatus`、`ManagedCommandGate`,以及客户端的读取、结果、取消和分配状态方法。
|
|
177
|
+
|
|
178
|
+
`useManagedRead`、`ManagedCommandButton`、`ManagedCommandPanel`、`useManagedFormSubmission`、`useManagedAllocation`、`AllocationStatus` 是建议的后续封装名称,当前不是公开导出。不要照这些名称编写 import。应用现在可以使用已有 hook 与平台普通 UI 组件组合界面,后续标准封装仍应复用同一状态机和传输。
|
|
179
|
+
|
|
180
|
+
复杂资料冻结、跨设备命令索引、独立页面准入及精确倒计时需要相应的平台契约,不能仅通过前端外观补齐。SDK 版本、平台部署和实际业务容量需要分别确认。
|
|
181
|
+
|
|
182
|
+
## 接入验收 {#acceptance}
|
|
183
|
+
|
|
184
|
+
| 操作 | 应观察到的结果 |
|
|
185
|
+
| --- | --- |
|
|
186
|
+
| 打开详情或重渲染 | 不自动报名;详情与余量按声明读取,不因排队反复查表 |
|
|
187
|
+
| 连点、刷新、提交响应丢失 | 沿用原意图,恢复原结果,不生成重复业务记录 |
|
|
188
|
+
| 排队时编辑资料 | 固定原输入,取消尚未核实前不切换到另一次提交 |
|
|
189
|
+
| 关闭状态区后返回 | 继续查看原操作,不宣称已取消 |
|
|
190
|
+
| 取消与受理/成功同时发生 | 显示服务端原结果;错误时保留待核实状态 |
|
|
191
|
+
| 身份切换、存储被禁用 | 不串用旧主体资料,不静默丢弃恢复能力 |
|
|
192
|
+
| 网络或依赖异常 | 退避观察,保留原键,不绕回数据库或快速换键重提 |
|
|
193
|
+
| 许可到期、预占到期 | 分别核实准入与分配,互不冒充 |
|
|
194
|
+
| 键盘、屏幕阅读器、窄屏 | 状态可读,焦点可控,关闭与取消语义清楚 |
|
|
195
|
+
|
|
196
|
+
这些检查与目标环境的吞吐、P95/P99、授权查询成本和故障恢复验证一起完成;页面演示和 SDK 单元测试不能代替真实业务验收。
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
`data.concurrency` 是可选的平台声明,用于限量申领、抢票和预约。平台负责缓存回源、等待资格、持久受理、受控 Native 事务及整数配额;应用通过 `openxiangda/react` 或 `openxiangda/mobile` 调用。平台必须声明能力 `data.managed-concurrency@1.0.0`。旧平台会在编译/发布能力检查阶段拒绝此声明。
|
|
4
4
|
|
|
5
|
+
页面开发参见[并发能力的前端接入](managed-concurrency-frontend.md):按热点详情、一键申请、填完再提交、短确认入口、预占确认和原结果恢复选择交互方式,并核对当前 API 与尚未提供的封装边界。
|
|
6
|
+
|
|
5
7
|
## 声明与权限
|
|
6
8
|
|
|
7
9
|
完整且由双编译器测试验证的示例位于源码 `examples/managed-concurrency/declaration.ts`。调用 `defineOpenXiangdaApp(concurrencyExample())` 即可编译。把示例中的 `data.concurrency` 合入应用配置,并按实际业务配置模型和参与权限。
|