chore: auto release commit (2026-09-15 01:39:08)
Some checks failed
技能自动化发布 / release (push) Failing after 6s
Some checks failed
技能自动化发布 / release (push) Failing after 6s
This commit is contained in:
@@ -2,6 +2,14 @@
|
|||||||
|
|
||||||
本文件对应技能市场 **「更新日志」** Tab。
|
本文件对应技能市场 **「更新日志」** Tab。
|
||||||
|
|
||||||
|
## 1.1.29
|
||||||
|
|
||||||
|
- **修复「老 DB 没 download_tasks 表导致入队失败」问题**:
|
||||||
|
- 用户本地 `data/alert-contract-expiring-hr.db` 是 v1.1.17 之前 init 的(只有前 6 张表),没跑过 v1.1.18 的 init-db → 没 `download_tasks` 表
|
||||||
|
- `_enqueue_download_tasks_safe` 在 `owns_conn=True` 路径下,先 idempotent `CREATE TABLE IF NOT EXISTS download_tasks ...` + 2 索引,再 INSERT
|
||||||
|
- 解决 `OperationalError: no such table: download_tasks` + 下载队列 `failed=3`
|
||||||
|
- 新增测试 `enqueue_safe_auto_create_table_v1_29` 模拟「老 DB 没建过 download_tasks」场景(用 monkeypatch `rp.get_db_path` + `db.connection.get_db_path`),合计 67/67
|
||||||
|
|
||||||
## 1.1.28
|
## 1.1.28
|
||||||
|
|
||||||
- **彻底修复「SPA 二级登录页卡顿」**:
|
- **彻底修复「SPA 二级登录页卡顿」**:
|
||||||
|
|||||||
2
SKILL.md
2
SKILL.md
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
name: 合同到期智能预警与续聘辅助
|
name: 合同到期智能预警与续聘辅助
|
||||||
description: "当用户说要扫描即将到期的合同、查 90/60/30 天预警名单、给某位教师生成续聘任务书、下发单位版续聘清单、从 SmartCampus HR 平台抓合同数据、解析续聘材料 PDF/Word、查看本月合同到期人员清单时,加载本技能。支持定时扫描(每日凌晨)+ 数据管理 + Agent 三入口,覆盖合同到期智能预警与续聘辅助全流程。"
|
description: "当用户说要扫描即将到期的合同、查 90/60/30 天预警名单、给某位教师生成续聘任务书、下发单位版续聘清单、从 SmartCampus HR 平台抓合同数据、解析续聘材料 PDF/Word、查看本月合同到期人员清单时,加载本技能。支持定时扫描(每日凌晨)+ 数据管理 + Agent 三入口,覆盖合同到期智能预警与续聘辅助全流程。"
|
||||||
version: 1.1.28
|
version: 1.1.29
|
||||||
author: 深圳匠厂科技有限公司
|
author: 深圳匠厂科技有限公司
|
||||||
metadata:
|
metadata:
|
||||||
openclaw:
|
openclaw:
|
||||||
|
|||||||
@@ -1358,6 +1358,35 @@ def _enqueue_download_tasks_safe(
|
|||||||
owns_conn = conn is None
|
owns_conn = conn is None
|
||||||
if owns_conn:
|
if owns_conn:
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
|
# v1.1.29:idempotent 表创建 —— 用户没跑 init-db 时,download_tasks 表不存在,
|
||||||
|
# 首次采集直接建表(CREATE TABLE IF NOT EXISTS)而不是报 "no such table"
|
||||||
|
try:
|
||||||
|
conn.executescript(
|
||||||
|
"""
|
||||||
|
CREATE TABLE IF NOT EXISTS download_tasks (
|
||||||
|
task_id TEXT PRIMARY KEY,
|
||||||
|
contract_id TEXT NOT NULL,
|
||||||
|
kind TEXT NOT NULL DEFAULT 'platform_button',
|
||||||
|
status TEXT NOT NULL DEFAULT 'pending',
|
||||||
|
attempts INTEGER NOT NULL DEFAULT 0,
|
||||||
|
error_msg TEXT,
|
||||||
|
enqueued_at TEXT NOT NULL,
|
||||||
|
started_at TEXT,
|
||||||
|
finished_at TEXT,
|
||||||
|
source_path TEXT,
|
||||||
|
result_path TEXT,
|
||||||
|
FOREIGN KEY (contract_id) REFERENCES contracts(contract_id)
|
||||||
|
);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dlq_status ON download_tasks(status);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_dlq_contract ON download_tasks(contract_id);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
conn.commit()
|
||||||
|
except Exception as ensure_err:
|
||||||
|
logger.warning(
|
||||||
|
"platform_collector: ensure download_tasks 表创建失败(继续走入队流程): %s",
|
||||||
|
ensure_err,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
for cid in contract_ids:
|
for cid in contract_ids:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
SKILL_SLUG = "alert-contract-expiring-hr"
|
SKILL_SLUG = "alert-contract-expiring-hr"
|
||||||
SKILL_VERSION = "1.1.28"
|
SKILL_VERSION = "1.1.29"
|
||||||
LOG_LOGGER_NAME = "openclaw.skill.alert_contract_expiring_hr"
|
LOG_LOGGER_NAME = "openclaw.skill.alert_contract_expiring_hr"
|
||||||
PLATFORM_KIT_MIN_VERSION = "1.2.11"
|
PLATFORM_KIT_MIN_VERSION = "1.2.11"
|
||||||
|
|
||||||
|
|||||||
@@ -1271,6 +1271,80 @@ def test_enqueue_download_tasks_safe_empty_list() -> tuple[bool, str]:
|
|||||||
return True, "空列表 no-op OK"
|
return True, "空列表 no-op OK"
|
||||||
|
|
||||||
|
|
||||||
|
def test_enqueue_download_tasks_safe_auto_create_table_v1_29() -> tuple[bool, str]:
|
||||||
|
"""v1.1.29:用户没跑 init-db 时,首次调用 `_enqueue_download_tasks_safe`
|
||||||
|
不传 conn(owns_conn=True),函数内部 idempotent 创建 download_tasks 表
|
||||||
|
而非抛 "no such table"。
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from service.platform_collector import _enqueue_download_tasks_safe
|
||||||
|
|
||||||
|
# 1. 临时文件 DB,**只建 contracts 表**(模拟老 DB 没 download_tasks)
|
||||||
|
with tempfile.TemporaryDirectory() as tmp:
|
||||||
|
tmp_db = Path(tmp) / "old.db"
|
||||||
|
# 重定向 JIANGCHANG_DATA_ROOT 或 get_db_path 用临时路径
|
||||||
|
# 这里用 monkeypatch get_db_path + get_connection
|
||||||
|
from util import runtime_paths as rp
|
||||||
|
from db import connection as dbconn
|
||||||
|
|
||||||
|
orig_rp_db_path = rp.get_db_path
|
||||||
|
orig_dbconn_db_path = dbconn.get_db_path
|
||||||
|
rp.get_db_path = lambda: tmp_db # type: ignore[assignment]
|
||||||
|
dbconn.get_db_path = lambda: tmp_db # type: ignore[assignment]
|
||||||
|
try:
|
||||||
|
# 模拟老 DB:只有 contracts 表(v1.1.17 之前 init 过)
|
||||||
|
_tmp_conn = sqlite3.connect(str(tmp_db))
|
||||||
|
try:
|
||||||
|
_tmp_conn.executescript(
|
||||||
|
"""
|
||||||
|
CREATE TABLE contracts (
|
||||||
|
contract_id TEXT PRIMARY KEY,
|
||||||
|
teacher_name TEXT, unit_id TEXT, unit_name TEXT,
|
||||||
|
position TEXT, contract_type TEXT, contract_start TEXT,
|
||||||
|
contract_end TEXT, trial_period_end TEXT,
|
||||||
|
status TEXT DEFAULT '可续聘',
|
||||||
|
last_material_id TEXT, renewed_at TEXT
|
||||||
|
);
|
||||||
|
INSERT INTO contracts
|
||||||
|
(contract_id, teacher_name, unit_id, unit_name,
|
||||||
|
position, contract_type, contract_start,
|
||||||
|
contract_end, trial_period_end)
|
||||||
|
VALUES ('ENQ-CREATE-1', '测试', 'U1', '物理学院',
|
||||||
|
'实验师', '专任教师聘用合同',
|
||||||
|
'2024-01-01', '2026-12-31', '2024-04-01');
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
_tmp_conn.commit()
|
||||||
|
finally:
|
||||||
|
_tmp_conn.close()
|
||||||
|
|
||||||
|
# 2. 不传 conn 调用(owns_conn=True,会走 get_connection → 触发 ensure 建表)
|
||||||
|
summary = _enqueue_download_tasks_safe(["ENQ-CREATE-1"])
|
||||||
|
if summary.get("failed", 0) > 0:
|
||||||
|
return False, f"应自动建表后入队成功,但 failed={summary}"
|
||||||
|
|
||||||
|
# 3. 验证表确实被自动创建了
|
||||||
|
conn = sqlite3.connect(str(tmp_db))
|
||||||
|
try:
|
||||||
|
count = conn.execute(
|
||||||
|
"SELECT COUNT(*) FROM download_tasks"
|
||||||
|
).fetchone()[0]
|
||||||
|
finally:
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
if count != 1:
|
||||||
|
return False, f"download_tasks 应有 1 行,实际 {count}"
|
||||||
|
|
||||||
|
return True, f"ensure 建表 + 入队 OK(enqueued={summary.get('enqueued')})"
|
||||||
|
finally:
|
||||||
|
rp.get_db_path = orig_rp_db_path # type: ignore[assignment]
|
||||||
|
dbconn.get_db_path = orig_dbconn_db_path # type: ignore[assignment]
|
||||||
|
|
||||||
|
|
||||||
# ---------- v1.1.19: _wait_for_url_stable + login flow ----------
|
# ---------- v1.1.19: _wait_for_url_stable + login flow ----------
|
||||||
|
|
||||||
|
|
||||||
@@ -1916,6 +1990,7 @@ def main() -> int:
|
|||||||
_run("download_tasks/enqueue_safe_idempotent", test_enqueue_download_tasks_safe_idempotent)
|
_run("download_tasks/enqueue_safe_idempotent", test_enqueue_download_tasks_safe_idempotent)
|
||||||
_run("download_tasks/enqueue_safe_fk_failure", test_enqueue_download_tasks_safe_fk_failure)
|
_run("download_tasks/enqueue_safe_fk_failure", test_enqueue_download_tasks_safe_fk_failure)
|
||||||
_run("download_tasks/enqueue_safe_empty_list", test_enqueue_download_tasks_safe_empty_list)
|
_run("download_tasks/enqueue_safe_empty_list", test_enqueue_download_tasks_safe_empty_list)
|
||||||
|
_run("download_tasks/enqueue_safe_auto_create_table_v1_29", test_enqueue_download_tasks_safe_auto_create_table_v1_29)
|
||||||
_run("login_flow/wait_url_stable_immediate", test_wait_for_url_stable_immediate_match)
|
_run("login_flow/wait_url_stable_immediate", test_wait_for_url_stable_immediate_match)
|
||||||
_run("login_flow/wait_url_stable_transition", test_wait_for_url_stable_transition)
|
_run("login_flow/wait_url_stable_transition", test_wait_for_url_stable_transition)
|
||||||
_run("login_flow/wait_url_stable_timeout", test_wait_for_url_stable_timeout)
|
_run("login_flow/wait_url_stable_timeout", test_wait_for_url_stable_timeout)
|
||||||
|
|||||||
Reference in New Issue
Block a user