feat(gateway): 下单任务支持 callback_url 终结类事件异步通知
- POST /api/orders 新增可选 callback_url(仅 http/https,其余 422) - 仅终结类事件各通知一次:terminal report 推入终态(succeeded/failed/ needs_human)、租约过期被 sweep 置 stale;中间态与终结后的监控上报不通知 - 幂等重发不更新既有任务的回调地址;投递 best-effort 单次尝试,失败只记日志 - CallbackNotifier 发后不管(create_task + 在途任务强引用),关停等在途发完; 生产路径按回调地址逐次构造客户端,满足统一出站代理策略(test_proxy.py) - tasks 表加 callback_url 列,GatewayDB.start() 内置迁移兼容既有库 - 新增 RAKUTEN_CALLBACK_TIMEOUT_SECONDS(默认 10);文档补 §4.8; openapi.json 重新导出(gitignore 未跟踪);新增 17 条测试,全量 492 通过
This commit is contained in:
+15
-2
@@ -25,6 +25,7 @@ CREATE TABLE IF NOT EXISTS tasks (
|
||||
task_id TEXT PRIMARY KEY,
|
||||
site TEXT NOT NULL,
|
||||
intent_json TEXT NOT NULL,
|
||||
callback_url TEXT, -- 终结类事件通知地址,见 docs/order-gateway.md §4.8
|
||||
status TEXT NOT NULL,
|
||||
lease_owner TEXT,
|
||||
lease_expires_at TEXT,
|
||||
@@ -98,6 +99,7 @@ class TaskRow:
|
||||
task_id: str
|
||||
site: str
|
||||
intent_json: str
|
||||
callback_url: str | None
|
||||
status: str
|
||||
lease_owner: str | None
|
||||
lease_expires_at: str | None
|
||||
@@ -180,6 +182,7 @@ def _row_to_task(row: aiosqlite.Row) -> TaskRow:
|
||||
task_id=row["task_id"],
|
||||
site=row["site"],
|
||||
intent_json=row["intent_json"],
|
||||
callback_url=row["callback_url"],
|
||||
status=row["status"],
|
||||
lease_owner=row["lease_owner"],
|
||||
lease_expires_at=row["lease_expires_at"],
|
||||
@@ -256,9 +259,18 @@ class GatewayDB:
|
||||
self._conn = await aiosqlite.connect(str(self._db_path))
|
||||
self._conn.row_factory = aiosqlite.Row
|
||||
await self._conn.executescript(SCHEMA)
|
||||
await self._migrate()
|
||||
await self._conn.commit()
|
||||
logger.info("网关 DB 已就绪:%s", self._db_path)
|
||||
|
||||
async def _migrate(self) -> None:
|
||||
"""给既有库补新列(CREATE TABLE IF NOT EXISTS 不会改老表)"""
|
||||
async with self.conn.execute("PRAGMA table_info(tasks)") as cur:
|
||||
columns = {row["name"] for row in await cur.fetchall()}
|
||||
if "callback_url" not in columns:
|
||||
await self.conn.execute("ALTER TABLE tasks ADD COLUMN callback_url TEXT")
|
||||
logger.info("迁移:tasks 表补充 callback_url 列")
|
||||
|
||||
async def close(self) -> None:
|
||||
if self._conn is not None:
|
||||
await self._conn.close()
|
||||
@@ -281,13 +293,14 @@ class GatewayDB:
|
||||
"""插入新任务。返回 True=新建,False=task_id 已存在(幂等命中)"""
|
||||
try:
|
||||
await self.conn.execute(
|
||||
"INSERT INTO tasks (task_id, site, intent_json, status, "
|
||||
"INSERT INTO tasks (task_id, site, intent_json, callback_url, status, "
|
||||
"lease_owner, lease_expires_at, lease_count, created_at, updated_at) "
|
||||
"VALUES (?, ?, ?, ?, NULL, NULL, 0, ?, ?)",
|
||||
"VALUES (?, ?, ?, ?, ?, NULL, NULL, 0, ?, ?)",
|
||||
(
|
||||
row.task_id,
|
||||
row.site,
|
||||
row.intent_json,
|
||||
row.callback_url,
|
||||
row.status,
|
||||
row.created_at,
|
||||
row.updated_at,
|
||||
|
||||
Reference in New Issue
Block a user