| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115 |
- # -*- coding: utf-8 -*-
- # Author : Charley
- # Python : 3.12.10
- # Date : 2026/09/09
- """得卡运维常驻:账号池 cooling 巡检恢复 + 定时清理堆积的报告 Excel。
- 两个后台定时任务(单进程 schedule 循环):
- 1) 账号池 cooling 巡检:每 10 分钟把冷却(cooling)到期的账号恢复 healthy(补账号池状态机闭环,
- 否则软失败转 cooling 的号永不自动恢复,见审查问题4)。
- 2) 报告 Excel 清理:每天 03:00 删除 EXCEL_DIRS 下超过 EXCEL_KEEP_DAYS 天的 .xlsx(报告每天生成、
- 堆积占盘),排除 EXCEL_KEEP_FILES 里的固定产物。
- ⚠️ 待主公确认(2026/09/09):EXCEL_KEEP_DAYS 保留天数、EXCEL_DIRS 清理目录、EXCEL_KEEP_FILES 白名单。
- """
- import os
- import sys
- import time
- import glob
- _ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
- sys.path.insert(0, os.path.join(_ROOT, "common"))
- os.chdir(_ROOT)
- import schedule
- from loguru import logger
- from mysql_pool import MySQLConnectionPool
- from account_pool import AccountPool
- logger.remove()
- logger.add(sys.stderr, format="[{time:HH:mm:ss}] {level} {message}", level="INFO")
- logger.add("./logs/maintenance_{time:YYYYMMDD}.log", encoding="utf-8", rotation="00:00",
- format="[{time:YYYY-MM-DD HH:mm:ss}] {level} {message}", level="INFO", retention="7 day")
- # ---- 配置(待主公确认)----
- EXCEL_KEEP_DAYS = 30 # Excel 保留天数:删除早于此的报告 xlsx
- EXCEL_DIRS = [ # 要清理的报告输出目录
- os.path.join(_ROOT, "output", "onsale"), # 在售报告
- os.path.join(_ROOT, "reports", "sold_report"), # 已售日/周/月报等
- ]
- EXCEL_KEEP_FILES = {"球队.xlsx"} # 固定产物白名单:不按天清理(非报告,勿删)
- COOLING_REVIVE_MINUTES = 10 # cooling 巡检间隔(分钟)
- KEEPALIVE_SCAN_HOURS = 1 # token 保活巡检间隔(小时)
- KEEPALIVE_IDLE_HOURS = 6 # 闲置超过该小时数的 healthy 号才主动刷新(远小于 refresh_token 过期时长)
- EXCEL_CLEAN_AT = "03:00" # 每天几点清理 Excel
- def keepalive_pool(log) -> None:
- """巡检:对闲置超过 KEEPALIVE_IDLE_HOURS 的 healthy 号主动刷新 token,避免 refresh_token 闲到过期被判死。
- Args:
- log: 日志对象。
- """
- try:
- pool = MySQLConnectionPool(log=log)
- AccountPool(pool, log=log, task_tag="maintenance").keepalive_idle(KEEPALIVE_IDLE_HOURS, log=log)
- except Exception as e:
- log.warning(f"[保活] token 保活异常: {e}")
- def revive_pool(log) -> None:
- """巡检:把账号池里 cooling 冷却到期的账号恢复 healthy。
- Args:
- log: 日志对象。
- """
- try:
- pool = MySQLConnectionPool(log=log)
- AccountPool(pool, log=log, task_tag="maintenance").revive_cooling()
- log.info("[巡检] cooling 冷却到期账号已恢复检查完成")
- except Exception as e:
- log.warning(f"[巡检] cooling 恢复异常: {e}")
- def clean_old_excel(log) -> None:
- """清理各报告目录下超过 EXCEL_KEEP_DAYS 天的 .xlsx(排除白名单固定产物)。
- Args:
- log: 日志对象。
- """
- cutoff = time.time() - EXCEL_KEEP_DAYS * 86400
- removed = 0
- for d in EXCEL_DIRS:
- if not os.path.isdir(d):
- continue
- for f in glob.glob(os.path.join(d, "*.xlsx")):
- if os.path.basename(f) in EXCEL_KEEP_FILES: # 固定产物不清
- continue
- try:
- if os.path.getmtime(f) < cutoff:
- os.remove(f)
- removed += 1
- log.info(f"[清理] 删除旧报告 {f}")
- except Exception as e:
- log.warning(f"[清理] 删除失败 {f}: {e}")
- log.info(f"[清理] Excel 清理完成,删除 {removed} 个(保留最近 {EXCEL_KEEP_DAYS} 天)")
- def main() -> None:
- """注册两个维护定时任务并进入单循环调度。"""
- schedule.every(COOLING_REVIVE_MINUTES).minutes.do(revive_pool, logger) # cooling 巡检
- schedule.every(KEEPALIVE_SCAN_HOURS).hours.do(keepalive_pool, logger) # token 保活巡检
- schedule.every().day.at(EXCEL_CLEAN_AT).do(clean_old_excel, logger) # Excel 清理
- logger.info(f"运维守护启动:cooling 巡检每 {COOLING_REVIVE_MINUTES} 分钟 / "
- f"token 保活每 {KEEPALIVE_SCAN_HOURS} 小时(闲置>{KEEPALIVE_IDLE_HOURS}h 刷新) / "
- f"Excel 清理每天 {EXCEL_CLEAN_AT}(保留 {EXCEL_KEEP_DAYS} 天)")
- revive_pool(logger) # 启动即巡检一次
- keepalive_pool(logger) # 启动即保活一次
- while True:
- schedule.run_pending()
- time.sleep(1)
- if __name__ == "__main__":
- main()
|