chore: auto release commit (2026-07-03 12:09:55)
All checks were successful
技能自动化发布 / release (push) Successful in 5s
All checks were successful
技能自动化发布 / release (push) Successful in 5s
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
"""任务编排、日志查询模板。
|
||||
|
||||
通用业务编排层:负责参数校验、鉴权、调用具体业务实现、写入任务日志。
|
||||
复制后在 cmd_run 中实现真正的业务逻辑。
|
||||
"""
|
||||
"""任务编排:下载 Amazon 结算报表、写入 task_logs。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import uuid
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
@@ -16,71 +13,186 @@ from jiangchang_skill_core import collect_runtime_diagnostics, config, format_ru
|
||||
from jiangchang_skill_core.rpa.video_session import RpaVideoSession
|
||||
|
||||
from db import task_logs_repository as tlr
|
||||
from service.amazon_report_adapter import get_adapter
|
||||
from service.entitlement_service import check_entitlement
|
||||
from service.task_run_support import (
|
||||
_print_video_summary,
|
||||
build_video_info,
|
||||
merge_video_into_result_summary,
|
||||
)
|
||||
from util.constants import PLATFORM_KIT_MIN_VERSION, SKILL_SLUG, SKILL_VERSION
|
||||
from util.constants import (
|
||||
DEFAULT_DATE_FROM,
|
||||
DEFAULT_DATE_TO,
|
||||
PLATFORM_KIT_MIN_VERSION,
|
||||
SKILL_SLUG,
|
||||
SKILL_VERSION,
|
||||
)
|
||||
from util.runtime_paths import get_skill_data_dir, get_skill_root
|
||||
from util.timeutil import unix_to_iso
|
||||
|
||||
|
||||
async def _run_template_demo(target: Optional[str], input_id: Optional[str]) -> tuple[int, dict[str, Any]]:
|
||||
"""最小 RpaVideoSession 示范:不启动浏览器、不执行业务,仅演示录屏包裹范式。"""
|
||||
batch_id = uuid.uuid4().hex[:12]
|
||||
_DATE_RE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
||||
|
||||
|
||||
def _validate_date(label: str, value: str) -> Optional[str]:
|
||||
v = (value or "").strip()
|
||||
if not v:
|
||||
return f"{label} 不能为空"
|
||||
if not _DATE_RE.match(v):
|
||||
return f"{label} 格式须为 YYYY-MM-DD"
|
||||
return None
|
||||
|
||||
|
||||
def _artifacts_dir(batch_id: str) -> str:
|
||||
return os.path.join(get_skill_data_dir(), "rpa-artifacts", batch_id)
|
||||
|
||||
|
||||
def _downloads_dir() -> str:
|
||||
path = os.path.join(get_skill_data_dir(), "downloads")
|
||||
os.makedirs(path, exist_ok=True)
|
||||
return path
|
||||
|
||||
|
||||
async def _run_with_video(
|
||||
*,
|
||||
date_from: str,
|
||||
date_to: str,
|
||||
seller_code: Optional[str],
|
||||
batch_id: str,
|
||||
) -> tuple[dict[str, Any], dict[str, Any]]:
|
||||
"""可选 RpaVideoSession 包裹 adapter 执行。"""
|
||||
data_dir = get_skill_data_dir()
|
||||
art_dir = _artifacts_dir(batch_id)
|
||||
os.makedirs(art_dir, exist_ok=True)
|
||||
|
||||
async with RpaVideoSession(
|
||||
skill_slug=SKILL_SLUG,
|
||||
skill_data_dir=data_dir,
|
||||
batch_id=batch_id,
|
||||
title="开始执行示例任务",
|
||||
closing_title="示例任务执行完成",
|
||||
title="开始下载 Amazon 结算报表",
|
||||
closing_title="结算报表下载完成",
|
||||
) as video:
|
||||
video.add_step("准备执行示例任务")
|
||||
if target:
|
||||
video.add_step(f"接收目标参数:{target}")
|
||||
if input_id:
|
||||
video.add_step(f"接收输入标识:{input_id}")
|
||||
video.add_step("示例任务执行完成")
|
||||
video.add_step("准备下载结算报表")
|
||||
video.add_step(f"日期范围:{date_from} 至 {date_to}")
|
||||
if seller_code:
|
||||
video.add_step(f"指定店铺:{seller_code}")
|
||||
|
||||
adapter = get_adapter(artifacts_dir=art_dir)
|
||||
video.add_step(f"使用 adapter:{adapter.name}")
|
||||
|
||||
result = adapter.download_settlement(
|
||||
date_from=date_from,
|
||||
date_to=date_to,
|
||||
seller_code=seller_code,
|
||||
downloads_dir=_downloads_dir(),
|
||||
artifacts_dir=art_dir,
|
||||
)
|
||||
|
||||
if result.ok:
|
||||
video.add_step(f"下载成功:{result.filename or ''}")
|
||||
else:
|
||||
video.add_step(f"下载失败:{result.error_msg or '未知错误'}")
|
||||
|
||||
video_summary = video.summary()
|
||||
video_info = build_video_info(video_summary, {}, {})
|
||||
return 1, video_info
|
||||
payload: dict[str, Any] = {
|
||||
"ok": result.ok,
|
||||
"job_id": result.job_id,
|
||||
"file_path": result.file_path,
|
||||
"filename": result.filename,
|
||||
"date_from": result.date_from,
|
||||
"date_to": result.date_to,
|
||||
"seller_code": result.seller_code or seller_code,
|
||||
"adapter": result.artifacts.get("adapter") or adapter.name,
|
||||
"artifacts": result.artifacts,
|
||||
}
|
||||
if not result.ok:
|
||||
payload["error"] = result.error_msg
|
||||
return payload, video_info
|
||||
|
||||
|
||||
def cmd_run(target: Optional[str] = None, input_id: Optional[str] = None) -> int:
|
||||
"""通用任务执行入口模板。复制后请实现真实业务逻辑。"""
|
||||
def cmd_run(
|
||||
date_from: Optional[str] = None,
|
||||
date_to: Optional[str] = None,
|
||||
seller_code: Optional[str] = None,
|
||||
) -> int:
|
||||
ok, reason = check_entitlement(SKILL_SLUG)
|
||||
if not ok:
|
||||
print(f"❌ {reason}")
|
||||
print(f"ERROR:ENTITLEMENT {reason}")
|
||||
return 1
|
||||
|
||||
rc, video_info = asyncio.run(_run_template_demo(target, input_id))
|
||||
_print_video_summary(video_info)
|
||||
d_from = (date_from or DEFAULT_DATE_FROM).strip()
|
||||
d_to = (date_to or DEFAULT_DATE_TO).strip()
|
||||
seller = (seller_code or "").strip() or None
|
||||
|
||||
summary_payload = merge_video_into_result_summary(
|
||||
{
|
||||
"template_demo": True,
|
||||
"target": target,
|
||||
"input_id": input_id,
|
||||
},
|
||||
video_info,
|
||||
)
|
||||
for label, val in (("date_from", d_from), ("date_to", d_to)):
|
||||
err = _validate_date(label, val)
|
||||
if err:
|
||||
print(f"ERROR:INVALID_ARGS {err}")
|
||||
return 1
|
||||
|
||||
batch_id = uuid.uuid4().hex[:12]
|
||||
record_video = config.get_bool("OPENCLAW_RECORD_VIDEO", default=True)
|
||||
|
||||
if record_video:
|
||||
payload, video_info = asyncio.run(
|
||||
_run_with_video(
|
||||
date_from=d_from,
|
||||
date_to=d_to,
|
||||
seller_code=seller,
|
||||
batch_id=batch_id,
|
||||
)
|
||||
)
|
||||
else:
|
||||
art_dir = _artifacts_dir(batch_id)
|
||||
os.makedirs(art_dir, exist_ok=True)
|
||||
adapter = get_adapter(artifacts_dir=art_dir)
|
||||
result = adapter.download_settlement(
|
||||
date_from=d_from,
|
||||
date_to=d_to,
|
||||
seller_code=seller,
|
||||
downloads_dir=_downloads_dir(),
|
||||
artifacts_dir=art_dir,
|
||||
)
|
||||
payload = {
|
||||
"ok": result.ok,
|
||||
"job_id": result.job_id,
|
||||
"file_path": result.file_path,
|
||||
"filename": result.filename,
|
||||
"date_from": result.date_from,
|
||||
"date_to": result.date_to,
|
||||
"seller_code": result.seller_code or seller,
|
||||
"adapter": result.artifacts.get("adapter") or adapter.name,
|
||||
"artifacts": result.artifacts,
|
||||
}
|
||||
if not result.ok:
|
||||
payload["error"] = result.error_msg
|
||||
video_info = {}
|
||||
|
||||
_print_video_summary(video_info if record_video else None)
|
||||
|
||||
summary_payload = merge_video_into_result_summary(payload, video_info if record_video else None)
|
||||
status = "success" if payload.get("ok") else "failed"
|
||||
tlr.save_task_log(
|
||||
task_type="demo",
|
||||
target_id=target,
|
||||
input_id=input_id,
|
||||
input_title="模板示例任务",
|
||||
status="failed",
|
||||
error_msg="模板仓库未实现真实业务",
|
||||
task_type="download_settlement",
|
||||
target_id=seller or "default",
|
||||
input_id=f"{d_from}_{d_to}",
|
||||
input_title=f"结算报表 {d_from} ~ {d_to}",
|
||||
status=status,
|
||||
error_msg=None if payload.get("ok") else str(payload.get("error") or ""),
|
||||
result_summary=json.dumps(summary_payload, ensure_ascii=False),
|
||||
)
|
||||
|
||||
print("❌ 这是模板仓库,请复制后在 scripts/service/task_service.py 中实现 cmd_run 的真实业务逻辑。")
|
||||
return rc
|
||||
stdout_payload = {
|
||||
"ok": payload.get("ok"),
|
||||
"job_id": payload.get("job_id"),
|
||||
"file_path": payload.get("file_path"),
|
||||
"filename": payload.get("filename"),
|
||||
"date_from": payload.get("date_from"),
|
||||
"date_to": payload.get("date_to"),
|
||||
}
|
||||
print(json.dumps(stdout_payload, ensure_ascii=False))
|
||||
return 0 if payload.get("ok") else 1
|
||||
|
||||
|
||||
def cmd_logs(
|
||||
|
||||
Reference in New Issue
Block a user