@xtalpi/agentic-lab-skills 0.0.7 → 0.0.8
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/package.json +1 -1
- package/skills/lab-flow-designer/SKILL.md +47 -6
- package/skills/lab-flow-designer/embedded-template/scripts//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.js +4 -17
- package/skills/lab-flow-designer/embedded-template/valves//347/244/272/344/276/213/346/225/260/346/215/256/344/270/216/346/240/241/351/252/214/351/227/250/346/216/247.md +3 -4
- package/skills/lab-flow-designer/references/agentic-lab-sdk.md +77 -244
- package/skills/lab-flow-designer/references/skill-package-layout.md +1 -1
- package/skills/lab-flow-designer/references//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/346/240/207/345/207/206.md +1 -1
- package/skills/lab-flow-designer/templates//344/270/232/345/212/241/346/265/201/347/250/213/346/226/207/346/241/243/347/244/272/344/276/213.md +2 -2
- package/skills/lab-flow-designer/testing/test-processer.mjs +37 -44
package/package.json
CHANGED
|
@@ -161,7 +161,7 @@ metadata:
|
|
|
161
161
|
|
|
162
162
|
- **`### 数据池(Pool)`**:说明 Pool 存记录、每池有独立 Schema、随阶段变化。
|
|
163
163
|
- **`### 门控(Valve)`**:说明每个 Valve 对应脚本、`Processer` 的 `start` / `complete` 职责分工。
|
|
164
|
-
另起简短列表说明执行引擎通过 `this.context` 暴露的能力,**仅写** [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 中已出现项(如 `ticket.list` / `update` / `append`、`pool.getNext`、`process.list`、`process.execute`、`station.list`、`compound.
|
|
164
|
+
另起简短列表说明执行引擎通过 `this.context` 暴露的能力,**仅写** [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 中已出现项(如 `ticket.list` / `update` / `append`、`pool.getNext`、`process.list`、`process.execute`、`station.list`、`compound.getStockFromXfcSh`),**不要**抄写模板里已过期的成员名。
|
|
165
165
|
|
|
166
166
|
#### 3.4 `## 流程图`(本节内须含「连接关系」列表,与模板一致)
|
|
167
167
|
|
|
@@ -242,7 +242,7 @@ metadata:
|
|
|
242
242
|
- **前置处理**(对应 `start`):提取配置项(如 `bookid`)、规则摘要表,说明 `start` 函数需执行的逻辑。
|
|
243
243
|
- **人工处理**:界面形态与数据绑定描述(仅供参考,不由门控脚本实现)。
|
|
244
244
|
- **后置处理**(对应 `complete`):规则摘要表、提交参数结构(如 `process.execute` 的 `items` 定义),说明 `complete` 函数需执行的逻辑。
|
|
245
|
-
-
|
|
245
|
+
- **化合物数据查询方式**(或等价标题):表或段落中「查化合物库存」等表述,与 [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 中 `compound.getStockFromXfcSh` 的对应关系写清。
|
|
246
246
|
- **字段映射表**:「门控加工数据 ↔ 库字段 / API 字段」;脚本写入 `ticket.detail` 的键须与 **pools Schema「字段」列(snake_case)** 一致。
|
|
247
247
|
- **数据处理规则**表:序号(如 1.1、1.2)须在脚本注释中可逐条追溯。
|
|
248
248
|
|
|
@@ -274,14 +274,16 @@ metadata:
|
|
|
274
274
|
|------|----------------|
|
|
275
275
|
| 门控 YAML `input` | `start` 中 `ticket.list({ pool_id__in, limit, offset })` 的 `pool_id__in`(入口池 ID 数组;`limit`/`offset` 遵守 §2);**返回** `TicketListResponse`,业务数据取 **`response.list`**;含 `secondary` 时合并多个入口池 ID 后再传入 `pool_id__in` |
|
|
276
276
|
| 门控 YAML `output` | `complete` 中 `await this.context.pool.getNext(valve_id)` 后按 `pool.name` 与条件分支筛选目标池 |
|
|
277
|
-
| **化合物数据查询方式** | `compound.
|
|
277
|
+
| **化合物数据查询方式** | `compound.getStockFromXfcSh`:入参为可选 **`filter`**(**dot-notation** 格式,如 `{ 'cas.$in': [...] }`,不传默认筛选 `status = '可用'`)+ 可选 `fields`/`pageSize`/`page`;返回 `{ data, meta }` 结构,取 **`.data`** 获取数据数组;见 [agentic-lab-sdk](references/agentic-lab-sdk.md) |
|
|
278
278
|
| **字段映射表** | 将 API 返回字段写入 `ticket.detail` 的 snake_case 键 |
|
|
279
279
|
| **数据处理规则**(序号表) | `start` 内计算与回填,或 `complete` 内最终路由前校验;关键分支写 `// 规则 1.x` 注释 |
|
|
280
280
|
| **门控脚本配置项**表 | 脚本模块级常量:`PAGE_URL`(取 `PageUrl` 值)、`DEFAULT_QUERY_LIMIT`(取入口池查询上限,缺省 `999999`)、`STATION_BASE_URL`(若有);**各门控前置处理**配置中的 `bookid` → `BOOK_ID`(每个门控独立值) |
|
|
281
281
|
|
|
282
282
|
### 2. 分页与「查全量」(`limit` / `offset`)
|
|
283
283
|
|
|
284
|
-
凡 [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 中带 **`limit`** / **`offset`** 的查询(如 **`ticket.list`**、**`process.list
|
|
284
|
+
凡 [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 中带 **`limit`** / **`offset`** 的查询(如 **`ticket.list`**、**`process.list`**),生成门控脚本时遵守:
|
|
285
|
+
|
|
286
|
+
> **注意**:`compound.getStockFromXfcSh` 使用 `pageSize` / `page` 分页(非 `limit` / `offset`),不传分页参数时使用 API 默认值,不适用下述 `limit: 999999` 约定。
|
|
285
287
|
|
|
286
288
|
1. **流程说明未写分页**:未出现「每页/只查前/offset/limit/条数上限」等明确要求时,**默认**显式传入 **`limit: 999999`**、**`offset: 0`**(可用模块级常量如 `DEFAULT_QUERY_LIMIT`),避免依赖引擎默认小页长导致**截断漏数据**。**例外**:若流程文档**门控脚本配置项**表中写明了入口池查询上限(如 `门控脚本对入口池单次查询上限: 9000`),以该值作为 `DEFAULT_QUERY_LIMIT`,不使用默认 `999999`。
|
|
287
289
|
2. **流程说明写了分页**:按文档给出的 `limit`、`offset`(或等价参数名)原样写入调用。
|
|
@@ -301,7 +303,7 @@ metadata:
|
|
|
301
303
|
- **`complete` 返回** `CompleteExecutionResult`:通过 `new_tickets`(`Record<string, any>[]`,可选)返回待写入出口池的工单,由引擎自动创建。
|
|
302
304
|
- **一级参数与返回值键名一律 snake_case**(如 `valve_id`、`pool_ids`、`ticket_ids`、`orbit_link`、`new_tickets`),**禁止** camelCase(如 ~~`ticketIds`~~、~~`poolIds`~~)。嵌套数据(如 `ticket.detail` 内容、`process.execute` 的 `params`/`items` 等 JSON 结构)保持流程文档或 API 原有格式,不受此约束。
|
|
303
305
|
|
|
304
|
-
- **`start(params)`**(对应**前置处理**):解构 **`valve_id`、`pool_ids`**(`Processer` 入参 snake_case)→ `ticket.list({ pool_id__in: pool_ids, … })`(§2 默认大 `limit`;**返回值**为 `TicketListResponse`,取 **`response.list`** 得 `Ticket[]`;筛选键名以 [agentic-lab-sdk.md](references/agentic-lab-sdk.md) `ListTicketParams` 为准)→ **仅当**流程说明前置处理中写明化合物查询 / 流程列表 / 工站等需求时,才分别调用 `compound.
|
|
306
|
+
- **`start(params)`**(对应**前置处理**):解构 **`valve_id`、`pool_ids`**(`Processer` 入参 snake_case)→ `ticket.list({ pool_id__in: pool_ids, … })`(§2 默认大 `limit`;**返回值**为 `TicketListResponse`,取 **`response.list`** 得 `Ticket[]`;筛选键名以 [agentic-lab-sdk.md](references/agentic-lab-sdk.md) `ListTicketParams` 为准)→ **仅当**流程说明前置处理中写明化合物查询 / 流程列表 / 工站等需求时,才分别调用 `compound.getStockFromXfcSh`(返回 `{ data, meta }`,取 `.data` 获取数组)、`process.list`、`station.list`(缺则**不调**)→ **仅按**数据处理规则与映射更新 `ticket.detail` 中**文档涉及的键** → `ticket.update`(若确有写回)→ 拼装 `orbit_link`(见下方步骤)→ 返回 `{ orbit_link, ticket_ids }` 。
|
|
305
307
|
|
|
306
308
|
**`orbit_link` 生成步骤**:
|
|
307
309
|
1. 从**门控脚本配置项**表提取 `PageUrl`(含 `{bookid}` 占位符),写为脚本模块级常量 `PAGE_URL`
|
|
@@ -310,6 +312,43 @@ metadata:
|
|
|
310
312
|
|
|
311
313
|
- **`complete(params)`**(对应**后置处理**):解构 **`valve_id`** 与 **`tickets`**(snake_case 入参)→ **优先使用 `params.tickets` 作为业务数据源**(引擎已传入完整 ticket 数据,无需再通过 `ticket.list` 重新查询;仅当流程说明后置处理中**明确要求**获取额外数据或最新状态时才按需调用 `ticket.list`)→ 按流程说明后置处理中的规则执行业务逻辑(准备提交参数、调用外部接口如 `process.execute`、判定成败等)→ **`await`** `this.context.pool.getNext(valve_id)` → 将入口池 tickets 更新为 `finished`(`ticket.update`)→ 按出口池条件映射 `new_tickets`(含 `flow_id`、`pool_id`、`order_id`、`detail`、`status: 'created'`、`parent_ticket_id`)→ 返回 `{ new_tickets }`(由引擎自动创建,**不**在脚本内调用 `ticket.append`)。
|
|
312
314
|
|
|
315
|
+
### 4. 运行环境约束(沙箱可用全局对象)
|
|
316
|
+
|
|
317
|
+
门控脚本在**沙箱**中执行,**仅**以下全局对象/函数可用:
|
|
318
|
+
|
|
319
|
+
| 类别 | 可用 |
|
|
320
|
+
|------|------|
|
|
321
|
+
| 基础类型 | `Array`, `Object`, `Map`, `Set`, `JSON`, `Math`, `Date`, `Number`, `String`, `Boolean`, `RegExp` |
|
|
322
|
+
| 错误类型 | `Error`, `TypeError`, `ReferenceError` |
|
|
323
|
+
| 数值函数 | `parseInt`, `parseFloat`, `isNaN`, `isFinite` |
|
|
324
|
+
| 编码函数 | `encodeURIComponent`, `decodeURIComponent`, `encodeURI`, `decodeURI` |
|
|
325
|
+
| 异步 | `Promise`, `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` |
|
|
326
|
+
| 常量 | `undefined`, `NaN`, `Infinity` |
|
|
327
|
+
| IO | `console`(`log`/`info`/`warn`/`error`/`debug`)、`fetch`(仅限 SDK 无法覆盖的外部调用) |
|
|
328
|
+
|
|
329
|
+
**不可用**(使用即抛 `ReferenceError`):
|
|
330
|
+
|
|
331
|
+
- `URLSearchParams`、`URL`、`Headers`、`Request`、`Response`、`FormData`、`Blob`、`Buffer`
|
|
332
|
+
- `TextEncoder`、`TextDecoder`
|
|
333
|
+
- `process`、`require`、`module`、`exports`、`__dirname`、`__filename`
|
|
334
|
+
- `window`、`document`、`globalThis`(浏览器/Node.js 专属)
|
|
335
|
+
|
|
336
|
+
**替代方案**:
|
|
337
|
+
|
|
338
|
+
1. **拼接 query string**——用模板字符串 + `encodeURIComponent`(不要用 `URLSearchParams`):
|
|
339
|
+
```js
|
|
340
|
+
// ✅ 正确
|
|
341
|
+
const qs = `view_id=${encodeURIComponent(viewId)}&page_size=500`;
|
|
342
|
+
const url = `${BASE}?${qs}`;
|
|
343
|
+
|
|
344
|
+
// ❌ 错误——URLSearchParams 不存在
|
|
345
|
+
const qs = new URLSearchParams({ view_id: viewId, page_size: '500' });
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
2. **外部 HTTP 调用**——优先使用 `this.context` SDK 方法(`ticket.list`、`compound.getStockFromXfcSh` 等)。仅当流程文档要求调用 SDK 未覆盖的第三方 API(如飞书/Lark)时,才使用 `fetch`;此时 URL 手动拼接,**不依赖** `URLSearchParams`。
|
|
349
|
+
|
|
350
|
+
3. **JSON 序列化**——`JSON.stringify` / `JSON.parse` 可用(`JSON` 在沙箱白名单中)。
|
|
351
|
+
|
|
313
352
|
### 5. 类型安全的字段访问(防止运行时 TypeError)
|
|
314
353
|
|
|
315
354
|
`ticket.detail` 的值为**任意 JSON 类型**(`string` / `number` / `boolean` / `array` / `object` / `null`)——即使 pool Schema 声明为 `text`,运行时也可能收到 `number` 或 `null`。**生成代码时须遵守**:
|
|
@@ -400,6 +439,8 @@ console.info(`[Processer] v${__ARTIFACT_VERSION__} (skill: ${__ARTIFACT_SKILL__}
|
|
|
400
439
|
- 脚本以 `Processer` 类结束,禁止 `return`/`module.exports`/`export`(见「生成流程 §6」)
|
|
401
440
|
- 按需生成,禁止冗余:代码和 Schema 仅覆盖流程文档明确写出的内容(见「编写指引 §0」)
|
|
402
441
|
- **类型安全**:禁止直接对 `ticket.detail` 值调用 `.trim()` / `.split()` 等字符串方法;必须先 `String()` 转换或 `typeof` 判断(见「编写指引 §5」)
|
|
442
|
+
- **运行环境**:门控脚本在沙箱执行,`URLSearchParams`/`URL`/`Buffer` 等 Web/Node API **不可用**;拼接 query string 须用模板字符串 + `encodeURIComponent`(见「编写指引 §4」)
|
|
443
|
+
- **外部 HTTP**:优先用 `this.context` SDK 方法;仅 SDK 未覆盖的第三方 API 才用 `fetch`,URL 手动拼接(见「编写指引 §4」)
|
|
403
444
|
- **产物版本**:SKILL.md frontmatter 须含 `metadata.version`;每个脚本须含 `__ARTIFACT_VERSION__` / `__ARTIFACT_SKILL__` 常量及 constructor `console.info`(见「编写指引 §7」)
|
|
404
445
|
- **脚本自测**:凡写入或修改 `scripts/*.js`,必须用 `testing/test-processer.mjs` 执行自测并自动修复,直到正常轮全 `pass`(见「脚本自测」)
|
|
405
446
|
|
|
@@ -414,7 +455,7 @@ console.info(`[Processer] v${__ARTIFACT_VERSION__} (skill: ${__ARTIFACT_SKILL__}
|
|
|
414
455
|
**流程来源**:若该包为 **初次生成** 产物,源流程文档应已通过 **「流程文档合规预检」**;复查可对照 [references/业务流程文档标准.md](references/业务流程文档标准.md) §4。**迭代修改**路径无此强制要求。
|
|
415
456
|
**结构清单**:根目录 `SKILL.md`(合法 `name`/`description`、固定二级标题:`概述`、`核心概念`、`流程图`、`节点清单`、`门控执行规范`、`使用方式`)、`**连接关系:**`、`### Pool 节点` / `### Valve 节点`、`Processer` 类与门控执行规范代码块、存在 `pools/` / `valves/` / `scripts/`、`valves` 与 `scripts` 同名成对、任取一个 `pools/*.md` 的 Schema 表头含 **字段** / **字段标题** / **属性** 且数据行「字段」列为 snake_case。
|
|
416
457
|
|
|
417
|
-
**人工抽查**:生成脚本里 `ticket.list`、`process.list
|
|
458
|
+
**人工抽查**:生成脚本里 `ticket.list`、`process.list` 在无流程分页说明时是否传入 **`limit: 999999`**(或流程说明指定的查全量数值),见 [references/agentic-lab-sdk.md](references/agentic-lab-sdk.md) 篇首约定。`compound.getStockFromXfcSh` 使用 `pageSize`/`page` 分页,不传时使用 API 默认值。
|
|
418
459
|
|
|
419
460
|
**版本追踪**:`SKILL.md` frontmatter 含 `metadata.version` / `metadata.generated_by` / `metadata.generated_at`;每个 `scripts/*.js` 含 `__ARTIFACT_VERSION__` 常量(值与 `metadata.version` 一致)和 `__ARTIFACT_SKILL__` 常量;`Processer` constructor 含 `console.info` 版本打印。
|
|
420
461
|
|
|
@@ -55,20 +55,13 @@ class Processer {
|
|
|
55
55
|
),
|
|
56
56
|
];
|
|
57
57
|
|
|
58
|
-
let chemicalRecords = [];
|
|
59
58
|
let stockRecords = [];
|
|
60
59
|
if (compoundIdList.length > 0) {
|
|
61
|
-
const filter = {
|
|
62
|
-
|
|
60
|
+
const filter = { 'cas.$in': compoundIdList };
|
|
61
|
+
const stockResponse = await this.context.compound.getStockFromXfcSh({
|
|
63
62
|
filter,
|
|
64
|
-
limit: DEFAULT_QUERY_LIMIT,
|
|
65
|
-
offset: 0,
|
|
66
|
-
});
|
|
67
|
-
stockRecords = await this.context.compound.getStock({
|
|
68
|
-
filter,
|
|
69
|
-
limit: DEFAULT_QUERY_LIMIT,
|
|
70
|
-
offset: 0,
|
|
71
63
|
});
|
|
64
|
+
stockRecords = stockResponse.data || [];
|
|
72
65
|
}
|
|
73
66
|
|
|
74
67
|
const stations = await this.context.station.list();
|
|
@@ -87,14 +80,8 @@ class Processer {
|
|
|
87
80
|
// --- 规则 1.1~1.4:按 valves/示例数据与校验门控.md「数据处理规则」写回 detail(以下为示意)---
|
|
88
81
|
for (const ticket of tickets) {
|
|
89
82
|
const d = ticket.detail || {};
|
|
90
|
-
const
|
|
91
|
-
const stock = stockRecords.filter((r) => r.cmpd_id === d.cmpd_id);
|
|
83
|
+
const stock = stockRecords.filter((r) => str(r.cas) === str(d.cmpd_id));
|
|
92
84
|
|
|
93
|
-
if (chem) {
|
|
94
|
-
d.chemist_property = str(chem.chemist_property);
|
|
95
|
-
d.dispensable = chem.dispensable;
|
|
96
|
-
d.dispensing_remark = str(chem.dispensing_remark);
|
|
97
|
-
}
|
|
98
85
|
if (stock.length) {
|
|
99
86
|
d.available_amount_mg = stock.reduce(
|
|
100
87
|
(s, r) => s + num(r.latest_amount_mg, 0),
|
|
@@ -29,8 +29,7 @@ output:
|
|
|
29
29
|
|
|
30
30
|
| 流程说明中的表述 | 推荐 SDK 调用 |
|
|
31
31
|
|----------------|---------------|
|
|
32
|
-
|
|
|
33
|
-
| 查 chemical_record / 知识库属性 | `context.compound.getChemicalRecord({ filter, limit, offset })`;同上,默认大 `limit` 防截断 |
|
|
32
|
+
| 查化合物库存 / 可用量 / 库存汇总 | `context.compound.getStockFromXfcSh({ filter })`;filter 使用 **dot-notation** 格式(如 `{ 'cas.$in': [...] }`);返回 `{ data, meta }`,取 **`.data`** 获取数据数组;不传 filter 时默认筛选 `status = '可用'`。另见 [agentic-lab-sdk](../../references/agentic-lab-sdk.md) |
|
|
34
33
|
|
|
35
34
|
## 字段映射(示例:知识库 → ticket.detail)
|
|
36
35
|
|
|
@@ -53,7 +52,7 @@ output:
|
|
|
53
52
|
| 序号 | 规则摘要 | 业务动作(示意) |
|
|
54
53
|
|------|----------|------------------|
|
|
55
54
|
| 1.1 | 汇总需求量 | `detail.requested_amount = sum(target_amount_n)` |
|
|
56
|
-
| 1.2 | 缺料判定 | 结合 `
|
|
55
|
+
| 1.2 | 缺料判定 | 结合 `getStockFromXfcSh` 汇总可用量与 `requested_amount` 比较 |
|
|
57
56
|
| 1.3 | 容差默认 | 目标量 >0 且容差空 → 默认 0.5 mg |
|
|
58
57
|
| 1.4 | 缺料标记 | `source_barcode` 空 → `是否缺料` = 缺料 |
|
|
59
58
|
|
|
@@ -70,7 +69,7 @@ output:
|
|
|
70
69
|
| 规则摘要 | 条件要点 | 业务动作或结果 |
|
|
71
70
|
| -------- | -------- | -------------- |
|
|
72
71
|
| 查询入口池 | 门控启动 | `ticket.list({ pool_id__in: pool_ids, limit: 999999, offset: 0 })`,取返回体 `list` |
|
|
73
|
-
| 查化合物数据 | 有 compound 查询需求 | `compound.
|
|
72
|
+
| 查化合物数据 | 有 compound 查询需求 | `compound.getStockFromXfcSh`(不传分页参数,使用 API 默认值) |
|
|
74
73
|
| 写回 detail | 查询完成 | 按映射表与规则表写回 `ticket.detail` → `ticket.update` |
|
|
75
74
|
| 拼装 orbit_link | 返回前 | `PageUrl.replace('{bookid}', bookid)` |
|
|
76
75
|
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
## 门控脚本生成约定(lab-flow-designer)
|
|
8
8
|
|
|
9
|
-
对 **`ticket.list`**、**`process.list
|
|
9
|
+
对 **`ticket.list`**、**`process.list`** 等支持 **`limit` / `offset`** 的查询:若流程说明**未**明确要求分页或条数,生成门控脚本时应显式传入 **`limit: 999999`**、**`offset: 0`**,避免引擎默认小页长截断。若流程说明写了分页或「查全部」的**具体数值**,以流程说明为准。
|
|
10
|
+
|
|
11
|
+
> **`compound.getStockFromXfcSh`** 使用 `pageSize` / `page` 分页(非 `limit` / `offset`),不传分页参数时使用 API 默认值。
|
|
10
12
|
|
|
11
13
|
---
|
|
12
14
|
|
|
@@ -176,265 +178,96 @@ async function station.list(): Promise<Station[]>
|
|
|
176
178
|
|
|
177
179
|
---
|
|
178
180
|
|
|
179
|
-
## compound.
|
|
181
|
+
## compound.getStockFromXfcSh
|
|
180
182
|
|
|
181
|
-
- **别名**:`context.compound.
|
|
182
|
-
-
|
|
183
|
+
- **别名**:`context.compound.getStockFromXfcSh`
|
|
184
|
+
- **描述**:获取 XFC 上海系统化合物库存。使用 `pageSize` / `page` 分页(非 `limit` / `offset`),不传分页参数时使用 API 默认值(默认 `pageSize: 400`、`page: 1`)。
|
|
183
185
|
|
|
184
186
|
```typescript
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
/** 查询参数:以 `filter` 表达条件(空对象是否合法以实现为准,建议至少包含一条有效条件)。 */
|
|
192
|
-
interface GetCompoundStockParams {
|
|
193
|
-
/**
|
|
194
|
-
* 作用于库存 collection 的筛选;键为库字段名,例如:
|
|
195
|
-
* - `{ cmpd_id: { $in: ['CA1078', 'CA1079'] } }`(批量化合物编号)
|
|
196
|
-
* - `{ cas: { $in: ['28022-43-7'] } }`(按 CAS)
|
|
197
|
-
* - `{ $or: [{ cmpd_id: 'CA1' }, { barcode: { $startsWith: 'BC' } }] }`
|
|
198
|
-
*/
|
|
199
|
-
filter: NocoBaseFilter;
|
|
200
|
-
/** 最大返回条数;默认以实现为准 */
|
|
201
|
-
limit?: number;
|
|
202
|
-
/** 分页偏移;默认以实现为准 */
|
|
203
|
-
offset?: number;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* 嵌套:项目(`project` 关联对象,节选)
|
|
208
|
-
* 注释体例:库字段 `name`;别名:`uiSchema.title`;备注:`description`
|
|
209
|
-
*/
|
|
210
|
-
interface CompoundStockProjectRef {
|
|
211
|
-
/** 库字段 `id`;别名:id;备注:关联主键 */
|
|
212
|
-
id: number;
|
|
213
|
-
/** 库字段 `createdAt`;别名:Created at;备注:无 */
|
|
214
|
-
createdAt?: string;
|
|
215
|
-
/** 库字段 `updatedAt`;别名:Last updated at;备注:无 */
|
|
216
|
-
updatedAt?: string;
|
|
217
|
-
/** 库字段 `project_code`;别名:项目编码;备注:以运行时为准 */
|
|
218
|
-
project_code?: string;
|
|
219
|
-
/** 库字段 `project_name`;别名:项目名称;备注:以运行时为准 */
|
|
220
|
-
project_name?: string;
|
|
221
|
-
/** 库字段 `project_link`;别名:项目链接;备注:以运行时为准 */
|
|
222
|
-
project_link?: string;
|
|
223
|
-
/** 库字段 `is_sync`;别名:是否同步;备注:以运行时为准 */
|
|
224
|
-
is_sync?: boolean;
|
|
225
|
-
/** 库字段 `createdById`;别名:Created by;备注:外键 users */
|
|
226
|
-
createdById?: number | null;
|
|
227
|
-
/** 库字段 `updatedById`;别名:Last updated by;备注:外键 users */
|
|
228
|
-
updatedById?: number | null;
|
|
187
|
+
interface StockFromXfcShParams {
|
|
188
|
+
filter?: Record<string, any>;
|
|
189
|
+
fields?: string[] | string;
|
|
190
|
+
pageSize?: number;
|
|
191
|
+
page?: number;
|
|
192
|
+
[propName: string]: any;
|
|
229
193
|
}
|
|
230
194
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
/** 库字段 `id`;别名:id;备注:关联主键 */
|
|
236
|
-
id: number;
|
|
237
|
-
createdAt?: string;
|
|
238
|
-
updatedAt?: string;
|
|
239
|
-
/** 库字段 `account`;别名:Account;备注:账户编码等 */
|
|
240
|
-
account?: string;
|
|
241
|
-
/** 库字段 `name`;别名:name;备注:账户名称 */
|
|
242
|
-
name?: string;
|
|
243
|
-
createdById?: number | null;
|
|
244
|
-
updatedById?: number | null;
|
|
245
|
-
}
|
|
195
|
+
async function compound.getStockFromXfcSh(
|
|
196
|
+
params?: StockFromXfcShParams
|
|
197
|
+
): Promise<CompoundResponse>
|
|
198
|
+
```
|
|
246
199
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
200
|
+
### 参数说明
|
|
201
|
+
|
|
202
|
+
| 参数名 | 含义 | 备注 |
|
|
203
|
+
|--------|------|------|
|
|
204
|
+
| `filter` | 筛选条件 | 查询化合物常用 `cas`、`cmpd_id`;**不传时默认筛选 `'status': '可用'`**。使用 **dot-notation** 格式(如 `'cas.$in': [...]`),**非**嵌套对象格式 |
|
|
205
|
+
| `fields` | 查询返回的字段 | **默认返回字段(不传时)**:`id, cas, status, source, barcode, cmpd_id, owner`。**全部可用字段**:`barcode, cas, cmpd_id, po_num, source, owner, hazard_class_adr, odor, initial_weight, initial_amount, initial_checkin_time, latest_weight, latest_amount_mg, latest_amount_ml, latest_checkin_time, latest_checkout_time, latest_location, location_detail, checkin_user, status, checkout_user, chemical_name`。**外键**:`f_category, f_account, f_latest_request_user, f_project` |
|
|
206
|
+
| `pageSize` | 分页大小 | 默认 `400` |
|
|
207
|
+
| `page` | 页码 | 默认 `1` |
|
|
208
|
+
|
|
209
|
+
### 调用示例
|
|
210
|
+
|
|
211
|
+
```javascript
|
|
212
|
+
const xfcStockSH = await this.context.compound.getStockFromXfcSh({
|
|
213
|
+
filter: {
|
|
214
|
+
'cas.$in': ['1065657-51-3']
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
// xfcStockSH.data → 数据数组
|
|
218
|
+
// xfcStockSH.meta → 分页信息
|
|
219
|
+
```
|
|
262
220
|
|
|
263
|
-
|
|
264
|
-
* 嵌套:最近申领人(`latest_request_user` 关联对象,节选)
|
|
265
|
-
* 对应 compound 上 `latest_request_user` 关联,`foreignKey` 为 `f_latest_request_user`
|
|
266
|
-
*/
|
|
267
|
-
interface CompoundStockUserRef {
|
|
268
|
-
id: number;
|
|
269
|
-
createdAt?: string;
|
|
270
|
-
updatedAt?: string;
|
|
271
|
-
nickname?: string;
|
|
272
|
-
username?: string;
|
|
273
|
-
email?: string | null;
|
|
274
|
-
phone?: string | null;
|
|
275
|
-
passwordChangeTz?: string | null;
|
|
276
|
-
appLang?: string | null;
|
|
277
|
-
systemSettings?: Record<string, unknown>;
|
|
278
|
-
createdById?: number | null;
|
|
279
|
-
updatedById?: number | null;
|
|
280
|
-
}
|
|
221
|
+
### 返回类型
|
|
281
222
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
* 字段注释:`name` = 接口/库字段 key;**别名** = `uiSchema.title`(含 i18n 模板时已展开为可读文案);**备注** = `description`(无则写「无」)
|
|
285
|
-
* 时间字段一般为 ISO8601 字符串
|
|
286
|
-
*/
|
|
287
|
-
interface CompoundStockRecord {
|
|
288
|
-
/** 库字段 `id`;别名:ID;备注:无 */
|
|
223
|
+
```typescript
|
|
224
|
+
interface CompoundItem {
|
|
289
225
|
id: number;
|
|
290
|
-
/** 库字段 `createdAt`;别名:Created at;备注:无 */
|
|
291
|
-
createdAt: string;
|
|
292
|
-
/** 库字段 `updatedAt`;别名:Last updated at;备注:无 */
|
|
293
|
-
updatedAt: string;
|
|
294
|
-
/** 库字段 `initial_checkin_time`;别名:Initial Check-in Time;备注:第一次进入系统的时间 */
|
|
295
|
-
initial_checkin_time?: string;
|
|
296
|
-
/** 库字段 `latest_checkin_time`;别名:Latest Check-in Time;备注:最近一次Check-in时间 */
|
|
297
|
-
latest_checkin_time?: string;
|
|
298
|
-
/** 库字段 `latest_checkout_time`;别名:Latest Check-out Time;备注:最近一次Check-out时间 */
|
|
299
|
-
latest_checkout_time?: string | null;
|
|
300
|
-
/** 库字段 `cas`;别名:CAS;备注:无 */
|
|
301
226
|
cas: string;
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
/** 库字段 `f_account`;别名:f_account;备注:外键 → account(化合物关联的客户) */
|
|
305
|
-
f_account?: number;
|
|
306
|
-
/** 库字段 `f_project`;别名:f_project;备注:外键 → project(化合物关联的项目) */
|
|
307
|
-
f_project?: number;
|
|
308
|
-
/** 库字段 `source`;别名:Source;备注:BB来源的供应商 */
|
|
309
|
-
source?: string;
|
|
310
|
-
/** 库字段 `barcode`;别名:Barcode;备注:全局唯一 */
|
|
227
|
+
status: string;
|
|
228
|
+
source: string;
|
|
311
229
|
barcode: string;
|
|
312
|
-
/** 库字段 `cmpd_id`;别名:CMPD-ID;备注:对应于自主实验室申请的Compound ID,示例:CA1078 */
|
|
313
230
|
cmpd_id: string;
|
|
314
|
-
|
|
315
|
-
owner?: string;
|
|
316
|
-
/** 库字段 `f_category`;别名:f_category;备注:外键 → category(化合物种类) */
|
|
317
|
-
f_category?: number;
|
|
318
|
-
/** 库字段 `hazard_class_adr`;别名:HazardClassAdr;备注:危险品等级 */
|
|
319
|
-
hazard_class_adr?: string | null;
|
|
320
|
-
/** 库字段 `odor`;别名:Odor;备注:气味 */
|
|
321
|
-
odor?: string;
|
|
322
|
-
/** 库字段 `initial_amount`;别名:Initial Amount;备注:第一次进入系统的原料重量(新瓶子),单位mg */
|
|
323
|
-
initial_amount?: number;
|
|
324
|
-
/** 库字段 `initial_weight`;别名:Initial Weight;备注:第一次进入系统的瓶子重量(新瓶子),单位g */
|
|
325
|
-
initial_weight?: number;
|
|
326
|
-
/** 库字段 `latest_weight`;别名:Latest Weight;备注:当前瓶重,每一次重新回到系统后更新,单位g */
|
|
327
|
-
latest_weight?: number;
|
|
328
|
-
/** 库字段 `latest_amount_mg`;别名:Latest Amount(mg);备注:原料的当前实际重量,每次Check-in时重新计算 */
|
|
329
|
-
latest_amount_mg?: number;
|
|
330
|
-
/** 库字段 `latest_amount_ml`;别名:Latest Amount(mL);备注:当前实际重量,单位mL,手填 */
|
|
331
|
-
latest_amount_ml?: number | null;
|
|
332
|
-
/** 库字段 `latest_location`;别名:Latest Location;备注:当前位置 */
|
|
333
|
-
latest_location?: string;
|
|
334
|
-
/** 库字段 `location_detail`;别名:Location Detail;备注:详细位置信息 */
|
|
335
|
-
location_detail?: Record<string, unknown>;
|
|
336
|
-
/** 库字段 `f_latest_request_user`;别名:f_latest_request_user;备注:外键 → users(申请出库使用者) */
|
|
337
|
-
f_latest_request_user?: number;
|
|
338
|
-
/** 库字段 `status`;别名:Status;备注:无(可选值如:可用、已预约、已出库、已核销) */
|
|
339
|
-
status?: string;
|
|
340
|
-
/** 库字段 `checkin_user`;别名:Check-in User;备注:初始入库用户的邮箱, 由orbit提交 */
|
|
341
|
-
checkin_user?: string;
|
|
342
|
-
/** 库字段 `checkout_user`;别名:Check-out User;备注:出库用户的邮箱, 由orbit提交 */
|
|
343
|
-
checkout_user?: string;
|
|
344
|
-
/** 库字段 `chemical_name`;别名:Chemical Name;备注:无 */
|
|
345
|
-
chemical_name?: string | null;
|
|
346
|
-
/** 库字段 `createdById`;别名:Created by(外键);备注:无 */
|
|
347
|
-
createdById?: number | null;
|
|
348
|
-
/** 库字段 `updatedById`;别名:Last updated by(外键);备注:无 */
|
|
349
|
-
updatedById?: number | null;
|
|
350
|
-
/** 库字段 `project`(关联);别名:Project;备注:化合物关联的项目 */
|
|
351
|
-
project?: CompoundStockProjectRef;
|
|
352
|
-
/** 库字段 `account`(关联);别名:Account;备注:化合物关联的客户 */
|
|
353
|
-
account?: CompoundStockAccountRef;
|
|
354
|
-
/** 库字段 `category`(关联);别名:Category;备注:化合物种类 */
|
|
355
|
-
category?: CompoundStockCategoryRef;
|
|
356
|
-
/** 库字段 `latest_request_user`(关联);别名:Latest Request User;备注:申请出库使用者 */
|
|
357
|
-
latest_request_user?: CompoundStockUserRef;
|
|
231
|
+
owner: string;
|
|
358
232
|
}
|
|
359
233
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
---
|
|
366
|
-
|
|
367
|
-
## compound.getChemicalRecord
|
|
368
|
-
|
|
369
|
-
- **别名**:`context.compound.getChemicalRecord`
|
|
370
|
-
- **描述**:按 NocoBase **`filter`** 查询 **化合物知识库** `chemical_record`(可分装性、化性状、备注等),与库存 `compound.getStock` 区分;算子与 `GetCompoundStockParams.filter` 相同,见 [Filter Operators](https://docs.nocobase.com/api/database/operators)。
|
|
371
|
-
|
|
372
|
-
```typescript
|
|
373
|
-
type NocoBaseFilter = Record<string, unknown>;
|
|
374
|
-
|
|
375
|
-
/** 查询参数:以 `filter` 描述对 `chemical_record` 的筛选。 */
|
|
376
|
-
interface GetChemicalRecordParams {
|
|
377
|
-
/**
|
|
378
|
-
* 示例:`{ cmpd_id: { $in: ['CA1078'] } }`;
|
|
379
|
-
* `{ compound_id: { $in: ['28022-43-7'] } }`(业务将 CAS 存于 `compound_id` 时);
|
|
380
|
-
* `{ dispensable: { $isTruly: true } }`(布尔字段算子见 NocoBase 文档)。
|
|
381
|
-
*/
|
|
382
|
-
filter: NocoBaseFilter;
|
|
383
|
-
limit?: number;
|
|
384
|
-
offset?: number;
|
|
234
|
+
interface CompoundMeta {
|
|
235
|
+
count: number;
|
|
236
|
+
page: number;
|
|
237
|
+
pageSize: number;
|
|
238
|
+
totalPage: number;
|
|
385
239
|
}
|
|
386
240
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
*/
|
|
391
|
-
interface ChemicalRecord {
|
|
392
|
-
/** 库字段 `id`;别名:ID;备注:无 */
|
|
393
|
-
id: number;
|
|
394
|
-
/** 库字段 `createdAt`;别名:Created at;备注:无 */
|
|
395
|
-
createdAt: string;
|
|
396
|
-
/** 库字段 `updatedAt`;别名:Last updated at;备注:无 */
|
|
397
|
-
updatedAt: string;
|
|
398
|
-
/** 库字段 `compound_id`;别名:Compound ID (CAS#);备注:无 */
|
|
399
|
-
compound_id: string;
|
|
400
|
-
/** 库字段 `cmpd_id`;别名:CMPD-ID;备注:无 */
|
|
401
|
-
cmpd_id: string;
|
|
402
|
-
/** 库字段 `business_type`;别名:Business Type;备注:无(可选值如:Fragment、VAST、D2B) */
|
|
403
|
-
business_type?: string;
|
|
404
|
-
/** 库字段 `chemist_property`;别名:Chemist Property;备注:性状(Chemist) */
|
|
405
|
-
chemist_property?: string[];
|
|
406
|
-
/** 库字段 `dispensable`;别名:Dispensable;备注:可分装判断(Chemist) */
|
|
407
|
-
dispensable: boolean;
|
|
408
|
-
/** 库字段 `dispensing_remark`;别名:Dispensing Remark;备注:不可分装备注(Chemist) */
|
|
409
|
-
dispensing_remark?: string[];
|
|
410
|
-
/** 库字段 `large_particle`;别名:Large Particle;备注:固体颗粒过大(Large Particle) */
|
|
411
|
-
large_particle?: boolean | null;
|
|
412
|
-
/**
|
|
413
|
-
* 库字段 `poor_flowability`;别名:Poor Flowability;备注:流动性差(Poor Flowability)
|
|
414
|
-
* 库类型为 string;若历史数据为 boolean,以实现为准
|
|
415
|
-
*/
|
|
416
|
-
poor_flowability?: string | null;
|
|
417
|
-
/** 库字段 `hygroscopicity`;别名:Hygroscopicity;备注:强吸水性(Hygroscopicity) */
|
|
418
|
-
hygroscopicity?: boolean | null;
|
|
419
|
-
/** 库字段 `agglomeration_tendency`;别名:Agglomeration Tendency;备注:易结块(Agglomeration Tendency) */
|
|
420
|
-
agglomeration_tendency?: boolean | null;
|
|
421
|
-
/** 库字段 `sticky`;别名:Sticky;备注:易粘黏(Sticky) */
|
|
422
|
-
sticky?: boolean | null;
|
|
423
|
-
/** 库字段 `strong_electrostatic_adsorption`;别名:Strong Electrostatic Adsorption;备注:强静电吸附(Strong Electrostatic Adsorption) */
|
|
424
|
-
strong_electrostatic_adsorption?: boolean | null;
|
|
425
|
-
/** 库字段 `remark`;别名:Remark;备注:无(schema 默认文案见数据源配置) */
|
|
426
|
-
remark?: string;
|
|
427
|
-
/** 库字段 `duplicate_check`;别名:Duplicate Check;备注:重复判断 */
|
|
428
|
-
duplicate_check?: string;
|
|
429
|
-
/** 库字段 `lark_record_id`;别名:Lark Record ID;备注:无 */
|
|
430
|
-
lark_record_id?: string;
|
|
431
|
-
/** 库字段 `createdById`;别名:Created by(外键);备注:无 */
|
|
432
|
-
createdById?: number | null;
|
|
433
|
-
/** 库字段 `updatedById`;别名:Last updated by(外键);备注:无 */
|
|
434
|
-
updatedById?: number | null;
|
|
241
|
+
interface CompoundResponse {
|
|
242
|
+
data: CompoundItem[];
|
|
243
|
+
meta: CompoundMeta;
|
|
435
244
|
}
|
|
245
|
+
```
|
|
436
246
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
247
|
+
### 返回数据示例
|
|
248
|
+
|
|
249
|
+
```json
|
|
250
|
+
{
|
|
251
|
+
"data": [
|
|
252
|
+
{
|
|
253
|
+
"id": 151,
|
|
254
|
+
"cas": "1065657-51-3",
|
|
255
|
+
"status": "可用",
|
|
256
|
+
"source": "上海锐谱医药科技有限公司",
|
|
257
|
+
"barcode": "XFC25103100049",
|
|
258
|
+
"cmpd_id": "BA0062",
|
|
259
|
+
"owner": "张琳"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"id": 6613,
|
|
263
|
+
"cas": "1065657-51-3",
|
|
264
|
+
"status": "可用",
|
|
265
|
+
"source": "上海锐谱医药科技有限公司",
|
|
266
|
+
"barcode": "XFC25121900056",
|
|
267
|
+
"cmpd_id": "BA0062",
|
|
268
|
+
"owner": "张琳"
|
|
269
|
+
}
|
|
270
|
+
],
|
|
271
|
+
"meta": { "count": 2, "page": 1, "pageSize": 400, "totalPage": 1 }
|
|
272
|
+
}
|
|
440
273
|
```
|
|
@@ -173,7 +173,7 @@ class Processer {
|
|
|
173
173
|
|
|
174
174
|
## 执行流程
|
|
175
175
|
|
|
176
|
-
1. **start 阶段**:`ticket.list` 按 `pool_id__in`(由 `Processer` 入参 `pool_ids` 映射)拉数,**取返回体 `list` 字段** →
|
|
176
|
+
1. **start 阶段**:`ticket.list` 按 `pool_id__in`(由 `Processer` 入参 `pool_ids` 映射)拉数,**取返回体 `list` 字段** → 按需拉化合物库存(`compound.getStockFromXfcSh`) → 写回 `detail` → `ticket.update`。
|
|
177
177
|
2. **complete 阶段**:`pool.getNext` 取出口池 → `ticket.update` / `ticket.append`。
|
|
178
178
|
|
|
179
179
|
## 输入/输出
|
|
@@ -158,7 +158,7 @@
|
|
|
158
158
|
|
|
159
159
|
1. **方法名**:与 **agentic-lab-sdk** 中条目一致(如 `process.execute`、`process.list`、`ticket.list` 等),且用途与文档叙述一致。
|
|
160
160
|
2. **请求体与类型**:对 **`process.execute`**,须能整理出 **`items[]`**(与 **[`rhea-api/execute_process_batch.md`](rhea-api/execute_process_batch.md)** 请求体中 `items` 一项同形):每条至少含 **`process`**、**`params`**(键、类型、默认值或来源字段);若流程需绑定业务表记录,则须说明 **`table_objects`**(表名、主键与 ticket 明细的对应);并须说明 **`baseURL`** 如何得到;**禁止**仅写「组装 JSON」而无键级说明。
|
|
161
|
-
3. **查询类接口**:若使用 `ticket.list`、`compound.
|
|
161
|
+
3. **查询类接口**:若使用 `ticket.list`、`compound.getStockFromXfcSh` 等,分页策略须与流程说明及 skill「门控脚本编写指引」一致或显式写出分页参数(`ticket.list` 使用 `limit`/`offset`,`compound.getStockFromXfcSh` 使用 `pageSize`/`page`);**`filter`** 须说明字段名与算子意图,避免无法落地的自然语言。
|
|
162
162
|
4. **`process.execute` 与 HTTP**:流程文档约束 **`items`**、**`baseURL` 语义**及任务参数;**不要求**写 path / HTTP 方法(由 SDK 封装)。
|
|
163
163
|
|
|
164
164
|
**预检产出**:凡属 **§3.2 / §3.3** 的不通过项,须在《流程文档合规问题与优化建议》中单独列出 **「不支持或不可生成项」**,避免进入代码生成阶段才发现无法对接。
|
|
@@ -135,9 +135,9 @@ output:
|
|
|
135
135
|
|
|
136
136
|
**化合物数据查询方式**
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
查询化合物库存的可用量:使用输入池 Compound ID 作为索引,通过 XFC 上海系统接口查询化合物库存(`compound.getStockFromXfcSh`),查找 status = 可用的记录,返回可用量字段的值并汇总加和。
|
|
139
139
|
|
|
140
|
-
|
|
140
|
+
查询化合物属性:使用输入池 Compound ID 通过 XFC 上海系统接口查询化合物相关属性。
|
|
141
141
|
|
|
142
142
|
**字段映射表**
|
|
143
143
|
|
|
@@ -212,7 +212,7 @@ function generateMockTickets(pools, valveConfig, adversarial) {
|
|
|
212
212
|
const KNOWN_SDK_NAMESPACES = new Set(['ticket', 'pool', 'compound', 'process', 'station']);
|
|
213
213
|
|
|
214
214
|
const PAGINATED_METHODS = new Set([
|
|
215
|
-
'ticket.list', 'process.list', 'compound.
|
|
215
|
+
'ticket.list', 'process.list', 'compound.getStockFromXfcSh',
|
|
216
216
|
]);
|
|
217
217
|
|
|
218
218
|
function createMockContext(pools, valveConfig, adversarial) {
|
|
@@ -250,31 +250,14 @@ function createMockContext(pools, valveConfig, adversarial) {
|
|
|
250
250
|
},
|
|
251
251
|
},
|
|
252
252
|
compound: {
|
|
253
|
-
|
|
254
|
-
callLog.push({ method: 'compound.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
id: i, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z',
|
|
260
|
-
cas: `12345-67-${i}`, barcode: `BC-MOCK-${i}`, cmpd_id: `CA${1000 + i}`,
|
|
261
|
-
status: 'available', latest_amount_mg: 50 * i, latest_weight: 10 * i,
|
|
262
|
-
chemical_name: `MockChemical${i}`,
|
|
263
|
-
}));
|
|
264
|
-
},
|
|
265
|
-
getChemicalRecord: async (params) => {
|
|
266
|
-
callLog.push({ method: 'compound.getChemicalRecord', params });
|
|
267
|
-
if (params?.limit === undefined) {
|
|
268
|
-
warnings.push('compound.getChemicalRecord called without explicit limit');
|
|
269
|
-
}
|
|
270
|
-
return [1, 2].map(i => ({
|
|
271
|
-
id: i, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z',
|
|
272
|
-
compound_id: `12345-67-${i}`, cmpd_id: `CA${1000 + i}`,
|
|
273
|
-
dispensable: i % 2 === 0, chemist_property: ['solid'],
|
|
274
|
-
dispensing_remark: [], large_particle: false, hygroscopicity: false,
|
|
275
|
-
agglomeration_tendency: false, sticky: false,
|
|
276
|
-
strong_electrostatic_adsorption: false,
|
|
253
|
+
getStockFromXfcSh: async (params) => {
|
|
254
|
+
callLog.push({ method: 'compound.getStockFromXfcSh', params });
|
|
255
|
+
const data = [1, 2].map(i => ({
|
|
256
|
+
id: i, cas: `12345-67-${i}`, status: '可用',
|
|
257
|
+
source: 'MockSupplier', barcode: `XFC-MOCK-${i}`,
|
|
258
|
+
cmpd_id: `CA${1000 + i}`, owner: 'mock@example.com',
|
|
277
259
|
}));
|
|
260
|
+
return { data, meta: { count: data.length, page: 1, pageSize: 400, totalPage: 1 } };
|
|
278
261
|
},
|
|
279
262
|
},
|
|
280
263
|
process: {
|
|
@@ -390,6 +373,17 @@ function loadProcesserClass(scriptContent, filename) {
|
|
|
390
373
|
Promise, Array, Object, Map, Set, JSON, Math, Date,
|
|
391
374
|
Number, String, Boolean, RegExp, Error, TypeError, ReferenceError,
|
|
392
375
|
parseInt, parseFloat, isNaN, isFinite, undefined, NaN, Infinity,
|
|
376
|
+
encodeURIComponent, decodeURIComponent, encodeURI, decodeURI,
|
|
377
|
+
fetch: async (url, opts) => {
|
|
378
|
+
const method = (opts && opts.method) || 'GET';
|
|
379
|
+
sandbox.console.warn(`[test-processer] fetch called: ${method} ${url}`);
|
|
380
|
+
return {
|
|
381
|
+
ok: true,
|
|
382
|
+
status: 200,
|
|
383
|
+
json: async () => ({ data: {} }),
|
|
384
|
+
text: async () => '{}',
|
|
385
|
+
};
|
|
386
|
+
},
|
|
393
387
|
};
|
|
394
388
|
const ctx = createContext(sandbox);
|
|
395
389
|
return script.runInContext(ctx);
|
|
@@ -551,25 +545,14 @@ function createTracingContext(pools, valveConfig, tickets) {
|
|
|
551
545
|
},
|
|
552
546
|
},
|
|
553
547
|
compound: {
|
|
554
|
-
|
|
555
|
-
const
|
|
556
|
-
id: i,
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
chemical_name: `MockChemical${i}`,
|
|
548
|
+
getStockFromXfcSh: async (params) => {
|
|
549
|
+
const data = [1, 2].map(i => ({
|
|
550
|
+
id: i, cas: `12345-67-${i}`, status: '可用',
|
|
551
|
+
source: 'MockSupplier', barcode: `XFC-MOCK-${i}`,
|
|
552
|
+
cmpd_id: `CA${1000 + i}`, owner: 'mock@example.com',
|
|
560
553
|
}));
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
},
|
|
564
|
-
getChemicalRecord: async (params) => {
|
|
565
|
-
const result = [1, 2].map(i => ({
|
|
566
|
-
id: i, createdAt: '2026-01-01T00:00:00Z', updatedAt: '2026-01-01T00:00:00Z',
|
|
567
|
-
compound_id: `12345-67-${i}`, cmpd_id: `CA${1000 + i}`,
|
|
568
|
-
dispensable: i % 2 === 0, chemist_property: ['solid'],
|
|
569
|
-
dispensing_remark: [], large_particle: false, hygroscopicity: false,
|
|
570
|
-
agglomeration_tendency: false, sticky: false, strong_electrostatic_adsorption: false,
|
|
571
|
-
}));
|
|
572
|
-
traceLog.push({ seq: seq++, method: 'compound.getChemicalRecord', params: JSON.parse(JSON.stringify(params || {})), returnSummary: `${result.length} records` });
|
|
554
|
+
const result = { data, meta: { count: data.length, page: 1, pageSize: 400, totalPage: 1 } };
|
|
555
|
+
traceLog.push({ seq: seq++, method: 'compound.getStockFromXfcSh', params: JSON.parse(JSON.stringify(params || {})), returnSummary: `${data.length} records` });
|
|
573
556
|
return result;
|
|
574
557
|
},
|
|
575
558
|
},
|
|
@@ -743,7 +726,7 @@ function formatPreviewReport(trace) {
|
|
|
743
726
|
const allCalls = [...startTraceLog, ...completeTraceLog];
|
|
744
727
|
const usedMethods = new Set(allCalls.map(c => c.method));
|
|
745
728
|
const allMethods = ['ticket.list', 'ticket.update', 'ticket.append', 'pool.getNext',
|
|
746
|
-
'compound.
|
|
729
|
+
'compound.getStockFromXfcSh', 'process.list', 'process.execute', 'station.list'];
|
|
747
730
|
const unused = allMethods.filter(m => !usedMethods.has(m));
|
|
748
731
|
|
|
749
732
|
const startStatus = startError ? `✗ 失败: ${startError.split('\n')[0]}` : `✓ 成功 (${startTraceLog.length} SDK 调用, ${inputTickets.length} tickets)`;
|
|
@@ -1003,6 +986,16 @@ async function runTests(args) {
|
|
|
1003
986
|
);
|
|
1004
987
|
}
|
|
1005
988
|
}
|
|
989
|
+
|
|
990
|
+
// Check for sandbox-unavailable APIs in source code
|
|
991
|
+
const UNAVAILABLE_APIS = ['URLSearchParams', 'TextEncoder', 'TextDecoder', 'Buffer'];
|
|
992
|
+
for (const api of UNAVAILABLE_APIS) {
|
|
993
|
+
if (scriptContent.includes(api)) {
|
|
994
|
+
result.phases.compliance.warnings.push(
|
|
995
|
+
`Script uses '${api}' which is not available in the gate script sandbox — use template literals + encodeURIComponent instead`
|
|
996
|
+
);
|
|
997
|
+
}
|
|
998
|
+
}
|
|
1006
999
|
}
|
|
1007
1000
|
|
|
1008
1001
|
const normalPass = result.phases.normalRun.start.pass && result.phases.normalRun.complete.pass;
|