openydt-api-explorer — 通用 API 兜底 / 接口探索
CRITICAL:开始前 MUST 先用 Read 工具读取
../openydt-shared/SKILL.md(认证 / profile / 签名 / 状态码 / 限速 / 安全规则)。openydt api与一等命令走完全相同的签名、包络、退出码与限速逻辑,未读共享基座不要执行任何命令。
何时用本技能
平台接口众多,其中多数已做成域一等命令(openydt <域> <命令>,参数已结构化为 flag),仍有不少未一等化(权威计数见 make counts / INTERFACE_INDEX.md,勿硬记数字)。当你要调的接口没有专属子命令时,用本技能的通用兜底:
openydt api <cmd> --body '{...}'
api 对任意可调用(direction=callable)的业务编码 cmd 自动签名并 POST,覆盖一等命令没点名的所有域,例如:
cityOperationCoupon城市运营券(创建/发放城市运营券模板)thirdParkForBolian第三方车场缴费接入回执upward上行数据上报回执(如asynSuccess)community小区门禁(如getAuthCommunities)ad广告统计、preferential/score积分兑换、invoice发票、ydtUser用户认证等- 有专属域、但子特性未一等化的接口:月票预约/排队(
ticket·appointment)、月票授权访客(ticket·authorize)、月票证件规则(ticket·certificate)、车辆标签(parking·tag:addCarTags/delCarTags)——monthticket / record 域技能会把这些指向本技能,同样用api调(写操作记得--yes)
这些域在 catalog.json 里 included=false(没生成一等命令),但只要 direction=callable 就能用 api 直接调。
选择顺序:先找一等命令(
openydt <域> --help或对应域技能),找不到再用api兜底。一等命令把参数拆成了 flag、自动判定读写、更不易出错;api是“原始 JSON 直发”,更通用但要你自己保证 body 正确。
用法:openydt api
# 1) 行内 JSON body(最常用)
openydt api getParkFee --body '{"parkCode":"1ZS7H5PQH9","carCode":"粤EJW962"}'
# 2) 无参接口可省略 body
openydt api getAuthParkCodes
# 3) 从文件读 body
openydt api getParkOnSiteCar --body-file ./body.json
# 4) 从 stdin 读 body(- 表示 stdin),适合管道 / 大 body
echo '{"parkCode":"PTD2YBBZ"}' | openydt api getParkOnSiteCar --body-file -
<cmd>:业务编码,就是 catalog 里的cmd字段(如getParkFee、createCityOperationCouponTemplate)。注意是 cmd,不是dir路径。--body与--body-file互斥;二者都不给则发送空 body({}),仅适合无参接口。- body 是原始 JSON:CLI 会先做 JSON compact 再用于签名与发送(与一等命令一致),字符串内部空格如
"2019-04-16 00:11:25"会保留。 - 参数命名与嵌套结构完全照搬接口定义(见下文从 catalog 查参数);写错字段名通常返回
status=2 / resultCode=909 请求参数错误或status=7 请求参数不完整。
--dry-run 预览
不确定 body 或在 prod 环境前,先用 --dry-run 只打印将发送的签名请求(URL / sign / ts / compact 后的 body),不实际发送:
openydt api createCityOperationCouponTemplate --dry-run \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","faceValue":1}}'
示例 parkCode/时间为文档化测试值(仅 test);照抄 catalog 历史 sampleBody 会撞无效车场/过期有效期。
写操作必须 --yes(api 与一等命令共用写守护)
api 与一等命令现在走同一个写守护——RunCall 对 catalog readwrite=write 的 cmd 统一要求 --yes(或 --dry-run):
- 漏
--yes会被拦,不会真的发出去;系统会提示「是写操作,需加 --yes 确认」。 - 全局
--read-only(或环境变量OPENYDT_READ_ONLY=1)下,任何写 cmd(含api)一律被拒绝。 - 凡会改变平台状态的 cmd(建/改/删、发券、缴费、开闸、上报回执等),仍建议先
--dry-run预览签名请求,确认无误再--yes实发——双重保障,prod 尤其重要。 - 判断 cmd 读写:看 catalog 的
readwrite字段(见下文「从 catalog 查」)。写操作的幂等/重试见 [[openydt-shared]] 的references/write-idempotency.md。
# 写操作(catalog readwrite=write),必须先 --dry-run 预览,再 --yes 实发
openydt api createCityOperationCouponTemplate --dry-run \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","totalNum":2,"couponType":1,"faceValue":1,"validFrom":"2026-06-01 00:00:00","validTo":"2027-06-01 00:00:00"}}'
openydt api createCityOperationCouponTemplate --yes \
--body '{"parkCodeList":["1ZS7H5PQH9"],"couponTemplate":{"name":"抵扣1元券","totalNum":2,"couponType":1,"faceValue":1,"validFrom":"2026-06-01 00:00:00","validTo":"2027-06-01 00:00:00"}}'
从 catalog 查可用 cmd 及参数
接口清单在 ../../catalog/catalog.json(绝对路径 /Users/zhoujw/develop/tmp/openydt-cli/catalog/catalog.json)。顶层结构 {generatedFrom, count, interfaces:[...]},每个 interface 对象关键字段:
| 字段 | 含义 |
|---|---|
cmd |
业务编码,直接作为 openydt api <cmd> 的 cmd |
domain / dir |
所属域 / 文档路径(仅供归类,不是 api 的入参) |
direction |
callable=可主动调 / webhook=平台主动推送(见下节,不能调) |
readwrite |
read / write——决定调用时是否要加 --yes |
included |
是否已做成一等命令;false 表示要用 api 兜底 |
excludeReason |
未做成一等命令的原因(out-of-scope-domain / deprecated / no-endpoint / vems-only 等) |
params |
参数定义数组:name / required / type / desc / group(group 非空表示该参数嵌在某个子对象里) |
sampleBody |
官方示例请求 body——构造 --body 的最佳起点,照抄字段名再改值 |
sampleResponse |
示例响应,帮你预判返回字段 |
用 jq / python3 检索(不要在终端打印密钥,这里只查清单不涉及凭据):
# 按 cmd 看完整定义(params + sampleBody)
jq '.interfaces[] | select(.cmd=="createCityOperationCouponTemplate")' catalog/catalog.json
# 列出某个未点名域所有“可调用”的 cmd 及读写
jq -r '.interfaces[] | select(.domain=="cityOperationCoupon" and .direction=="callable") | "\(.cmd)\t\(.readwrite)\t\(.explain)"' catalog/catalog.json
# 全量“没做成一等命令但可调用”的接口(included=false 且 callable)
jq -r '.interfaces[] | select(.included==false and .direction=="callable") | "\(.domain)\t\(.cmd)\t\(.readwrite)"' catalog/catalog.json
# 只看某 cmd 的参数清单(含嵌套 group)
jq '.interfaces[] | select(.cmd=="createCityOperationCouponTemplate") | .params' catalog/catalog.json
included=false 但 direction=callable 的接口照样能用 api 调——常见于城市运营券、第三方车场接入(thirdParkForBolian)、上行回执(upward)、小区门禁(community)、广告/积分/发票/ydtUser 等“未点名域”。included=false 只是“没生成专属命令”,不代表“不能调”;判断能否调用看 direction,判断要不要 --yes 看 readwrite。
构造流程:① jq 取目标 cmd 的 sampleBody → ② 照抄字段名、按 params 校对必填/类型/嵌套 → ③ --dry-run 预览签名请求 → ④ 写操作加 --yes 正式发。
错误自愈速查(api 兜底常见)
| 现象 | 含义 | 恢复动作 |
|---|---|---|
status=9 接口不存在 |
cmd 拼错 / 该 cmd 是 webhook(不可主动调) | `jq '.interfaces[] |
status=2 resultCode=909 / status=7 |
body 字段名/必填错 | 按 catalog 该 cmd 的 params 逐项核对必填与嵌套 group,用 sampleBody 起手 |
写 cmd 漏 --yes 被拦「是写操作,需加 --yes」 |
RunCall 写守护(api 与一等命令共用) | 先 --dry-run 预览,确认是写 cmd 再 --yes |
通用码与退出码、重试语义见 [[openydt-shared]];幂等键见其
references/write-idempotency.md。
不能调用的 webhook(平台主动推送)
catalog 中 direction=webhook 的接口(如 reportParkinglotChange 车位变更上报;数量见 make counts)是平台主动 POST 到你方接收端的回调,方向与 callable 相反:
- CLI 不能主动调这些 cmd——它们没有“你方请求平台”的入口,
openydt api <webhook-cmd>不是正确用法(平台不提供该方向端点)。 - 要接收这类推送,需你自建一个 HTTP 接收端(webhook receiver),向平台登记回调地址,由平台在事件发生时把
sampleBody形态的数据 POST 给你;你的服务负责验签、处理并按约定返回(很多上行回执对应一个upward域的 callable 确认 cmd,如asynSuccess)。 - 区分方法:调用前先
jq '.interfaces[]|select(.cmd=="<cmd>")|.direction',是webhook就别用api调,改为自建接收端;是callable才用api。
示例
查某未点名域有哪些可调 cmd(读,纯查 catalog,不发请求):
jq -r '.interfaces[] | select(.domain=="thirdParkForBolian" and .direction=="callable") | "\(.cmd)\t\(.readwrite)"' catalog/catalog.json
用 sampleBody 起手、先预览再发(写操作示例,正式发要加 --yes):
# 1) 取示例 body
jq -r '.interfaces[]|select(.cmd=="createCityOperationCouponTemplate")|.sampleBody' catalog/catalog.json
# 2) 预览签名请求,确认无误
openydt api createCityOperationCouponTemplate --dry-run --body-file ./body.json
# 3) 正式发(写操作,必须 --yes)
openydt api createCityOperationCouponTemplate --yes --body-file ./body.json
读接口直接调(无需 --yes):
echo '{}' | openydt api getAuthCommunities --body-file - # community 域,readwrite=read