本文记录 @gitcoffee-os/postbot-cli 实际使用的 exmay-postbot-engine 接口、字段和行为差异。实现位于 packages/sdk/postbot-sdk,CLI 只负责参数解析、预检和展示。
| 项 | 值 |
|---|---|
| API 根地址 | https://api.exmay.com/exmay/authority/api(Postar 使用对应站点) |
| 认证头 | Authorization: Bearer <token> |
| 应用头 | App-Id: <appId> |
| 内容类型 | Content-Type: application/json |
| 默认超时 | 30 秒 |
环境变量 POSTBOT_API_URL / POSTAR_API_URL 可覆盖完整 API 根地址(必须包含 /exmay/authority/api)。
引擎多数接口返回 HTTP 200,业务结果在响应体:
{ "code": "SUCCESS", "msg": "ok", "data": {} }- 成功码:
SUCCESS、0、"0"、缺省code。 - 失败:HTTP 200 且
code为业务错误码/数值码;SDK 会抛出IamApiError,保留code。 - 非 2xx:SDK 抛出带 HTTP 状态和响应片段的错误。
NOT_LOGIN/USER.TOKEN_EXPIRED/DEVELOPER.TOKEN_EXPIRED等由 CLI 归类后提示重新登录。
| SDK 方法 | 方法 / 路径 | 说明 |
|---|---|---|
TaskApi.list |
GET /postbot/media/task/list?page=&size= |
分页;引擎只按用户过滤,不实现 status 过滤 |
TaskApi.listAll |
GET /postbot/media/task/listing |
全量;CLI 用于状态/平台/关键字筛选 |
TaskApi.get |
GET /postbot/media/task/load?id= |
校验归属 |
TaskApi.create |
POST /postbot/media/task/save |
新建;isAutoPublish=true 时保存后立即编排 Job |
TaskApi.update |
PUT /postbot/media/task/update |
需含 id;见第 3 节注意事项 |
TaskApi.delete |
DELETE /postbot/media/task/remove |
请求体 { id } |
TaskApi.batchDelete |
DELETE /postbot/media/task/batch_remove?ids=1,2 |
查询参数,不是 JSON body |
TaskApi.bindContent |
POST /postbot/media/task/bind_content |
{ taskId, contentId },幂等 |
| SDK 方法 | 方法 / 路径 | 说明 |
|---|---|---|
AutoPublishApi.trigger |
POST /postbot/autopublish/task/{taskId}/trigger |
忽略定时时间立即编排;已有活跃 Job 的平台跳过 |
AutoPublishApi.getStatus |
GET /postbot/autopublish/task/{taskId}/status |
最新一条 Job 或 null |
AutoPublishApi.listJobs |
GET /postbot/autopublish/job/list |
返回当前用户全部 Job;SDK 在客户端按 taskId/status/platformCode/limit 过滤 |
AutoPublishApi.listLogs |
GET /postbot/autopublish/log/list |
返回全部日志;SDK 客户端过滤 |
| SDK 方法 | 方法 / 路径 |
|---|---|
PlatformApi.list / listPaged |
GET /postbot/media/platform/listing / /list |
PlatformApi.get |
GET /postbot/media/platform/load?id= |
AccountApi.list / listPaged |
GET /postbot/media/account/listing / /list |
AccountApi.get |
GET /postbot/media/account/load?id= |
AccountApi.findBySession |
GET /postbot/media/account/find_by_session?sessionbotSessionId= |
AccountApi.save |
POST /postbot/media/account/save(无返回体) |
AccountApi.update |
PUT /postbot/media/account/update |
AccountApi.remove |
DELETE /postbot/media/account/remove(body { id }) |
AccountApi.batchRemove |
DELETE /postbot/media/account/batch_remove?ids= |
ClientApi.list / listPaged |
GET /postbot/media/client/listing / /list |
ClientApi.get |
GET /postbot/media/client/load?id= |
ClientApi.register |
POST /postbot/media/client/save(无返回体) |
ClientApi.update |
PUT /postbot/media/client/update |
ClientApi.remove / batchRemove |
DELETE .../remove(body)/ .../batch_remove?ids= |
| SDK 方法 | 方法 / 路径 |
|---|---|
FingerprintApi.load |
GET /postbot/media/account/fingerprint/load?mediaAccountId= |
FingerprintApi.save |
POST /postbot/media/account/fingerprint/save |
FingerprintApi.remove |
DELETE /postbot/media/account/fingerprint/remove?mediaAccountId= |
SessionApi.list / listPaged |
GET /sessionbot/session/listing / /list |
SessionApi.get |
GET /sessionbot/session/load?id=<bizId> |
NotifyApi.send |
POST /postbot/notify |
WorkflowApi.listDefinitions |
GET /workflow/definitions |
WorkflowApi.executeWorkflow |
POST /workflow/execute |
WorkflowApi.getExecutionStatus |
GET /workflow/execution/{id} |
WorkflowApi.retryExecution |
POST /workflow/execution/{id}/retry |
interface TaskSaveRequest {
id?: number;
name?: string;
type?: string; // article | moment | video | audio
mediaPlatformId?: number | null;
mediaAccountId?: number | null;
mediaClientId?: number | null;
contentId?: string;
data?: string | Record<string, any>;
platformCodes?: string[]; // 与 data.platformCodes 合并去重、别名归一化
isAutoPublish?: boolean;
config?: string | Record<string, any>;
publishChannel?: string; // CLOUD | BROWSER_EXTENSION | BROWSER_EXTENSION_EPHEMERAL | DESKTOP
scheduledAt?: string; // Jackson LocalDateTime,建议 "yyyy-MM-ddTHH:mm:ss"
maxRetryCount?: number;
status?: string;
}- 解析
data(对象或 JSON 字符串)并统一存为 JSON 字符串。 - 合并顶层
platformCodes与data.platformCodes,做别名归一化、去重。 - 若未传
scheduledAt,从data.onlineTime解析;支持yyyy-MM-dd HH:mm:ss、yyyy-MM-dd'T'HH:mm:ss、yyyy-MM-dd HH:mm、纯日期、毫秒时间戳、空串(立即)。 - 由
isAutoPublish+ 定时时间推导status:草稿DRAFT;未来时间SCHEDULED;否则PENDING。 isAutoPublish=true时立即为每个目标平台创建 Job;Job 初始状态统一为PENDING,即使定时时间在未来。
PUT /update 不是 PATCH:
updateById忽略 null 字段,但保存前会执行fillPublishParams;缺省publishChannel会重置为CLOUD,缺省status会按定时时间重置为PENDING/SCHEDULED。- 因此 CLI 先
GET /load,合并本次修改后整体回传。 mediaClientId使用 MyBatisNOT_NULL更新策略,无法通过/update清空;从DESKTOP切到CLOUD时 CLI 会报错,建议新建任务。- 端点解析器在
mediaClientId存在时会按客户端类型覆盖publishChannel;CLI 在绑定客户端时按此规则校验。 - 修改
scheduledAt不会更新已创建 Job 的定时时间。要按新时间执行,需取消/重建任务或等待旧 Job 处理;trigger只对缺少活跃 Job 的平台补建。
保存/更新后,ExmayPostbotMediaTaskAutoPublishHandler 在插入和更新时都会触发自动发布处理:
- 读取任务
platformCodes与data。 - 对每个目标平台查平台字典;平台不存在则跳过并记录。
- 解析该平台的媒体账号(优先任务
mediaAccountId/mediaPlatformId,否则按平台自动选账号)。 - 已有
PENDING/RUNNING/FAILED_RETRYABLEJob 的平台不重复创建。 - 创建 Job,写入平台、账号、客户端、通道、定时时间与任务快照。
常见“保存成功但没有 Job”原因:
- 平台 code 不在引擎字典(尤其是数据库仍用
xhs/channels旧码); - 自动发布目标平台没有可用账号;
DESKTOP通道未绑定mediaClientId,canAutoPublish=false;- 已有同平台活跃 Job,属于幂等跳过。
| 通道 | 执行方 | 客户端绑定 |
|---|---|---|
CLOUD |
云端 Worker | 不需要;传入客户端会被端点解析器改通道,CLI 拒绝 |
BROWSER_EXTENSION |
已保存的浏览器扩展 | 可指定 mediaClientId;不指定时由扩展侧领取 |
BROWSER_EXTENSION_EPHEMERAL |
服务端临时浏览器 | 不可绑定客户端;由服务端执行 |
DESKTOP |
桌面端 | 必须绑定在线桌面端 mediaClientId |
在线判定:lastOnlineAt 距当前不超过 120 秒(客户端心跳周期 90 秒,留余量)。
任务级聚合状态使用工作流:
{ "workflowType": "STATUS_QUERY", "input": { "taskId": 123 } }返回 task、latestJob、recentLogs、summary 等。注意:
latestJob是精简字段(id/status/channel/times/retry),完整 Job 需再查/autopublish/task/{id}/status。- 任务聚合状态可能因平台多 Job 而取“最差/进行中”组合;CLI 会优先展示任务自身
status,并展示 Job/日志。
引擎代码中的标准码(normalizePlatformCode)与 Worker 发布器 key 一致:
| 标准码 | 历史别名 |
|---|---|
xiaohongshu |
xhs |
weixin_channels |
channels、wechat_channels |
weixin |
weixinmp、wechat_mp |
xianyu |
无(CLI 额外接受 goofish / idlefish) |
但仓库内 V1_3_1__media_platform_init.sql 仍插入 xhs、channels、xianyu。如果数据库平台字典没有升级:
- 保存请求里的
xiaohongshu会被归一化,但按 code 查平台时可能找不到; - Job 不会创建,任务看似“保存成功”。
核对命令:
postbot platform list
postbot platform get xiaohongshu修复方向:把 exmay_postbot_media_platform.code 更新为标准码(同时保留别名兼容),或让部署脚本按实际字典同步。