# GEO 服务商 API 对接文档

版本：v1.3 · 更新日期：2026-10-13

标准接口文件：[OpenAPI 3.0.3 JSON](./openapi.json)，可在 Apifox / Postman 中导入；导入后将服务器地址设为实际 HTTPS 地址加 `/api/v1`。规范参考：[OpenAPI 3.0.3](https://spec.openapis.org/oas/v3.0.3.html)。

## 1. 接入地址与认证

Base URL：**部署本系统的域名 + /api/v1**。部署后的公开文档地址为 **/api-docs**，无需管理账号即可阅读。

所有请求携带：

```http
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
```

API Key 由管理员在“服务商 API”页面签发，只在创建后显示一次，可以设置有效期并随时撤销。服务商不得使用管理员账号密码，不得将密钥放入浏览器前端、URL、日志或公开仓库。适用于服务商后端调用。

旧 IP 部署若仍使用自签名证书，需要从管理员获取 tls.crt。请通过可信渠道核对证书指纹，并将此证书配置为该服务的信任证书；curl 使用 --cacert tls.crt，Python requests 使用 verify='tls.crt'。不要使用 -k 或 verify=False。正式域名及受信任证书配置完成后可使用系统默认信任链。

**验收范围：** 本版本包含云端采集适配、19 个平台版本、模拟上游测试及原有业务回归。真实上游账户及平台采集需要部署后验收；202 表示任务成功入队，不代表成功获得 AI 回答。本地浏览器采集仍需服务器登录。

## 2. 数据与权限

- read：查询授权品牌、问题、任务、来源和报告。
- monitor：为授权品牌发起监控。
- provision：创建自己的品牌及问题。
- 自助接入密钥初始没有内部品牌权限，创建的新品牌自动加入该密钥授权范围。
- 不同密钥创建的数据默认互相不可访问。管理员可额外授权指定品牌。
- 不提供平台登录管理、修改定时计划、修改内部品牌或删除数据的外部接口。
- 密钥轮换时选择已有客户账户（同一 customerId）并勾选其品牌，再撤销旧密钥；这样可共用余额、幂等记录及客户历史。

## 3. 接口列表

以下路径均相对于 Base URL。

| 方法 | 路径 | 权限 | 说明 |
| --- | --- | --- | --- |
| GET | /platforms | read | 支持的平台标识与名称 |
| GET | /usage | read | 客户 ID、余额、有效销售价及请求统计 |
| POST | /brands | provision | 创建品牌并自动授权 |
| GET | /brands | read | 仅返回本密钥可访问品牌 |
| POST | /brands/{brandId}/questions | provision | 批量添加问题，同品牌相同文本复用已有记录 |
| GET | /brands/{brandId}/questions | read | 获取问题 ID |
| POST | /runs | monitor | 创建异步监控任务 |
| GET | /runs/{runId} | read | 查询任务状态及回答 |
| GET | /runs/{runId}/report | read | 查询采样报告 JSON |
| GET | /brands/{brandId}/runs?limit=20 | read | 任务列表，limit 最多 100 |
| GET | /brands/{brandId}/overview?days=30 | read | 统计与趋势 |
| GET | /brands/{brandId}/sources?days=30&platformId=deepseek | read | 来源列表 |

## 4. 完整调用流程

### 4.1 创建品牌（可选，直接采集无需调用）

```http
POST /api/v1/brands
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{"name":"示例品牌","aliases":["示例品牌简称"]}
```

成功返回 201，读取响应中的 id 作为 brandId。name 为 1–100 字，aliases 可选，最多 20 个，每个最多 100 字。品牌创建不做名称去重；建议保存 ID，网络结果不明时先查询 /brands，避免重复创建。

### 4.2 添加问题

```http
POST /api/v1/brands/BRAND_ID/questions
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json

{"texts":["示例品牌怎么样？","示例品牌适合哪些用户？"],"category":"品牌认知"}
```

响应 201：

```json
{"questions":[{"id":"QUESTION_ID","text":"示例品牌怎么样？"}],"created":1}
```

响应示例作字段节选，实际返回问题完整属性。每次 1–100 个问题，每题最多 2000 字。

### 4.3 创建监控

```http
POST /api/v1/runs
Authorization: Bearer YOUR_API_KEY
Idempotency-Key: vendor-order-20261009-001
Content-Type: application/json

{"brandId":"BRAND_ID","questionIds":["QUESTION_ID"],"platforms":["deepseek"],"repeatCount":1,"intervalSeconds":60}
```

成功返回 202：

```json
{"id":"RUN_ID","brandId":"BRAND_ID","status":"queued","reused":false}
```

Idempotency-Key 必填，为 1–80 位字母、数字、下划线、点或连字符。同一密钥、相同 Idempotency-Key、相同参数重复请求，返回原任务并标记 reused=true；参数不同返回 409。该键应代表服务商的一次业务请求，重试时保持不变。

接受直接问题 prompt、单平台 platform，或旧 brandId、questionIds 模式；公共参数为 platforms、repeatCount、intervalSeconds、collector、platformOptions。repeatCount 默认 1，范围 1–20；intervalSeconds 默认 60，范围 0–3600。问题必须属于授权品牌且已启用。所有服务商采集固定使用云端采集。无需指定 collector，系统自动选择云端采集通道；不支持指定本地采集通道。不会继承管理端本地浏览器配置，上游未配置或失败时不会降级到本地浏览器。外部任务固定单任务并发 1。不同云端采集任务可并行执行，云端全局最多同时执行 4 个采集单元（运营方可用 GEO_CLOUD_CONCURRENCY 配置 1–16，修改后需重启）。同任务重复采样仍遵守 intervalSeconds；不同客户、不同任务不共享采样间隔。达到并发上限时排队，不改变余额与幂等规则。

### 4.4 轮询与结果

每隔 5–10 秒查询 GET /runs/RUN_ID。终态包括 completed、partial_failed、failed、cancelled；queued 和 running 表示尚未结束。

platformRuns 数组按平台与采样轮次返回结果。成功记录的 answer 包含 content、brandMentioned、mentionCount、citations 和 responseUrl。采集失败时 answer 可能为空。失败说明不包含服务器路径和内部日志；需要管理员查阅详细原因。

GET /runs/RUN_ID/report 返回 summary、groups 和 samples。summary 包括 planned、valid、mentioned、failed、cancelled、pending、mentionRate、coverageRate、citationRate。没有有效样本时比率可能为 null。

### 无品牌直接采集（推荐）

无需先调用创建品牌和添加问题接口。只需 monitor 权限和有效余额：

```http
POST /api/v1/runs
Authorization: Bearer YOUR_API_KEY
Idempotency-Key: your-order-unique-id
Content-Type: application/json

{"prompt":"什么是 SaaS agent 平台？","platform":"deepseek"}
```

也可用 platforms 数组指定多个平台，但不能与 platform 同传。prompt 为 1–2000 字，不能和 brandId/questionIds 同传。系统按客户唯一 ID 保存隔离的采集记录，不收取创建品牌或添加问题接口费；沿用采样、截图及 runs.create 价格。返回任务 id 后调用 GET /runs/{id} 查询。余额不足时任务及自动归档记录全部回滚。同一客户同一 Idempotency-Key 重试不重复扣费。原品牌模式保留兼容。

## 5. Python 示例

依赖 requests。将 API Key 放入 GEO_API_KEY 环境变量，将 GEO_API_BASE_URL 设为实际服务器地址加 /api/v1；仅旧自签名部署需要通过 GEO_CA_BUNDLE 指定已核验的证书。运行脚本直接提交一个监控任务，无需创建品牌或问题。

```python
import os
import time
import uuid
import requests

base = os.environ["GEO_API_BASE_URL"].rstrip("/")
session = requests.Session()
session.headers.update({"Authorization": f"Bearer {os.environ['GEO_API_KEY']}"})
session.verify = os.environ.get("GEO_CA_BUNDLE") or True

def call(method, path, **kwargs):
    response = session.request(method, base + path, timeout=30, **kwargs)
    response.raise_for_status()
    return response.json()

# 生产环境将 request_id 与业务订单持久化，网络重试时复用。
request_id = "vendor-" + uuid.uuid4().hex
run = call("POST", "/runs", headers={"Idempotency-Key": request_id}, json={
    "prompt": "什么是 SaaS agent 平台？",
    "platforms": ["deepseek"],
    "repeatCount": 1,
    "intervalSeconds": 60
})
for _ in range(120):
    detail = call("GET", f"/runs/{run['id']}")
    if detail["status"] in {"completed", "partial_failed", "failed", "cancelled"}:
        print(call("GET", f"/runs/{run['id']}/report"))
        break
    time.sleep(10)
else:
    print("任务仍在运行，请保存任务 ID 稍后查询：", run["id"])
```

生产调用应单独处理 429 的等待重试和网络超时；不要因一次超时就换用新的 Idempotency-Key。

## 6. 余额准入与错误

无每日、每分钟、每小时次数限制，也不因排队任务达到 5 个而拒绝新请求。单任务最多 20 个采集单元、并发为 1；批量问题和参数长度校验保留。计次仅用于统计。

| HTTP | 含义 |
| --- | --- |
| 400 | 参数无效或未配置采集器 |
| 401 | 密钥无效、过期或撤销 |
| 402 | 余额不足，业务写入和扣费整体回滚 |
| 403 | 接口关闭、密钥关闭、未生效或无权限 |
| 404 | 资源不存在或不属于当前租户 |
| 409 | 幂等冲突，或平台销售价尚未配置 |
| 410 | 截图超过保留期限 |
| 500 | 服务异常 |

### 采集失败原因（2026-10-09）

HTTP 202 仅表示任务已入队。查询任务详情时，应检查每个 `platformRuns[]` 的 `status`、`errorCode`、`errorMessage`、`retryable`，不能仅凭 HTTP 200 判断采集成功。

| errorCode | 原因 | 处理方式 |
| --- | --- | --- |
| LOGIN_REQUIRED | 服务器采集浏览器未登录或登录失效 | 管理员在服务器浏览器中登录后，重新提交任务 |
| CAPTCHA_REQUIRED | 平台要求人工验证 | 人工完成验证后再提交，避免立即连续重试 |
| BROWSER_NOT_CONNECTED | OpenCLI 浏览器桥未连接 | 管理员恢复 Bridge 连接后再提交 |
| COLLECTION_TIMEOUT | 平台响应超时 | 可以稍后重试 |
| COLLECTION_FAILED | 其他错误 | 联系管理员查询内部日志 |

没有错误时 `errorCode`、`errorMessage` 为 `null`。`retryable: false` 表示需要先处理环境问题或查看日志，不应自动重试；它不代表永久禁止重新提交。恢复环境后重跑应使用新的 `Idempotency-Key`，旧键仍返回原任务。

同一批次中遇到登录、验证码或浏览器未连接时，该平台后续采集单元直接标记失败并沿用原因，其他平台继续。下一批重新检查，不会永久封禁该平台。旧任务只记录了“超时”的，无法事后推断为登录或验证码错误。

采集器会检查已知可见登录弹窗、手机号表单及验证码 iframe；平台页面变化后仍可能需要更新检测规则。检测与明确报错不等于完成了登录或获得有效回答。


## 7. 平台目录与云端采集（2026-10-12）

以下 19 个版本同时出现在管理界面、GET /platforms 和公开网页文档。目录存在不代表上游实时可用，新平台需先配置销售价。

新增平台：`chatgpt`（ChatGPT）、`googleai`（Google AI）、`bingai_overviews`（Bing AI）、`xiaohongshu_mobile`（小红书点点移动端）。

| 平台 | platforms 参数 | 版本 |
| --- | --- | --- |
| DeepSeek | `deepseek` | 网页版 |
| 豆包 | `doubao` | 网页版 |
| Kimi | `kimi` | 网页版 |
| 通义千问 | `qwen` | 网页版 |
| 元宝 | `yuanbao` | 网页版 |
| 豆包 | `doubao_mobile` | 手机版 |
| 元宝 | `yuanbao_mobile` | 手机版 |
| DeepSeek | `deepseek_mobile` | 手机版 |
| 百度文心 | `baiduai` | 网页版 |
| 百度文心 | `baidu_mobile` | 手机版 |
| 通义千问 | `qianwen_mobile` | 手机版 |
| 蚂蚁阿福 | `antafu` | 网页版 |
| 夸克 | `quark` | 网页版 |
| 微博智搜 | `weibo_zhisou` | 网页版 |
| 抖音AI | `douyinai` | 网页版 |

兼容性：千问网页版继续使用 `qwen`，服务器将其映射至上游 `qianwen`；手机版使用 `qianwen_mobile`。不要将网页版和手机版合并成一个平台。

GET /platforms 返回每个平台的 id、name、label、client、upstreamId、supportedCollectors、defaultMode、upstreamConfigured、pricing。upstreamConfigured 仅说明服务器已配置凭据，不是账户有效性或实时平台可用性证明。平台维护时以采集单元返回的失败状态为准。

### 云端提交示例

```json
{
  "brandId": "BRAND_ID",
  "questionIds": [
    "QUESTION_ID"
  ],
  "platforms": [
    "doubao",
    "qianwen_mobile"
  ],
  "repeatCount": 1,
  "intervalSeconds": 60,
  "platformOptions": {
    "doubao": {
      "screenshot": false
    },
    "qianwen_mobile": {
      "screenshot": true
    }
  }
}
```

`platformOptions` 是以已选平台 id 为键的对象，每项仅允许 `screenshot` 一个布尔字段，默认 false。它只适用于云端采集。非布尔值、未知字段、未选择的平台配置，返回 400。云端采集统一使用普通联网搜索。云端业务模式与本地 headed/headless 浏览器模式是两个概念。定时采集未设置这些选项时使用普通搜索、不截图。

系统每个采集单元生成一个持久化上游幂等标识，重试保持标识及请求快照不变；重复采样分别调度。已保存上游任务编号时，服务重启恢复查询原任务。本版本通过后台轮询同步，不设置或接收上游 Callback。每次网络请求最多重试 3 次，仅网络异常、429 或 5xx 可重试；业务拒绝不盲目重试。单个采集最长等待 30 分钟，响应未确认或超时需要管理员核对原任务后决定下一步，不要直接换新幂等键重复采集。

### 回答、来源与截图

每个 platformRuns 项新增 collector、monitoringOptions、quotedPriceCents。answer.providerResult 包含 screenshotUrl、references、sentiment、mentionPosition、competitorRankings。未知值使用 null 或空数组。上游扣费金额、任务原始响应和凭据不通过服务商结果返回。

answer.citations 仅取上游 citationList 中实际引用的链接，按 URL 去重；references 是 referenceList 来源集合，不能当作实际引用次数。无法获取精确引用频次时，每个去重链接仅计一次。brandMentioned/mentionCount 仍采用本地关键词匹配，上游情感及位置另存于 providerResult。上游响应先保存到服务器数据库，完成的文字结果和图片落盘后再返回。图片独立按租户／服务商 UUID／任务 UUID／采样 UUID 存储，最多保留 90 天，到期立即拒绝访问，并由每分钟定时清理任务删除。截图缺失时 screenshotUrl 为 null；短期签名地址有效期最长一小时，也受密钥开关、有效期、接口开关和余额约束。链接到期请重新查询任务。第三方分享页可能需要登录；引用链接的 availability 表示最近检查状态，不保证第三方永久可用。

### 客户销售价与上游成本

上游报价仅用于内部成本记录，不能视为客户销售价。服务商专属销售价优先于租户默认销售价。已有平台迁移保留原网页 20 分、手机 34 分及截图 0 分；新增平台销售价未设置时拒绝受理。管理员可以修改每个平台的基础销售价和截图加价，以及每个 HTTP 接口的请求费（默认 0 分）。

采集总费 = 各采样基础销售价与所选截图加价之和 + `runs.create` 请求费。
金额单位为人民币整数分。`GET /usage` 的 `pricing.platforms` 与 `pricing.operations` 为权威价格；旧 `webCents/mobileCents` 仅兼容旧客户端，不能用于计算订单。

受理即扣款；失败、取消或截图缺失不自动退款。余额不足时不创建任务。事务同时提交任务、价格快照、扣款、流水、幂等记录，避免并发透支和重试重复扣费。相同客户可以跨密钥复用原幂等编号。

其他收费写入接口（创建品牌、添加问题）必须携带 `Idempotency-Key`；相同客户同接口同键同参重试不再扣费，异参返回 409。免费写入也建议携带此头。收费查询按每次成功受理的请求计费；HEAD 与 GET 使用相同接口价格。截图下载受理后按配置扣费，传输中断不自动退款。

### 上游异常

UPSTREAM_AUTH_ERROR：上游鉴权失败；UPSTREAM_BALANCE_ERROR：上游余额不足；UPSTREAM_REJECTED：请求被拒绝或平台暂不可用；UPSTREAM_NETWORK_ERROR / UPSTREAM_TIMEOUT：响应未确认或等待超时；UPSTREAM_SAMPLE_FAILED：没有有效回答；UPSTREAM_RESULT_MISMATCH：回答与请求不匹配。以上均不要由服务商自动创建新采集重试，应联系管理员核对。

停止任务时只能请求停止上游未分配的子任务，已运行任务可能继续完成并计费，不能据本地 cancelled 状态推断上游费用为零。

## 8. 身份与数据库隔离

服务商账户由服务端生成唯一 `customerId`，绑定到一个 `tenantId`。认证从密钥记录读取身份，不能通过请求体中的客户 ID 改变扣费对象。品牌通过 `api_customer_brands` 绑定客户，任务通过品牌归属做权限检查；幂等记录按客户分区，流水按客户与操作编号唯一约束。跨客户品牌授权被拒绝。

轮换密钥需在管理界面选择已有客户。新客户余额默认 0；管理员通过带 `operationId` 的核销操作充值，重复编号不重复入账。管理员可查看每个客户最近 200 笔流水、请求日志和价格快照。

`dailyUsage` 继续统计请求数，`limit` 固定为 0，`remaining` 为 null；旧每日限额字段被忽略，不返回次数限制响应头。接口开关、密钥状态、有效期和权限校验继续生效。

| 响应头 | 说明 |
| --- | --- |
| X-Request-Id | 请求追踪编号 |
| X-Billing-Charged-Cents | 本次实际扣费分数，幂等重试为 0 |
| X-Balance-Cents | 本次请求扣费后的余额 |

## 9. 管理员价格配置

管理端 `GET /api/access-keys/prices?customerId=UUID` 查看指定客户有效价，省略 customerId 返回默认价和内部成本。`PATCH /api/access-keys/prices` 支持以下数据：

```json
{"kind":"platform","itemId":"chatgpt","baseCents":100,"screenshotCents":20}
```

上例仅说明字段，不代表正式销售价。增加 `customerId` 设置某客户专属价。`kind=operation` 时 itemId 为 `runs.create` 等接口标识，baseCents 为每请求费用；`kind=cost` 时只修改租户的上游成本。客户专属价可使用 `reset:true` 恢复默认。配置事务持久化，重启后保留，历史成交价不变。

## 10. 截图接口

`GET /screenshots/{assetId}` 使用 Bearer 密钥或短期 ticket。图片按租户／客户／任务／采样存储；签名绑定密钥与单张图片，最大有效期 1 小时，图片保留 90 天。不存在返回 404，到期返回 410，无授权返回 403。授权成功后按 `screenshots.get` 接口销售价计费（默认 0 分）。


管理端余额流水和调用日志新增 platform_ids_json 平台快照（JSON 数组），以客户唯一 ID 和租户关联任务。多平台任务记录全部平台；非平台操作或无法可靠关联的历史记录为 []，界面显示 —。余额流水和调用日志 CSV 均增加 AI 平台列，包含名称及平台 ID。历史回填不修改余额或扣费金额。

余额流水与调用日志新增 question_texts_json（问题文本快照数组），界面及 CSV 的“客户提问”列同步展示。优先保存任务创建时的问题快照，不使用后来编辑的问题文本替换历史内容。重复采样去重，多问题完整保存；无法可靠关联的历史记录显示 —。已鉴权的直接提交若创建失败，也保存请求中的合法长度问题文本；未鉴权请求不保存正文。


## 商品卡采集
无需新增提交参数；支持平台的云端回答有商品时，任务详情中的 `answer.providerResult.goods` 返回商品列表。支持平台为 `baiduai`、`yuanbao`、`quark`、`doubao_mobile`、`qianwen_mobile`、`yuanbao_mobile`、`baidu_mobile`，可通过平台列表的 `goodsSupported` 查询。支持能力不保证每次回答出现商品。

每项字段：`title`、`url`、`thumbnail`、`price`、`priceUnit`、`mall`、`storeName`、`shopName`、`mallPlatform`、`mallProductId`、`skuId`、`position`。`position` 与正文 `[goods_card:x]` 对应，不应按数组下标匹配；缺失序号为 null。价格保留字符串，是商品标价，不是接口扣费。未返回商品时为 `[]`，旧结果可能缺少此字段，不会触发重新采集。商品随回答按原客户/任务权限保存与读取，无单独商品卡加价，原采集、截图及接口计费规则不变。


## 平台内容能力（2026-10-20）

当前结构化返回已接入商品和分享链接；视频、搜索词暂未作为结构化字段返回。分享链接须开启截图且平台支持，不保证每次回答均返回。

|平台|视频|商品|搜索词|分享链接|
|---|---|---|---|---|
|豆包|支持|不支持|支持|支持|
|百度文心|支持|支持|不支持|支持|
|元宝|支持|支持|不支持|支持|
|夸克|支持|支持|支持|不支持|
|微博智搜|支持|不支持|不支持|不支持|
|抖音AI|支持|不支持|支持|不支持|
|通义千问|支持|不支持|支持|支持|
|DeepSeek|不支持|不支持|支持|支持|
|Kimi|不支持|不支持|支持|支持|
|蚂蚁阿福|不支持|不支持|支持|支持|
|ChatGPT|不支持|不支持|不支持|不支持|
|Google AI|不支持|不支持|不支持|不支持|
|Bing AI|不支持|不支持|不支持|不支持|
|豆包 · 手机版|不支持|支持|支持|支持|
|通义千问 · 手机版|不支持|支持|支持|不支持|
|元宝 · 手机版|支持|支持|不支持|不支持|
|百度文心 · 手机版|支持|支持|不支持|不支持|
|DeepSeek · 手机版|不支持|不支持|支持|不支持|
|小红书点点 · 手机版|不支持|不支持|不支持|支持|


## 商品卡对接示例（服务商）

使用管理员签发的 API Key，账户需有余额，密钥需具备 read、monitor 权限。请求发送到我们的服务器，无需配置其他服务商账户，也无需创建品牌。

### 1. 提交采集

以下为 Bash / macOS / Linux 的 curl 示例；Windows 可将 JSON 请求体导入 Postman 或 Apifox。

```bash
curl -X POST 'https://118.89.28.187/api/v1/runs' \
  -H 'Authorization: Bearer YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -H 'Idempotency-Key: goods-order-20261020-001' \
  -d '{
    "prompt": "推荐几款适合通勤的降噪蓝牙耳机，请展示商品卡、价格和购买链接。",
    "platform": "doubao_mobile",
    "platformOptions": {
      "doubao_mobile": { "screenshot": true }
    }
  }'
```

商品卡无需额外开关。screenshot 控制截图，不保证生成商品卡。也可使用 qianwen_mobile；切换时须同步修改 platformOptions 的平台键。新业务使用新的 Idempotency-Key，同一业务网络重试须保持编号与参数完全一致。受理即扣款，失败或取消不自动退款。

受理响应示意（HTTP 202，字段节选）：

```json
{"id":"RUN_ID","status":"queued","reused":false}
```

保存 id。HTTP 202 只代表受理，不代表采集完成；出现超时不要换新编号盲目重提。

### 2. 轮询结果

```bash
curl 'https://118.89.28.187/api/v1/runs/RUN_ID' \
  -H 'Authorization: Bearer YOUR_API_KEY'
```

将 RUN_ID 替换为提交返回的 id。每 5～10 秒查询一次，queued、running 继续等待；completed、partial_failed、failed、cancelled 时停止轮询，并逐项检查 platformRuns[].status、errorCode、errorMessage。查询按该客户接口价格配置计费（默认免费），不要无限高速轮询。

### 3. 读取商品和截图

读取 platformRuns[].answer.providerResult.goods，先判断 answer 是否为 null。商品字段示意如下，仅用于说明格式，不是真实采集结果：

```json
{
  "content": "推荐商品：[goods_card:1]",
  "providerResult": {
    "goods": [{
      "title": "示例降噪耳机",
      "price": "299.00",
      "priceUnit": "¥",
      "url": "https://example.com/product/123",
      "thumbnail": "https://example.com/product/123.jpg",
      "mall": "示例商城",
      "storeName": "示例店铺",
      "shopName": "示例店铺",
      "mallPlatform": "example",
      "mallProductId": "123",
      "skuId": "00123",
      "position": 1
    }]
  }
}
```

position 对应正文 [goods_card:x]，不要按数组下标配对。price 是字符串商品标价，不是接口扣费金额。goods 为 [] 表示本次没有返回商品卡；旧记录可能缺少 goods 字段。平台支持不代表每次问题必然产生商品卡。

截图读取 platformRuns[].answer.screenshotUrl；为空时检查 screenshotStatus。签名链接到期后重新查询原任务获取新链接，不必重新采集。商品详情、商品图片及分享链接可能指向第三方网站。

任务结果由我们的服务器保存、整理后返回，查询已完成任务不触发新的上游采集。商品卡沿用原采集、截图及接口计费规则，无单独商品卡加价。
