|
|
@@ -3,8 +3,9 @@
|
|
|
|
|
|
提供:
|
|
|
1. API 接口:POST /api/tasks - 触发内容寻找任务
|
|
|
-2. 定时调度:启动后先恢复 demand_find_task 中 status=执行中 的任务;之后每 2 分钟轮询一次,
|
|
|
- 若当前无任务在执行,则从 demand_content 取当天(dt=YYYYMMDD)、未建任务记录且 score 最高的一条执行(不区分品类)
|
|
|
+2. 定时调度:启动后先恢复 demand_find_task 中 status=执行中 的任务;之后按间隔轮询;
|
|
|
+ 若当前无任务在执行,则从 demand_content 取当天(dt=YYYYMMDD)、未建任务记录且 score 最高的一条执行(不区分品类)。
|
|
|
+ 本文件常量 SCHEDULE_DISPATCH_NOT_BEFORE_HOUR:仅在该本地时刻(SCHEDULER_TIMEZONE)及之后才派发;与按 UTC 计日的上游日限额对齐时常用 8(北京 08:00 = UTC 换日)。
|
|
|
3. 并发控制:限制最大并发任务数;定时侧若已有任务在执行则跳过本次轮询
|
|
|
4. 单次寻找任务最长执行 25 分钟,超时记为失败并回写 demand_find_task
|
|
|
"""
|
|
|
@@ -75,6 +76,10 @@ task_semaphore = asyncio.Semaphore(MAX_CONCURRENT_TASKS)
|
|
|
SCHEDULE_DISPATCH_INTERVAL_SECONDS = int(os.getenv("SCHEDULE_DISPATCH_INTERVAL_SECONDS", "30"))
|
|
|
TASK_TIMEOUT_SECONDS = int(os.getenv("SCHEDULE_TASK_TIMEOUT_SECONDS", "1500"))
|
|
|
|
|
|
+# 定时派发最早本地整点(0-23,含该小时起至当日结束;SCHEDULER_TIMEZONE)。None=不限制。
|
|
|
+# 上游按 UTC 计日(如 OpenRouter 日限额)时与 UTC 换日对齐可改为 8。
|
|
|
+SCHEDULE_DISPATCH_NOT_BEFORE_HOUR: Optional[int] = 8
|
|
|
+
|
|
|
# 统计信息
|
|
|
stats = {
|
|
|
"total_tasks": 0,
|
|
|
@@ -199,6 +204,17 @@ async def scheduled_tick():
|
|
|
按 SCHEDULE_DISPATCH_INTERVAL_SECONDS 派发:若当前并发有空槽,则从 demand_content 取
|
|
|
当天(dt=今日)、尚未出现在 demand_find_task 中且 score 最高的一条需求并执行。
|
|
|
"""
|
|
|
+ if SCHEDULE_DISPATCH_NOT_BEFORE_HOUR is not None:
|
|
|
+ now = datetime.now(SCHEDULER_TZ)
|
|
|
+ if now.hour < SCHEDULE_DISPATCH_NOT_BEFORE_HOUR:
|
|
|
+ logger.info(
|
|
|
+ "定时任务跳过:未到本地派发窗口(需 %02d:00 %s 及之后,当前 %s)",
|
|
|
+ SCHEDULE_DISPATCH_NOT_BEFORE_HOUR,
|
|
|
+ SCHEDULER_TIMEZONE,
|
|
|
+ now.strftime("%H:%M"),
|
|
|
+ )
|
|
|
+ return
|
|
|
+
|
|
|
logger.info("定时任务触发(scheduled_tick)")
|
|
|
|
|
|
# demand_task_oprate:最新一条 is_open=0 时关闭定时派发;无记录时默认继续(兼容未配置)
|
|
|
@@ -445,9 +461,14 @@ async def startup():
|
|
|
logger.info("内容寻找服务启动中...")
|
|
|
logger.info(f"最大并发任务数: {MAX_CONCURRENT_TASKS}")
|
|
|
logger.info(f"定时器时区: {SCHEDULER_TIMEZONE}")
|
|
|
+ window_desc = (
|
|
|
+ f";本地派发不早于 {SCHEDULE_DISPATCH_NOT_BEFORE_HOUR:02d}:00({SCHEDULER_TIMEZONE})"
|
|
|
+ if SCHEDULE_DISPATCH_NOT_BEFORE_HOUR is not None
|
|
|
+ else ""
|
|
|
+ )
|
|
|
logger.info(
|
|
|
- f"定时策略:每 {SCHEDULE_DISPATCH_INTERVAL_SECONDS} 秒尝试派发 1 条(有并发空槽才派发);"
|
|
|
- f"单次任务超时 {TASK_TIMEOUT_SECONDS}s"
|
|
|
+ f"定时策略:每 {SCHEDULE_DISPATCH_INTERVAL_SECONDS} 秒尝试派发 1 条(有并发空槽才派发)"
|
|
|
+ f"{window_desc};单次任务超时 {TASK_TIMEOUT_SECONDS}s"
|
|
|
)
|
|
|
|
|
|
asyncio.create_task(run_startup_resume())
|