Files
rag/api/__init__.py
lacerate551 183a57e7f1 refactor(api): 统一响应格式迁移 + 异步任务系统 + 状态码体系完善
- 将全部路由文件(12个)的 jsonify 响应迁移至 success_response/error_response 统一格式
- 修复 sync_routes.py error_response 参数错误(P0)
- 新增异步任务系统:task_registry + task_routes
- 新增状态码:TASK_NOT_FOUND(4014)、TASK_CONFLICT(4015)、REINDEX_ERROR(5040)
- 修正 task_routes/exam_pkg 中语义不匹配的状态码
- 更新 curl 测试手册、后端对接规范文档
- 添加缓存性能报告和 Redis 迁移计划
2026-06-05 22:56:00 +08:00

263 lines
8.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
API 路由层 — Flask 应用工厂
本模块实现 Flask 应用工厂模式,负责:
- 创建和配置 Flask 应用实例
- 初始化核心服务(同步服务)
- 注册所有 API Blueprint
- 配置前端静态文件路由
核心路由模块:
- chat_routes.py : 问答接口 (/chat, /rag, /rag/stream, /search)
- kb_routes.py : 向量库管理 (/collections, /documents/sync)
- document_routes.py: 文档管理 (/documents/upload, /documents/list)
- sync_routes.py : 同步服务 (/sync, /sync/status)
- image_routes.py : 图片服务 (/images/*)
- exam_pkg/api.py : 出题系统 (/exam/generate, /exam/grade)
架构说明:
- 会话管理、审计日志、反馈系统由后端服务负责
- 权限验证由后端网关完成Header 注入)
- RAG 服务无状态,不存储用户数据
Example:
>>> from api import create_app
>>> app = create_app()
>>> app.run(host='0.0.0.0', port=5001)
"""
import sys
import os
import logging
from typing import Optional
# 确保项目根目录在路径中
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
logger = logging.getLogger(__name__)
def create_app() -> 'Flask':
"""
Flask 应用工厂函数
创建并配置 Flask 应用实例,完成以下初始化步骤:
1. 创建 Flask 应用,配置 CORS
2. 初始化 Repository会话存储
3. 初始化核心服务(同步服务)
4. 注册所有 API Blueprint
5. 配置前端静态文件路由
6. 执行生产环境配置校验
Returns:
Flask: 配置完成的 Flask 应用实例
Example:
>>> app = create_app()
>>> app.run(host='0.0.0.0', port=5001)
"""
from flask import Flask, send_from_directory
from flask_cors import CORS
from config import ENABLE_SESSION, ENABLE_FEEDBACK, IS_PROD
# 静态文件目录(前端)
static_folder = os.path.join(PROJECT_ROOT, 'chat-ui')
app = Flask(__name__, static_folder=static_folder, static_url_path='')
# CORS 配置:生产环境限制来源,开发环境允许全部
if IS_PROD:
cors_origins = os.environ.get('CORS_ORIGINS', '').split(',') if os.environ.get('CORS_ORIGINS') else []
if cors_origins:
CORS(app, origins=cors_origins)
else:
CORS(app) # 未配置时仍允许全部,但记录警告
logger.warning("生产环境未配置 CORS_ORIGINSCORS 允许所有来源")
else:
CORS(app)
# ==================== Repository 依赖注入 ====================
# 会话存储开发环境用SQLite生产环境无状态
if ENABLE_SESSION:
from repositories.sqlite_session_repo import SQLiteSessionRepo
app.session_repo = SQLiteSessionRepo()
logger.info("会话存储: SQLite (开发环境)")
else:
from repositories.stateless_session_repo import StatelessSessionRepo
app.session_repo = StatelessSessionRepo()
logger.info("会话存储: 无状态 (生产环境)")
# ==================== 核心服务初始化 ====================
# 同步服务
try:
from knowledge.sync import KnowledgeSyncService
from config import DOCUMENTS_PATH
sync_service = KnowledgeSyncService(documents_path=DOCUMENTS_PATH)
app.config['SYNC_SERVICE'] = sync_service
logger.info("知识库同步服务已初始化")
except Exception as e:
app.config['SYNC_SERVICE'] = None
logger.warning(f"知识库同步服务未启用: {e}")
# 会话管理器(仅开发环境)
if ENABLE_SESSION:
try:
from services.session import SessionManager
session_manager = SessionManager()
app.config['SESSION_MANAGER'] = session_manager
logger.info("会话管理器已初始化")
except Exception as e:
app.config['SESSION_MANAGER'] = None
logger.warning(f"会话管理器初始化失败: {e}")
# ==================== 注册 Blueprint ====================
# 核心 API
from api.chat_routes import chat_bp
from api.kb_routes import kb_bp
from api.document_routes import document_bp
from api.sync_routes import sync_bp
app.register_blueprint(chat_bp)
app.register_blueprint(kb_bp)
app.register_blueprint(document_bp)
app.register_blueprint(sync_bp)
# 图片服务
from api.image_routes import image_bp
app.register_blueprint(image_bp)
# 异步任务查询
from api.task_routes import task_bp
app.register_blueprint(task_bp)
# 健康检查
from api.auth_routes import auth_bp
app.register_blueprint(auth_bp)
# 会话管理(仅开发环境)
if ENABLE_SESSION:
try:
from api.session_routes import session_bp
app.register_blueprint(session_bp)
logger.info("会话管理 API 已启用")
except ImportError as e:
logger.info(f"会话管理 API 未加载: {e}")
# 审计日志(仅开发环境,依赖 session.db
if ENABLE_SESSION:
try:
from api.audit_routes import audit_bp
app.register_blueprint(audit_bp)
logger.info("审计日志 API 已启用")
except ImportError as e:
logger.info(f"审计日志 API 未加载: {e}")
# 反馈系统(开发和生产环境都启用)
if ENABLE_FEEDBACK:
try:
from api.feedback_routes import feedback_bp
app.register_blueprint(feedback_bp)
logger.info("反馈系统 API 已启用")
except ImportError as e:
logger.info(f"反馈系统 API 未加载: {e}")
# 出题系统(可选)
try:
from exam_pkg.api import exam_bp
app.register_blueprint(exam_bp, url_prefix='/exam')
logger.info("出题系统 API 已启用: /exam")
except ImportError as e:
logger.info(f"出题系统 API 未加载: {e}")
# ==================== 生产环境启动校验 ====================
if IS_PROD:
_validate_production_config()
# ==================== 前端静态文件路由 ====================
# 首页
@app.route('/')
def serve_index():
"""首页"""
return send_from_directory(static_folder, 'index.html')
# 静态文件(需要明确指定,避免与 API 路由冲突)
@app.route('/app.js')
def serve_app_js():
return send_from_directory(static_folder, 'app.js')
@app.route('/style.css')
def serve_style_css():
return send_from_directory(static_folder, 'style.css')
@app.route('/exam.html')
def serve_exam_html():
return send_from_directory(static_folder, 'exam.html')
@app.route('/exam.js')
def serve_exam_js():
return send_from_directory(static_folder, 'exam.js')
@app.route('/api-test.html')
def serve_api_test_html():
return send_from_directory(static_folder, 'api-test.html')
@app.route('/api-test.js')
def serve_api_test_js():
return send_from_directory(static_folder, 'api-test.js')
# ==================== 启动信息 ====================
_print_startup_info(app)
return app
def _validate_production_config() -> None:
"""
生产环境启动前配置校验
检查必要的配置项是否存在:
- DASHSCOPE_API_KEY: 大模型调用必需
Raises:
RuntimeError: 缺少必需的配置项
"""
import os
from config import DASHSCOPE_API_KEY
# 检查环境变量或配置文件中的 API Key
has_key = os.getenv("DASHSCOPE_API_KEY") or os.environ.get("DASHSCOPE_API_KEY") or DASHSCOPE_API_KEY
if not has_key or has_key == "":
raise RuntimeError("Missing DASHSCOPE_API_KEY in production environment")
logger.info("Configuration validated")
def _print_startup_info(app: 'Flask') -> None:
"""
打印启动信息摘要
在应用启动时输出注册的路由数量和主要端点列表,
便于运维人员确认服务状态。
Args:
app: Flask 应用实例
"""
route_count = len([rule for rule in app.url_map.iter_rules() if rule.endpoint != 'static'])
logger.info(f"应用初始化完成,共注册 {route_count} 个路由")
logger.info(" 问答接口: /chat, /rag, /rag/stream, /search")
logger.info(" 向量库: /collections, /collections/<name>")
logger.info(" 文档管理: /documents/upload, /documents/list, /documents/*")
logger.info(" 切片管理: /chunks/*")
logger.info(" 同步服务: /sync, /sync/status")
logger.info(" 图片服务: /images/*")
logger.info(" 任务查询: /tasks, /tasks/<id>, /tasks/<id>/progress")
logger.info(" 健康检查: /health")