diff --git a/acceptance_service.py b/acceptance_service.py index f452f07..a628c19 100644 --- a/acceptance_service.py +++ b/acceptance_service.py @@ -996,17 +996,34 @@ def create_acceptance_task(data: dict[str, Any]) -> dict[str, Any]: 422, ) name = str(data.get("name") or "").strip() or f"项目验收|{keywords[0] if keywords else '飞书资料'}" + schedule_requested = data.get("schedule_weekdays") not in (None, [], "") + schedule_weekdays = _normalize_schedule_weekdays(data.get("schedule_weekdays")) if schedule_requested else [] + schedule_start = _normalize_schedule_time(data.get("schedule_start_time", "00:00"), "开始时间") if schedule_requested else "00:00" + schedule_end = _normalize_schedule_time(data.get("schedule_end_time", "23:59"), "结束时间") if schedule_requested else "23:59" + if schedule_requested and schedule_start > schedule_end: + raise TaskError("INVALID_SCHEDULE_RANGE", "开始时间不能晚于结束时间", 422) + defer_initial = data.get("defer_initial_collection") is True + initial_status = "completed" if defer_initial else "waiting" + initial_progress = 100 if defer_initial else 0 now = now_iso() with connection() as conn: public_id = _public_id() cursor = conn.execute( """INSERT INTO acceptance_tasks (public_task_id,name,acceptance_method,status,progress_percent,device_count, - created_by_id,created_by_name,created_at,updated_at,trigger_source,cookie_count) - VALUES (?,?,?,'waiting',0,?,?,?,?,?,?,?)""", - (public_id, name, method, device_count, str(data.get("created_by_id") or "local-dev"), + created_by_id,created_by_name,created_at,updated_at,trigger_source,cookie_count, + schedule_enabled,schedule_weekday,schedule_weekdays,schedule_start_time, + schedule_end_time,next_run_at,use_all_devices) + VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""", + (public_id, name, method, initial_status, initial_progress, device_count, + str(data.get("created_by_id") or "local-dev"), str(data.get("created_by_name") or "本地开发用户"), now, now, - str(data.get("trigger_source") or "acceptance"), cookie_count), + str(data.get("trigger_source") or "acceptance"), cookie_count, + 1 if schedule_requested else 0, schedule_weekdays[0] if schedule_weekdays else None, + json.dumps(schedule_weekdays) if schedule_weekdays else None, + schedule_start, schedule_end, + _next_weekly_run(schedule_weekdays, schedule_start) if schedule_weekdays else None, + 1 if method in {"screenshot", "combined"} else 0), ) task_id = cursor.lastrowid conn.executemany( @@ -1046,7 +1063,8 @@ def create_acceptance_task(data: dict[str, Any]) -> dict[str, Any]: VALUES (?,?,?,?,'waiting',?,?)""", [(task_id, index, url, _source_type(url), now, now) for index, url in enumerate(links)], ) - threading.Thread(target=_fetch_sources, args=(public_id,), daemon=True, name=f"acceptance-{public_id}").start() + if not defer_initial: + threading.Thread(target=_fetch_sources, args=(public_id,), daemon=True, name=f"acceptance-{public_id}").start() return get_acceptance_task(public_id) diff --git a/apifox-business-tasks.openapi.json b/apifox-business-tasks.openapi.json index 0ee83d6..4ca5875 100644 --- a/apifox-business-tasks.openapi.json +++ b/apifox-business-tasks.openapi.json @@ -222,7 +222,11 @@ "description": "仅用于笔记收录。填写后仅使用该 Cookie;留空时使用 Cookie 池全部可用凭据。不会在任务详情中返回。" }, "created_by_id": {"type": "string"}, - "created_by_name": {"type": "string"} + "created_by_name": {"type": "string"}, + "schedule_weekdays": {"type": "array", "items": {"type": "integer", "minimum": 1, "maximum": 7}}, + "schedule_start_time": {"type": "string", "example": "20:00"}, + "schedule_end_time": {"type": "string", "example": "22:00"}, + "defer_initial_collection": {"type": "boolean", "description": "true 时只保存周期项目,不立即启动首批采集"} } } }, diff --git a/tests/test_acceptance_creation.py b/tests/test_acceptance_creation.py index 1cb68c2..1a1a032 100644 --- a/tests/test_acceptance_creation.py +++ b/tests/test_acceptance_creation.py @@ -62,6 +62,24 @@ class AcceptanceCreationTest(unittest.TestCase): self.assertEqual(task["keywords"], ["山姆特价"]) self.assertEqual(task["feishu_links"], []) + @patch("acceptance_service.threading.Thread", _ThreadWithoutStart) + @patch("acceptance_service.get_wen_resources") + def test_create_can_save_schedule_without_starting_initial_collection(self, resources): + resources.return_value = {"cookie_count": 2, "device_count": 3, "credential_ids": ["c1", "c2"]} + task = create_acceptance_task({ + "acceptance_method": "combined", "keywords": ["山姆会员卡"], + "match_keywords": ["民生"], "schedule_weekdays": [1, 4], + "schedule_start_time": "20:00", "schedule_end_time": "22:00", + "defer_initial_collection": True, + }) + self.assertEqual(task["status"], "completed") + self.assertEqual(task["cookie_count"], 2) + self.assertEqual(task["device_count"], 3) + self.assertEqual(task["schedule_weekdays"], [1, 4]) + self.assertEqual(task["schedule_start_time"], "20:00") + self.assertIsNotNone(task["next_run_at"]) + self.assertEqual(task["keyword_runs"], []) + @patch("acceptance_service.threading.Thread", _ThreadWithoutStart) @patch("acceptance_service.get_wen_resources") def test_active_acceptance_task_can_be_cancelled(self, resources):