first commit

This commit is contained in:
zmq
2026-05-26 14:16:16 +08:00
committed by Jerry Yan
commit 535d7f4ac5
40 changed files with 4571 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
import asyncio
import signal
import sys
from loguru import logger
from src.config.settings import get_settings
from src.config.accounts import load_accounts_config
from src.notifications.service import NotificationService
from src.utils.logger import setup_logger
from src.task_queue.dispatcher import TaskDispatcher
from src.task_queue.delay_zset_listener import RedisZSetDelayListener
from src.task_queue.listener import RedisStreamListener
from src.task_queue.redis_manager import get_redis_manager
async def main() -> None:
"""
应用程序主入口函数。
- 初始化定制的 Loguru 结构化日志系统。
- 校验 Pydantic 配置及多站点账号 YAML 配置文件是否正确。
- 针对不同操作系统平台注册优雅退出信号处理器。
- 启动 Redis Stream 消息队列持续消费监听循环。
"""
# 1. 初始化按天分割日志系统
setup_logger()
logger.info("正在初始化日本采购自动化守护进程...")
# 2. 启动前快速校验:验证全局环境变量设置与账号配置文件
try:
settings = get_settings()
logger.info(f"应用配置加载成功: APP_ENV={settings.app_env.upper()}")
NotificationService(settings).validate_configuration()
# 解析并校验 accounts.yaml
load_accounts_config()
await get_redis_manager().initialize()
except Exception as e:
logger.critical(f"由于配置错误,守护进程启动失败: {e}")
sys.exit(1)
# 3. 实例化核心任务分发器与 Stream 监听器
dispatcher = TaskDispatcher()
stream_listener = RedisStreamListener(dispatcher)
delay_listener = RedisZSetDelayListener(dispatcher)
# 4. 优雅停机信号处理 (跨平台支持)
loop = asyncio.get_running_loop()
def shutdown_handler(sig_name: str) -> None:
logger.warning(f"接收到系统退出信号 '{sig_name}'。正在启动优雅停机流程...")
# 触发监听器停止的协程任务
asyncio.create_task(stream_listener.stop())
asyncio.create_task(delay_listener.stop())
# Windows 默认的 Proactor 事件循环不支持 add_signal_handler。
if sys.platform != "win32":
logger.debug("正在注册 Unix 系统信号监听器 (SIGINT / SIGTERM)。")
for sig in (signal.SIGINT, signal.SIGTERM):
try:
loop.add_signal_handler(sig, lambda s=sig: shutdown_handler(s.name))
except NotImplementedError:
pass
else:
logger.debug("运行于 Windows 平台。跳过系统信号注册,将直接捕获 KeyboardInterrupt。")
# 5. 启动消息监听消费主循环
try:
stream_task = asyncio.create_task(stream_listener.listen())
delay_task = asyncio.create_task(delay_listener.listen())
await asyncio.gather(stream_task, delay_task)
except asyncio.CancelledError:
logger.warning("监听器轮询执行被事件循环取消。")
except Exception as e:
logger.exception(f"主守护进程轮询中发生致命崩溃: {e}")
finally:
# 保证安全回收与连接断开
await asyncio.gather(
stream_listener.stop(),
delay_listener.stop(),
get_redis_manager().close(),
return_exceptions=True,
)
logger.success("日本采购自动化守护进程已安全优雅退出。")
if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
logger.warning("捕获到键盘手动中断。立即退出程序...")
sys.exit(0)