216 lines
7.2 KiB
Python
216 lines
7.2 KiB
Python
"""
|
||
定时任务调度服务
|
||
基于 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()
|