""" 定时任务调度服务 基于 APScheduler 实现 """ import logging import random import asyncio from typing import Optional, List from datetime import datetime, time from apscheduler.schedulers.asyncio import AsyncIOScheduler from apscheduler.triggers.cron import CronTrigger from apscheduler.triggers.interval import IntervalTrigger from sqlalchemy.orm import Session from app.models.virtual_user import VirtualUser, ActivityLevel, UserStatus from app.models.base import get_db, SessionLocal from app.services.interaction_service import InteractionService from app.core.config import settings logger = logging.getLogger(__name__) class SchedulerService: """定时任务调度服务类""" def __init__(self): self.scheduler = AsyncIOScheduler() self.is_running = False self._current_job = None def start(self): """启动调度器""" if not self.is_running: self.scheduler.start() self.is_running = True logger.info("Scheduler started") def stop(self): """停止调度器""" if self.is_running: self.scheduler.shutdown() self.is_running = False logger.info("Scheduler stopped") def add_interaction_task(self): """添加互动任务""" # 在活动时间段内,每隔随机时间执行一次互动 # 由于 APScheduler 不支持随机间隔,我们使用固定间隔但通过概率控制执行 # 每 5 分钟检查一次 trigger = IntervalTrigger(minutes=5) self.scheduler.add_job( self._execute_random_interaction, trigger=trigger, id="random_interaction", name="Random Interaction Task", replace_existing=True ) logger.info("Interaction task added") def remove_interaction_task(self): """移除互动任务""" try: self.scheduler.remove_job("random_interaction") logger.info("Interaction task removed") except Exception as e: logger.warning(f"Remove interaction task error: {e}") async def _execute_random_interaction(self): """执行随机互动任务""" # 检查是否在活动时间段内 now = datetime.now() current_hour = now.hour if current_hour < settings.TASK_START_HOUR or current_hour > settings.TASK_END_HOUR: logger.debug(f"Outside activity hours: {current_hour}") return # 随机决定是否执行(通过随机间隔模拟) if random.random() > 0.5: # 50% 概率执行 logger.debug("Skip this round") return logger.info("Executing random interaction task") # 获取数据库会话 db = SessionLocal() try: # 获取所有活跃的虚拟用户 users = db.query(VirtualUser).filter( VirtualUser.status == UserStatus.ACTIVE, VirtualUser.is_logged_in == True ).all() if not users: logger.debug("No active logged-in users") return # 随机选择一个用户 user = random.choice(users) # 检查用户活跃度 if not self._should_user_interact(user): logger.debug(f"User {user.id} should not interact now") return # 执行互动 interaction_service = InteractionService(db) await interaction_service.execute_interaction(virtual_user_id=user.id) except Exception as e: logger.error(f"Execute random interaction error: {e}") finally: db.close() def _should_user_interact(self, user: VirtualUser) -> bool: """根据活跃度判断用户是否应该互动""" # 根据活跃度决定互动概率 if user.activity_level == ActivityLevel.HIGH: # 高活跃度:80% 概率 return random.random() < 0.8 elif user.activity_level == ActivityLevel.MEDIUM: # 中活跃度:50% 概率 return random.random() < 0.5 else: # 低活跃度:30% 概率 return random.random() < 0.3 def add_login_task(self, hour: int = 8, minute: int = 0): """添加每日登录任务""" trigger = CronTrigger(hour=hour, minute=minute) self.scheduler.add_job( self._auto_login_users, trigger=trigger, id="daily_login", name="Daily Auto Login", replace_existing=True ) logger.info(f"Daily login task added at {hour:02d}:{minute:02d}") async def _auto_login_users(self): """自动登录所有活跃用户""" db = SessionLocal() try: from app.services.huihui_api_service import huihui_api_service users = db.query(VirtualUser).filter( VirtualUser.status == UserStatus.ACTIVE ).all() for user in users: try: # 调用登录接口 result = await huihui_api_service.login(user.username, user.password) if result and result.get("token"): user.is_logged_in = True user.session_token = result["token"] # TODO: 设置 token 过期时间 logger.info(f"Auto login success: {user.username}") else: logger.warning(f"Auto login failed: {user.username}") except Exception as e: logger.error(f"Auto login error for {user.username}: {e}") db.commit() except Exception as e: logger.error(f"Auto login task error: {e}") db.rollback() finally: db.close() def reset_daily_counters(self, hour: int = 0, minute: int = 1): """添加每日计数器重置任务""" trigger = CronTrigger(hour=hour, minute=minute) self.scheduler.add_job( self._reset_daily_counters, trigger=trigger, id="reset_daily_counters", name="Reset Daily Counters", replace_existing=True ) logger.info(f"Daily reset task added at {hour:02d}:{minute:02d}") def _reset_daily_counters(self): """重置每日计数器""" db = SessionLocal() try: # 重置所有用户的今日计数 db.query(VirtualUser).update({ VirtualUser.today_comments: 0, VirtualUser.today_replies: 0 }) db.commit() logger.info("Daily counters reset") except Exception as e: logger.error(f"Reset daily counters error: {e}") db.rollback() finally: db.close() # 创建全局服务实例 scheduler_service = SchedulerService()