包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。 - 工作台画布:节点拖放、连线模式、右键菜单、AI 助手 - 后端:连接器 API、专员种子数据 - 导航:左侧导航、工坊、市场、控制台
7.3 KiB
7.3 KiB
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="eaisalestrain_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()