Files
pj0235-eai_agentplatform/docs/02_Architecture/AR01_Backend_Arch.md
T
eaiadminandClaude Code 0455f064ac feat: 新增语音转文字(ASR)功能
- 后端:新增 /api/audio/transcribe 接口,调用 Ollama whisper 进行语音识别
- 前端:新增 AudioTranscribePage.vue 页面,支持 MP3/WAV/M4A/OGG/FLAC 等格式
- 注册路由、工具卡片、智能助手欢迎语更新

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-09-14 00:53:36 +08:00

7.3 KiB
Raw Blame History

AR01 — 后端架构设计

版本:V1.1 | 框架:FastAPI + SQLAlchemy + MySQL 8.0 参考:pj006-zhilianyuan2 的 BE01_backend_arch + main.py 装配模式

⚠️ 本文档为 V1.1 设计期历史快照,不再反映当前实现。 后端已重写为 Go + Gin + GORM + MySQL 8.0 + FAISS,以 docs/changelog.md(V1.2)、docs/db_schema.md、docs/deploy.md 为准;下文 FastAPI/Python 结构与 backend/ 路径仅作设计参考。


1. 架构分层

┌─────────────────────────────────────────────┐
│              API 路由层 (routes)              │
│  auth / company_train / product / course     │
│  exam / media / ai_chat / system             │
├─────────────────────────────────────────────┤
│            Pydantic 模型层 (schemas)          │
│  请求/响应模型,统一响应格式 Envelope         │
├─────────────────────────────────────────────┤
│             服务层 (services)                 │
│  exam_service / media_service / ai_service    │
├─────────────────────────────────────────────┤
│         SQLAlchemy ORM 模型层 (models)        │
│  User / Product / Course / MediaFile / ...    │
├─────────────────────────────────────────────┤
│            核心层 (core)                      │
│  config / security / deps                    │
├─────────────────────────────────────────────┤
│           MySQL 8.0 + data/media             │
└─────────────────────────────────────────────┘

2. 目录结构

backend/
├── app/
│   ├── main.py                 # FastAPI 应用装配 + CORS + 异常处理
│   ├── api/                    # 路由层
│   │   ├── auth.py             # /api/auth/* — 登录/注册/me
│   │   ├── company_train.py    # /api/company-train/*
│   │   ├── product.py          # /api/products/*
│   │   ├── sales_train.py      # /api/courses/*
│   │   ├── exam.py             # /api/exam/* — 题库/组卷/考试/记录
│   │   ├── media.py            # /api/media/* — 上传/预览/审批
│   │   ├── ai_chat.py          # /api/ai-chat/* — PathCoach SSE
│   │   └── system.py           # /api/system/* — 用户/成绩/配置
│   ├── models/                 # SQLAlchemy ORM 模型
│   │   ├── user.py
│   │   ├── product.py
│   │   ├── course.py
│   │   ├── media_file.py
│   │   ├── knowledge_chunk.py
│   │   ├── question.py
│   │   ├── exam_paper.py
│   │   └── exam_record.py
│   ├── schemas/                # Pydantic 请求/响应模型
│   ├── services/               # 业务逻辑层
│   │   ├── media_service.py    # 上传/转换/提取
│   │   ├── ai_service.py       # LLM 调用 + 知识检索
│   │   └── exam_service.py     # 题库/组卷/判分/记录
│   ├── core/                   # 核心基础设施
│   │   ├── config.py           # .env + 系统参数读取
│   │   ├── security.py         # JWT 签发/校验 + bcrypt
│   │   └── deps.py             # FastAPI Depends(get_db / get_current_user)
│   └── utils/                  # 工具函数
├── data/media/                 # 文件存储(git忽略)
│   ├── upload/
│   └── _preview_cache/
├── requirements.txt
└── .env

3. 应用装配模式(main.py)

参考 zhilianyuan2 的模式,每个模块的 router 独立注册:

from fastapi import FastAPI
from app.api import auth, company_train, product, sales_train
from app.api import exam, media, ai_chat, system

app = FastAPI(title="eai_agentplatform_app", version="1.1.0")

# 异常处理器
@app.exception_handler(AppError)
def handle_app_error(request, exc):
    return JSONResponse(status_code=exc.status_code, content={...})

# 路由注册
app.include_router(auth.router)
app.include_router(company_train.router)
app.include_router(product.router)
app.include_router(sales_train.router)
app.include_router(exam.router)
app.include_router(media.router)
app.include_router(ai_chat.router)
app.include_router(system.router)

4. 依赖注入模式

参考 zhilianyuan2 的 auth/dependencies.py:

# core/deps.py
async def get_current_user(
    credentials: HTTPAuthorizationCredentials | None = Depends(HTTPBearer(auto_error=False)),
    db: Session = Depends(get_db),
) -> User:
    """解析 JWT → 校验用户状态 → 返回 User"""
    if credentials is None:
        raise AuthError("缺少 Authorization Bearer 令牌")
    payload = decode_access_token(credentials.credentials, settings)
    user = db.query(User).filter(User.username == payload["sub"]).first()
    if user is None or user.status != "active":
        raise AuthError("用户不存在或已禁用")
    return user

def require_admin(user: User = Depends(get_current_user)) -> User:
    """管理员角色守卫"""
    if user.role != "admin":
        raise ForbiddenError("需要管理员权限")
    return user

5. API 路由前缀

路由前缀 模块 说明
/api/auth/* auth 登录/注册/当前用户
/api/company-train/* company_train 公司介绍内容
/api/products/* product 产品 CRUD + 导入
/api/courses/* sales_train 课程 CRUD + 绑定产品
/api/exam/* exam 题库/组卷/考试/记录
/api/media/* media 上传/预览/审批/状态
/api/ai-chat/* ai_chat PathCoach 流式对话
/api/system/* system 用户/成绩/配置
/api/health — 健康检查

6. 异步任务模式

文档转换管线(审批通过后异步执行):

# services/media_service.py
import threading

def _async_convert_and_extract(media_file_id: int):
    """审批通过后异步执行:文档转 PDF → 文本提取 → 切片入库"""
    with Session() as db:
        media = db.query(MediaFile).get(media_file_id)
        # 1. 调用 LibreOffice 转 PDF
        pdf_path = libreoffice_convert(media.stored_path)
        # 2. PyMuPDF 提取文本
        text = pymupdf_extract(pdf_path)
        # 3. 按段落切片写入 knowledge_chunk
        chunks = split_into_chunks(text)
        for i, chunk in enumerate(chunks):
            db.add(KnowledgeChunk(media_file_id=media.id, ...))
        media.extracted = True
        db.commit()

def approve_media(media_file_id: int, auditor_id: int):
    """审批通过 → 触发异步转换"""
    media.status = "approved"
    media.audit_by = auditor_id
    media.audit_at = datetime.utcnow()
    db.commit()
    # 启动异步任务
    threading.Thread(target=_async_convert_and_extract, args=(media_file_id,)).start()