chore: auto release commit (2026-09-14 15:11:41)
All checks were successful
技能自动化发布 / release (push) Successful in 9s

This commit is contained in:
chenfuqiang
2026-09-14 15:11:41 +08:00
parent ce96693564
commit c5103e2650
17 changed files with 2626 additions and 2 deletions

View File

@@ -697,3 +697,292 @@ def cmd_stats() -> int:
log.exception("task_failed task_type=stats")
print("ERROR:QUERY_FAILED 统计查询异常,详见统一日志")
return 1
# ---------------------------------------------------------------------------
# RPA 命令:发票信息提取
# ---------------------------------------------------------------------------
def cmd_extract_invoices(
username: str,
password: str,
profile_dir: str,
headless: bool = False,
detail_mode: bool = False,
) -> int:
"""从网站提取发票信息。"""
log = _get_task_logger()
task_type = "extract_invoices"
with job_context(skill=SKILL_SLUG):
log.info(
"task_start task_type=%s username=%s detail_mode=%s",
task_type,
username[:3] + "***",
detail_mode,
)
emit("开始提取发票信息", skill=SKILL_SLUG, stage="rpa_start")
try:
ok, reason = check_entitlement(SKILL_SLUG)
if not ok:
log.warning(
"entitlement_failed task_type=%s stage=auth error_code=ENTITLEMENT_DENIED",
task_type,
)
emit("鉴权失败,已记录诊断信息", type="warn", skill=SKILL_SLUG, stage="auth")
finish(
status="failed",
message=reason,
skill=SKILL_SLUG,
error_code="ENTITLEMENT_DENIED",
stage="auth",
)
print(f"ERROR:ENTITLEMENT_DENIED {reason}")
return 1
log.info("entitlement_ok task_type=%s stage=auth", task_type)
# 导入RPA模块
from service.rpa import run_invoice_rpa_with_video
# 运行RPA
result = asyncio.run(
run_invoice_rpa_with_video(
username=username,
password=password,
profile_dir=profile_dir,
skill_slug=SKILL_SLUG,
skill_data_dir=get_skill_root(),
headless=headless,
detail_mode=detail_mode,
title="开始提取发票信息",
closing_title="发票信息提取完成",
)
)
if result.success:
invoice_count = result.data.get("total_count", 0)
log.info(
"rpa_success task_type=%s invoice_count=%s stage=extraction",
task_type,
invoice_count,
)
emit(f"成功提取 {invoice_count} 张发票信息", skill=SKILL_SLUG, stage="rpa_complete")
# 保存到数据库
try:
from service.rpa_invoice_service import save_rpa_invoice_data
batch_id = uuid.uuid4().hex[:12]
record_id = save_rpa_invoice_data(
batch_id=batch_id,
source_url="https://sandbox.jc2009.com/industries/campus/smartcampus/app/finance/invoice-precheck",
invoice_data=result.data,
username=username,
status="extracted",
)
log.info(
"rpa_data_saved record_id=%s batch_id=%s",
record_id,
batch_id,
)
# 添加记录ID到结果中
result.data["record_id"] = record_id
result.data["batch_id"] = batch_id
except Exception as save_error:
log.warning("rpa_data_save_failed error=%s", str(save_error))
# 即使保存失败,也继续输出结果
# 输出结果
_print_json(result.data)
finish(
status="success",
message=f"成功提取 {invoice_count} 张发票信息",
skill=SKILL_SLUG,
stage="extraction",
)
return 0
else:
log.warning(
"rpa_failed task_type=%s error=%s stage=%s",
task_type,
result.error,
result.stage,
)
emit("发票信息提取失败", type="warn", skill=SKILL_SLUG, stage="rpa_failed")
finish(
status="failed",
message=result.error or "发票信息提取失败",
skill=SKILL_SLUG,
error_code="RPA_FAILED",
stage=result.stage or "unknown",
)
print(f"ERROR:RPA_FAILED {result.error}")
return 1
except JobStopped:
raise
except Exception:
log.exception(
"task_failed task_type=%s",
task_type,
)
emit("任务失败,已记录诊断信息", type="warn", skill=SKILL_SLUG, stage="rpa_error")
finish(
status="failed",
message="发票信息提取异常,详见统一日志",
skill=SKILL_SLUG,
stage="rpa_error",
)
return 1
def cmd_list_rpa_invoices(
limit: int = 20,
batch_id: Optional[str] = None,
username: Optional[str] = None,
status: Optional[str] = None,
) -> int:
"""查询RPA提取的发票数据。"""
log = _get_task_logger()
task_type = "list_rpa_invoices"
with job_context(skill=SKILL_SLUG):
log.info(
"task_start task_type=%s limit=%s batch_id=%s username=%s status=%s",
task_type,
limit,
batch_id,
username,
status,
)
try:
from service.rpa_invoice_service import list_rpa_invoices
records = list_rpa_invoices(
limit=limit,
batch_id=batch_id,
username=username,
status=status,
)
_print_json({
"ok": True,
"count": len(records),
"records": records,
})
return 0
except Exception as e:
log.exception("task_failed task_type=%s", task_type)
print(f"ERROR:QUERY_FAILED 查询RPA提取数据失败: {str(e)}")
return 1
def cmd_show_rpa_invoice(record_id: int) -> int:
"""查看RPA提取的发票数据详情。"""
log = _get_task_logger()
task_type = "show_rpa_invoice"
with job_context(skill=SKILL_SLUG):
log.info("task_start task_type=%s record_id=%s", task_type, record_id)
try:
from service.rpa_invoice_service import get_rpa_invoice_data
record = get_rpa_invoice_data(record_id)
if not record:
print(f"ERROR:RECORD_NOT_FOUND 未找到记录 ID={record_id}")
return 1
_print_json(record)
return 0
except Exception as e:
log.exception("task_failed task_type=%s", task_type)
print(f"ERROR:QUERY_FAILED 查询RPA发票详情失败: {str(e)}")
return 1
def cmd_rpa_invoice_stats() -> int:
"""获取RPA提取统计信息。"""
log = _get_task_logger()
task_type = "rpa_invoice_stats"
with job_context(skill=SKILL_SLUG):
log.info("task_start task_type=%s", task_type)
try:
from service.rpa_invoice_service import get_rpa_invoice_stats
stats = get_rpa_invoice_stats()
_print_json(stats)
return 0
except Exception as e:
log.exception("task_failed task_type=%s", task_type)
print(f"ERROR:QUERY_FAILED 获取RPA统计失败: {str(e)}")
return 1
def cmd_test_rpa_connection(
username: str,
password: str,
profile_dir: str,
) -> int:
"""测试RPA连接和登录。"""
log = _get_task_logger()
task_type = "test_rpa_connection"
with job_context(skill=SKILL_SLUG):
log.info(
"task_start task_type=%s username=%s",
task_type,
username[:3] + "***",
)
emit("测试RPA连接", skill=SKILL_SLUG, stage="test_start")
try:
# 导入RPA模块
from service.rpa import close_browser_context, start_browser_session
from service.rpa.login_handler import login_if_needed
# 启动浏览器
pw, context, page = asyncio.run(
start_browser_session(profile_dir, headless=False)
)
# 测试登录
login_success = asyncio.run(
login_if_needed(page, username, password, wait_sec=30)
)
# 关闭浏览器
asyncio.run(close_browser_context(pw, context))
if login_success:
log.info("rpa_connection_test_success task_type=%s", task_type)
emit("RPA连接测试成功", skill=SKILL_SLUG, stage="test_success")
_print_json({"success": True, "message": "连接和登录测试成功"})
finish(status="success", message="RPA连接测试成功", skill=SKILL_SLUG, stage="test")
return 0
else:
log.warning("rpa_connection_test_failed task_type=%s", task_type)
emit("RPA连接测试失败", type="warn", skill=SKILL_SLUG, stage="test_failed")
_print_json({"success": False, "message": "登录失败"})
finish(status="failed", message="登录失败", skill=SKILL_SLUG, stage="test")
return 1
except Exception as e:
log.exception("task_failed task_type=%s", task_type)
emit("RPA连接测试异常", type="warn", skill=SKILL_SLUG, stage="test_error")
finish(status="failed", message=f"测试异常: {str(e)}", skill=SKILL_SLUG, stage="test")
return 1