#!/usr/bin/env python3 """ 会话归档脚本 功能:将会话结束超过90天的会话标记为已归档 运行方式: cd backend python scripts/archive_sessions.py 定时任务配置(crontab): # 每天凌晨3点执行 0 3 * * * cd /opt/wecom-it-desk/backend && python scripts/archive_sessions.py >> /var/log/itdesk-archive.log 2>&1 """ import sys import os from datetime import datetime, timedelta, timezone # 添加 backend 目录到 Python 路径 sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from sqlalchemy import create_engine, select, update from sqlalchemy.orm import Session from app.config import settings from app.models.conversation import Conversation # 归档阈值:会话结束后多少天开始归档(可配置) ARCHIVE_DAYS = 90 def archive_sessions(): """将会话结束超过 ARCHIVE_DAYS 天的会话标记为已归档""" print(f"[{datetime.now()}] 开始执行会话归档任务...") print(f"归档阈值: {ARCHIVE_DAYS} 天") # 使用配置中的数据库 URL # 注意:本地开发使用 aiosqlite 异步驱动,但脚本是同步的 # 需要将 sqlite+aiosqlite:// 替换为 sqlite:// db_url = settings.database_url.replace("sqlite+aiosqlite://", "sqlite://") engine = create_engine(db_url) # 计算归档截止时间:当前时间 - ARCHIVE_DAYS 天 # 只有 resolved_at 早于此时间的会话才会被归档 cutoff_date = datetime.now(timezone.utc) - timedelta(days=ARCHIVE_DAYS) with Session(engine) as session: # 查找需要归档的会话: # 1. 状态为 resolved(已结单) # 2. 已结单时间(resolved_at 或 updated_at)早于截止时间 # 3. 尚未被归档 stmt = select(Conversation).where( Conversation.status == "resolved", Conversation.is_archived == False, # 使用 updated_at 作为参考(resolved 状态的会话 updated_at 即为结单时间) Conversation.updated_at < cutoff_date ) sessions_to_archive = session.execute(stmt).scalars().all() count = len(sessions_to_archive) if count == 0: print(f"[{datetime.now()}] 没有需要归档的会话") return print(f"[{datetime.now()}] 找到 {count} 个需要归档的会话") # 标记为已归档 now = datetime.now(timezone.utc) for session_obj in sessions_to_archive: session_obj.is_archived = True session_obj.archived_at = now # 提交更改 session.commit() print(f"[{datetime.now()}] 成功归档 {count} 个会话") print(f"归档时间: {now.isoformat()}") if __name__ == "__main__": archive_sessions()