facc04aa65
本提交为 .git 对象库损坏后的重建提交,内容等价于原先三个本地提交 (5e2fd4c2 / 57a53c98 / 5d7e1873)的累积结果,未做任何额外改动。 一、docs 结构整改(整改 #14) 根因:重构时新结构为 untracked 文件,执行 git stash(未带 -u)未纳入, 随后 git reset 拉回 HEAD 旧 tracked 树,导致旧树复活、新旧两棵目录 树并存于 docs/,共 791 文件、双分类体系冲突。 修复动作: - b2 同名异主题文件改名迁移保全 9 个 - C 类 39 个孤立文件按主题正确归类 - A/B1 类 222 个重复文件删除(新结构已有内容副本) - 9 个旧独有空目录删除 - 270 处内部引用按 verified 映射改写 - 整改记录 #14 登记于 04-运维文档/部署运维 结果:docs 791 → 569 文件,顶层仅规范 8 类 + 治理文件,单树恢复。 残留:约 20 处指向从未存在文件的陈旧死链,归入独立文档卫生任务。 二、compose 双目录对齐(消除踩坑 A) - docker-compose.yml:nginx 前端挂载全部由根目录 frontend-*/dist 改为 src/frontend-*/dist(h5 / agent / admin / terminal) - docker-compose.dev.yml:dev 服务 build context 与卷同步改 src/ - 效果:本地 docker compose up 不再把根目录 stale dist 挂回, 与线上一致,分叉隐患消除(已 docker compose config 校验通过) 防复发铁律: - 重构须提交;仓库修复须 git stash -u 或先 commit - 新结构须 git add 并提交,避免再次 untracked 复活 - H5 改动只动 src/frontend-h5/,禁改根目录遗留 frontend-*/
249 lines
8.4 KiB
Python
249 lines
8.4 KiB
Python
# =============================================================================
|
|
# 企微智能IT支持服务台 — FastAPI 依赖注入 & 共享服务管理
|
|
# =============================================================================
|
|
# 说明:集中管理 Redis、AIService、WecomService、AIHandler 等共享服务实例,
|
|
# 解决原代码中多处手动创建/关闭服务导致的资源泄漏问题。
|
|
#
|
|
# 提供:
|
|
# 1. 应用级共享实例的初始化和清理(在 main.py 的 lifespan 中调用)
|
|
# 2. FastAPI Depends 依赖注入函数(供 h5.py 等路由模块使用)
|
|
# 3. 模块级访问函数(供 wecom_callback.py 等非 DI 场景使用)
|
|
#
|
|
# 资源管理原则:
|
|
# - 共享实例在应用启动时创建一次,关闭时清理一次
|
|
# - 路由端点通过 Depends 获取共享实例,不再手动创建/关闭
|
|
# - httpx.AsyncClient 连接池由共享 AIService 和 WecomService 复用
|
|
# =============================================================================
|
|
|
|
import logging
|
|
from typing import Optional
|
|
|
|
import redis.asyncio as aioredis
|
|
from fastapi import Depends
|
|
|
|
from app.config import settings
|
|
from app.services.ai_service import AIService
|
|
from app.services.ai_handler import AIHandler
|
|
from app.services.wecom_service import WecomService
|
|
from app.services.wingman_service import WingmanService
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 全局共享实例(由 init_shared_services / cleanup_shared_services 管理)
|
|
# --------------------------------------------------------------------------
|
|
_redis_client: Optional[aioredis.Redis] = None
|
|
_ai_service: Optional[AIService] = None
|
|
_wecom_service: Optional[WecomService] = None
|
|
_ai_handler: Optional[AIHandler] = None
|
|
_wingman_service: Optional[WingmanService] = None
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 应用生命周期管理(在 main.py 的 lifespan 中调用)
|
|
# --------------------------------------------------------------------------
|
|
async def init_shared_services() -> None:
|
|
"""初始化共享服务实例(在应用启动时调用)。
|
|
|
|
创建 Redis 客户端、AIService、WecomService、AIHandler 的全局共享实例。
|
|
所有实例在应用运行期间复用,避免每次请求重新创建导致资源泄漏。
|
|
|
|
如果 Redis 不可用,会记录警告但不阻塞启动(降级运行)。
|
|
"""
|
|
global _redis_client, _ai_service, _wecom_service, _ai_handler, _wingman_service
|
|
|
|
# 1. 初始化 Redis 客户端(可能不可用,容错处理)
|
|
try:
|
|
_redis_client = aioredis.from_url(settings.redis_url)
|
|
await _redis_client.ping()
|
|
logger.info("共享 Redis 客户端初始化成功")
|
|
except Exception as e:
|
|
logger.warning(f"共享 Redis 客户端初始化失败(降级运行): {e}")
|
|
_redis_client = None
|
|
|
|
# 2. 初始化 AI 服务(共享 httpx 连接池)
|
|
_ai_service = AIService()
|
|
logger.info("共享 AIService 初始化成功")
|
|
|
|
# 3. 初始化企微服务(使用共享 Redis)
|
|
_wecom_service = WecomService(_redis_client)
|
|
logger.info("共享 WecomService 初始化成功")
|
|
|
|
# 4. 初始化 AI 处理器(统一 AI 调用逻辑)
|
|
_ai_handler = AIHandler(_ai_service)
|
|
logger.info("共享 AIHandler 初始化成功")
|
|
|
|
# 5. 初始化 Wingman 服务(坐席智能副驾驶,独立 Agent)
|
|
_wingman_service = WingmanService()
|
|
logger.info("共享 WingmanService 初始化成功")
|
|
|
|
|
|
async def cleanup_shared_services() -> None:
|
|
"""清理共享服务实例(在应用关闭时调用)。
|
|
|
|
按照依赖关系逆序关闭:AIHandler → WecomService → AIService → Redis。
|
|
确保所有 httpx 连接池和 Redis 连接被正确释放。
|
|
"""
|
|
global _redis_client, _ai_service, _wecom_service, _ai_handler, _wingman_service
|
|
|
|
# AIHandler 无需关闭(无外部资源)
|
|
_ai_handler = None
|
|
|
|
if _wecom_service:
|
|
try:
|
|
await _wecom_service.close()
|
|
except Exception as e:
|
|
logger.warning(f"WecomService 关闭异常: {e}")
|
|
_wecom_service = None
|
|
|
|
if _ai_service:
|
|
try:
|
|
await _ai_service.close()
|
|
except Exception as e:
|
|
logger.warning(f"AIService 关闭异常: {e}")
|
|
_ai_service = None
|
|
|
|
if _wingman_service:
|
|
try:
|
|
await _wingman_service.close()
|
|
except Exception as e:
|
|
logger.warning(f"WingmanService 关闭异常: {e}")
|
|
_wingman_service = None
|
|
|
|
if _redis_client:
|
|
try:
|
|
await _redis_client.close()
|
|
except Exception as e:
|
|
logger.warning(f"Redis 客户端关闭异常: {e}")
|
|
_redis_client = None
|
|
|
|
logger.info("共享服务实例清理完成")
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# 模块级访问函数(供 wecom_callback.py 等非 DI 场景使用)
|
|
# --------------------------------------------------------------------------
|
|
def get_shared_redis() -> Optional[aioredis.Redis]:
|
|
"""获取共享 Redis 客户端(非 DI 方式)。
|
|
|
|
适用于无法使用 FastAPI Depends 的场景(如回调处理器)。
|
|
|
|
Returns:
|
|
Optional[aioredis.Redis]: Redis 客户端实例,不可用时为 None
|
|
"""
|
|
return _redis_client
|
|
|
|
|
|
def get_shared_ai_service() -> Optional[AIService]:
|
|
"""获取共享 AI 服务(非 DI 方式)。
|
|
|
|
Returns:
|
|
Optional[AIService]: AI 服务实例,不可用时为 None
|
|
"""
|
|
return _ai_service
|
|
|
|
|
|
def get_shared_wecom_service() -> Optional[WecomService]:
|
|
"""获取共享企微服务(非 DI 方式)。
|
|
|
|
Returns:
|
|
Optional[WecomService]: 企微服务实例,不可用时为 None
|
|
"""
|
|
return _wecom_service
|
|
|
|
|
|
def get_shared_ai_handler() -> Optional[AIHandler]:
|
|
"""获取共享 AI 处理器(非 DI 方式)。
|
|
|
|
Returns:
|
|
Optional[AIHandler]: AI 处理器实例,不可用时为 None
|
|
"""
|
|
return _ai_handler
|
|
|
|
|
|
def get_shared_wingman_service() -> Optional[WingmanService]:
|
|
"""获取共享 Wingman 服务(非 DI 方式)。
|
|
|
|
Returns:
|
|
Optional[WingmanService]: Wingman 服务实例,不可用时为 None
|
|
"""
|
|
return _wingman_service
|
|
|
|
|
|
# --------------------------------------------------------------------------
|
|
# FastAPI 依赖注入函数(供路由端点使用)
|
|
# --------------------------------------------------------------------------
|
|
async def dep_redis() -> Optional[aioredis.Redis]:
|
|
"""获取共享 Redis 客户端(FastAPI 依赖注入)。
|
|
|
|
Redis 不可用时返回 None,调用方根据业务需要决定是否降级处理。
|
|
|
|
Returns:
|
|
Optional[aioredis.Redis]: Redis 客户端实例
|
|
"""
|
|
return _redis_client
|
|
|
|
|
|
async def dep_ai_service() -> AIService:
|
|
"""获取共享 AI 服务(FastAPI 依赖注入)。
|
|
|
|
AI 服务不可用时抛出异常(AI 为核心功能,不可降级)。
|
|
|
|
Returns:
|
|
AIService: AI 服务实例
|
|
|
|
Raises:
|
|
RuntimeError: AI 服务未初始化
|
|
"""
|
|
if _ai_service is None:
|
|
raise RuntimeError("AI 服务未初始化,请检查应用启动日志")
|
|
return _ai_service
|
|
|
|
|
|
async def dep_wecom_service() -> WecomService:
|
|
"""获取共享企微服务(FastAPI 依赖注入)。
|
|
|
|
企微服务不可用时抛出异常(核心通信功能,不可降级)。
|
|
|
|
Returns:
|
|
WecomService: 企微服务实例
|
|
|
|
Raises:
|
|
RuntimeError: 企微服务未初始化
|
|
"""
|
|
if _wecom_service is None:
|
|
raise RuntimeError("企微服务未初始化,请检查应用启动日志")
|
|
return _wecom_service
|
|
|
|
|
|
async def dep_ai_handler() -> AIHandler:
|
|
"""获取共享 AI 处理器(FastAPI 依赖注入)。
|
|
|
|
AI 处理器不可用时抛出异常(核心功能,不可降级)。
|
|
|
|
Returns:
|
|
AIHandler: AI 处理器实例
|
|
|
|
Raises:
|
|
RuntimeError: AI 处理器未初始化
|
|
"""
|
|
if _ai_handler is None:
|
|
raise RuntimeError("AI 处理器未初始化,请检查应用启动日志")
|
|
return _ai_handler
|
|
|
|
|
|
async def dep_wingman_service() -> WingmanService:
|
|
"""获取共享 Wingman 服务(FastAPI 依赖注入)。
|
|
|
|
Wingman 服务不可用时抛出异常。
|
|
|
|
Returns:
|
|
WingmanService: Wingman 服务实例
|
|
|
|
Raises:
|
|
RuntimeError: Wingman 服务未初始化
|
|
"""
|
|
if _wingman_service is None:
|
|
raise RuntimeError("Wingman 服务未初始化,请检查应用启动日志")
|
|
return _wingman_service
|