Merge branch 'master' of https://github.com/MoFox-Studio/MoFox_Bot
This commit is contained in:
@@ -64,8 +64,9 @@ class MonthlyPlanManager:
|
||||
target_month = datetime.now().strftime("%Y-%m")
|
||||
|
||||
if not has_active_plans(target_month):
|
||||
logger.info(f" {target_month} 没有任何有效的月度计划,将立即生成。")
|
||||
return await self.generate_monthly_plans(target_month)
|
||||
logger.info(f" {target_month} 没有任何有效的月度计划,将触发同步生成。")
|
||||
generation_successful = await self._generate_monthly_plans_logic(target_month)
|
||||
return generation_successful
|
||||
else:
|
||||
logger.info(f"{target_month} 已存在有效的月度计划。")
|
||||
plans = get_active_plans_for_month(target_month)
|
||||
@@ -74,8 +75,8 @@ class MonthlyPlanManager:
|
||||
max_plans = global_config.monthly_plan_system.max_plans_per_month
|
||||
if len(plans) > max_plans:
|
||||
logger.warning(f"当前月度计划数量 ({len(plans)}) 超出上限 ({max_plans}),将自动删除多余的计划。")
|
||||
# 按创建时间升序排序(旧的在前),然后删除超出上限的部分(新的)
|
||||
plans_to_delete = sorted(plans, key=lambda p: p.created_at, reverse=True)[: len(plans) - max_plans]
|
||||
# 数据库查询结果已按创建时间降序排序(新的在前),直接截取超出上限的部分进行删除
|
||||
plans_to_delete = plans[:len(plans)-max_plans]
|
||||
delete_ids = [p.id for p in plans_to_delete]
|
||||
delete_plans_by_ids(delete_ids)
|
||||
# 重新获取计划列表
|
||||
@@ -86,10 +87,21 @@ class MonthlyPlanManager:
|
||||
logger.info(f"当前月度计划内容:\n{plan_texts}")
|
||||
return True # 已经有计划,也算成功
|
||||
|
||||
async def generate_monthly_plans(self, target_month: Optional[str] = None) -> bool:
|
||||
async def generate_monthly_plans(self, target_month: Optional[str] = None):
|
||||
"""
|
||||
生成指定月份的月度计划
|
||||
启动月度计划生成。
|
||||
"""
|
||||
if self.generation_running:
|
||||
logger.info("月度计划生成任务已在运行中,跳过重复启动")
|
||||
return
|
||||
|
||||
logger.info(f"已触发 {target_month or '当前月份'} 的月度计划生成任务。")
|
||||
await self._generate_monthly_plans_logic(target_month)
|
||||
|
||||
async def _generate_monthly_plans_logic(self, target_month: Optional[str] = None) -> bool:
|
||||
"""
|
||||
生成指定月份的月度计划的核心逻辑
|
||||
|
||||
:param target_month: 目标月份,格式为 "YYYY-MM"。如果为 None,则为当前月份。
|
||||
:return: 是否生成成功
|
||||
"""
|
||||
@@ -136,14 +148,6 @@ class MonthlyPlanManager:
|
||||
finally:
|
||||
self.generation_running = False
|
||||
|
||||
def trigger_generate_monthly_plans(self, target_month: Optional[str] = None):
|
||||
"""
|
||||
以非阻塞的方式启动月度计划生成任务。
|
||||
这允许其他模块(如ScheduleManager)触发计划生成,而无需等待其完成。
|
||||
"""
|
||||
logger.info(f"已触发 {target_month or '当前月份'} 的非阻塞月度计划生成任务。")
|
||||
asyncio.create_task(self.generate_monthly_plans(target_month))
|
||||
|
||||
def _get_previous_month(self, current_month: str) -> str:
|
||||
"""获取上个月的月份字符串"""
|
||||
try:
|
||||
@@ -287,8 +291,6 @@ class MonthlyPlanManager:
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f" 归档 {target_month} 月度计划时发生错误: {e}")
|
||||
|
||||
|
||||
class MonthlyPlanGenerationTask(AsyncTask):
|
||||
"""每月初自动生成新月度计划的任务"""
|
||||
|
||||
@@ -324,8 +326,8 @@ class MonthlyPlanGenerationTask(AsyncTask):
|
||||
# 生成新月份的计划
|
||||
current_month = next_month.strftime("%Y-%m")
|
||||
logger.info(f" 到达月初,开始生成 {current_month} 的月度计划...")
|
||||
await self.monthly_plan_manager.generate_monthly_plans(current_month)
|
||||
|
||||
await self.monthly_plan_manager._generate_monthly_plans_logic(current_month)
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.info(" 每月月度计划生成任务被取消。")
|
||||
break
|
||||
|
||||
@@ -159,42 +159,37 @@ class ScheduleManager:
|
||||
schedule_record = session.query(Schedule).filter(Schedule.date == today_str).first()
|
||||
if schedule_record:
|
||||
logger.info(f"从数据库加载今天的日程 ({today_str})。")
|
||||
|
||||
try:
|
||||
schedule_data = orjson.loads(str(schedule_record.schedule_data))
|
||||
|
||||
# 使用Pydantic验证日程数据
|
||||
if self._validate_schedule_with_pydantic(schedule_data):
|
||||
self.today_schedule = schedule_data
|
||||
schedule_str = f"已成功加载今天的日程 ({today_str}):\n"
|
||||
if self.today_schedule:
|
||||
for item in self.today_schedule:
|
||||
schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n"
|
||||
logger.info(schedule_str)
|
||||
else:
|
||||
logger.warning("数据库中的日程数据格式无效,将异步重新生成日程")
|
||||
await self.generate_and_save_schedule()
|
||||
except orjson.JSONDecodeError as e:
|
||||
logger.error(f"日程数据JSON解析失败: {e},将异步重新生成日程")
|
||||
await self.generate_and_save_schedule()
|
||||
schedule_data = orjson.loads(str(schedule_record.schedule_data))
|
||||
if self._validate_schedule_with_pydantic(schedule_data):
|
||||
self.today_schedule = schedule_data
|
||||
schedule_str = f"已成功加载今天的日程 ({today_str}):\n"
|
||||
if self.today_schedule:
|
||||
for item in self.today_schedule:
|
||||
schedule_str += f" - {item.get('time_range', '未知时间')}: {item.get('activity', '未知活动')}\n"
|
||||
logger.info(schedule_str)
|
||||
return # 成功加载,直接返回
|
||||
else:
|
||||
logger.warning("数据库中的日程数据格式无效,将重新生成日程")
|
||||
else:
|
||||
logger.info(f"数据库中未找到今天的日程 ({today_str}),将异步调用 LLM 生成。")
|
||||
await self.generate_and_save_schedule()
|
||||
logger.info(f"数据库中未找到今天的日程 ({today_str}),将调用 LLM 生成。")
|
||||
|
||||
# 仅在需要时生成
|
||||
await self.generate_and_save_schedule()
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"加载或生成日程时出错: {e}")
|
||||
# 出错时也尝试异步生成
|
||||
logger.info("尝试异步生成日程作为备用方案...")
|
||||
logger.info("尝试生成日程作为备用方案...")
|
||||
await self.generate_and_save_schedule()
|
||||
|
||||
async def generate_and_save_schedule(self):
|
||||
"""启动异步日程生成任务,避免阻塞主程序"""
|
||||
"""将日程生成任务提交到后台执行"""
|
||||
if self.schedule_generation_running:
|
||||
logger.info("日程生成任务已在运行中,跳过重复启动")
|
||||
return
|
||||
|
||||
# 创建异步任务进行日程生成,不阻塞主程序
|
||||
asyncio.create_task(self._async_generate_and_save_schedule())
|
||||
logger.info("已启动异步日程生成任务")
|
||||
logger.info("检测到需要生成日程,已提交后台任务。")
|
||||
task = OnDemandScheduleGenerationTask(self)
|
||||
await async_task_manager.add_task(task)
|
||||
|
||||
async def _async_generate_and_save_schedule(self):
|
||||
"""异步生成并保存日程的内部方法"""
|
||||
@@ -234,12 +229,14 @@ class ScheduleManager:
|
||||
logger.info("可用的月度计划已耗尽或不足,触发后台补充生成...")
|
||||
from mmc.src.schedule.monthly_plan_manager import monthly_plan_manager
|
||||
|
||||
# 以非阻塞方式触发月度计划生成
|
||||
monthly_plan_manager.trigger_generate_monthly_plans(current_month_str)
|
||||
# 等待月度计划生成完成
|
||||
await monthly_plan_manager.ensure_and_generate_plans_if_needed(current_month_str)
|
||||
|
||||
# 注意:这里不再等待生成结果,因此后续代码不会立即获得新计划。
|
||||
# 日程将基于当前可用的信息生成,新计划将在下一次日程生成时可用。
|
||||
logger.info("月度计划的后台生成任务已启动,本次日程将不包含新计划。")
|
||||
# 重新获取月度计划
|
||||
sampled_plans = get_smart_plans_for_daily_schedule(
|
||||
current_month_str, max_count=3, avoid_days=avoid_days
|
||||
)
|
||||
logger.info("月度计划补充生成完毕,继续日程生成任务。")
|
||||
|
||||
if sampled_plans:
|
||||
plan_texts = "\n".join([f"- {plan.plan_text}" for plan in sampled_plans])
|
||||
@@ -448,6 +445,20 @@ class ScheduleManager:
|
||||
return True
|
||||
|
||||
|
||||
class OnDemandScheduleGenerationTask(AsyncTask):
|
||||
"""按需生成日程的后台任务"""
|
||||
|
||||
def __init__(self, schedule_manager: "ScheduleManager"):
|
||||
task_name = f"OnDemandScheduleGenerationTask-{datetime.now().strftime('%Y%m%d%H%M%S')}"
|
||||
super().__init__(task_name=task_name)
|
||||
self.schedule_manager = schedule_manager
|
||||
|
||||
async def run(self):
|
||||
logger.info(f"后台任务 {self.task_name} 开始执行日程生成。")
|
||||
await self.schedule_manager._async_generate_and_save_schedule()
|
||||
logger.info(f"后台任务 {self.task_name} 完成。")
|
||||
|
||||
|
||||
class DailyScheduleGenerationTask(AsyncTask):
|
||||
"""每日零点自动生成新日程的任务"""
|
||||
|
||||
@@ -471,9 +482,9 @@ class DailyScheduleGenerationTask(AsyncTask):
|
||||
# 2. 等待直到零点
|
||||
await asyncio.sleep(sleep_seconds)
|
||||
|
||||
# 3. 执行异步日程生成
|
||||
logger.info("到达每日零点,开始异步生成新的一天日程...")
|
||||
await self.schedule_manager.generate_and_save_schedule()
|
||||
# 3. 执行日程生成
|
||||
logger.info("到达每日零点,开始生成新的一天日程...")
|
||||
await self.schedule_manager._async_generate_and_save_schedule()
|
||||
|
||||
except asyncio.CancelledError:
|
||||
logger.info("每日日程生成任务被取消。")
|
||||
|
||||
Reference in New Issue
Block a user