init: 数字员工平台初始代码

包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。
- 工作台画布:节点拖放、连线模式、右键菜单、AI 助手
- 后端:连接器 API、专员种子数据
- 导航:左侧导航、工坊、市场、控制台
This commit is contained in:
eaiadmin
2026-08-18 20:19:58 +08:00
commit 4e8817d768
239 changed files with 48631 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
# 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 独立注册:
```python
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`:
```python
# 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. 异步任务模式
文档转换管线(审批通过后异步执行):
```python
# 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()
```
+175
View File
@@ -0,0 +1,175 @@
# AR02 — 前端架构设计
> **版本:V1.1 | 左导航 + 中间工作区 + 右 AI 侧栏 | Vue3 + Vite + Element Plus**
> **参考:pj006-zhilianyuan2 frontend-orgadmin 三栏布局模式**
---
## 1. 目录结构
```
frontend/
├── public/
│ └── static-lib/ # 本地 pdf.js(无 CDN)
├── src/
│ ├── api/ # API 调用层(axios 封装)
│ │ ├── auth.js
│ │ ├── product.js
│ │ ├── course.js
│ │ ├── exam.js
│ │ ├── media.js
│ │ ├── aiChat.js
│ │ └── system.js
│ ├── components/ # 通用组件
│ │ └── MaterialSuggestUpload.vue # 员工提交素材弹窗
│ ├── layout/ # 全局布局
│ │ ├── MainLayout.vue # 三栏布局(左导航 + 内容 + AI 侧栏)
│ │ ├── SideNav.vue # 左边栏导航(含品牌 + 菜单 + 用户信息)
│ │ └── PathCoachPanel.vue # 右侧 AI 聊天框
│ ├── router/ # 路由
│ │ ├── index.js # 路由定义
│ │ └── guards.js # 路由守卫(角色/认证)
│ ├── store/ # 状态管理(Pinia)
│ │ ├── auth.js # 用户认证状态
│ │ └── aiChat.js # AI 聊天会话
│ ├── views/ # 页面视图
│ │ ├── home/ # 首页
│ │ ├── companyTrain/ # 公司介绍培训
│ │ ├── product/ # 产品知识
│ │ ├── salesTrain/ # 产品销售培训
│ │ ├── exam/ # 考试
│ │ ├── knowledge/ # 管理员-知识管理
│ │ └── system/ # 管理员-系统管理
│ ├── App.vue
│ └── main.js
├── index.html
├── vite.config.js
└── package.json
```
## 2. 布局结构
```
┌──────┬───────────────────────────────────────┬────────────────┐
│ 导航 │ │ │
│ ───── │ 主工作区 │ AI PathCoach │
│ 品牌 │ <router-view /> │ ──────────── │
│ │ │ 消息列表 │
│ 首页 │ │ │
│ 公司 │ │ [输入] [发送] │
│ 产品 │ │ │
│ 销售 │ │ [情景演练] │
│ 考试▼ │ │ [查佣金] │
│ │ │ [产品对比] │
│ ───── │ │ │
│ 知识▼ │ ← admin only │ │
│ 系统▼ │ ← admin only │ │
│ │ │ │
│ ───── │ │ │
│ 用户 │ [退出] │ │
└──────┴───────────────────────────────────────┴────────────────┘
```
收起 AI 面板状态:左边导航不变,主内容区占满剩余宽度,右下角浮动 [🤖 展开AI] 按钮。
## 3. 路由设计
| 路径 | 视图 | 角色 | 说明 |
|------|------|------|------|
| `/` | Home | all | 首页 |
| `/company-train` | CompanyTrain | all | 公司介绍培训 |
| `/products` | ProductList | all | 产品列表 |
| `/products/:id` | ProductDetail | all | 产品详情 |
| `/courses` | CourseList | all | 课程列表 |
| `/courses/:id` | CourseDetail | all | 课程详情 |
| `/exam/self-test` | ExamSelfTest | all | 自测练习 |
| `/exam/formal` | ExamFormal | all | 正式结业考试 |
| `/exam/my-records` | ExamMyRecord | all | 我的考试记录 |
| `/exam/questions` | ExamQuestionBank | admin | 题库管理 |
| `/knowledge/materials` | MaterialManage | admin | 课件素材管理 |
| `/knowledge/audit` | MaterialAuditList | admin | 素材审批列表 |
| `/system/users` | UserManage | admin | 用户账号管理 |
| `/system/exam-records` | ExamRecordManage | admin | 全部考试成绩 |
| `/system/config` | SystemConfig | admin | 系统参数配置 |
## 4. 路由守卫
```javascript
// router/guards.js
import { useAuthStore } from '@/store/auth'
router.beforeEach((to, from, next) => {
if (to.path === '/login') {
next()
return
}
const authStore = useAuthStore()
// 未登录 → 跳转登录页
if (!authStore.isLoggedIn) {
return next('/login')
}
// 管理员路由校验(前端仅作 UX 隐藏,非安全边界)
if (to.meta.requiresAdmin && authStore.user?.role !== 'admin') {
return next('/')
}
next()
})
```
## 5. API 调用模式
```javascript
// api/http.js — axios 封装
import axios from 'axios'
import { ElMessage } from 'element-plus'
const http = axios.create({ baseURL: '/api' })
http.interceptors.request.use(config => {
const token = localStorage.getItem('token')
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
http.interceptors.response.use(
res => res.data.data, // 解包 Envelope
err => {
const resp = err.response
if (resp?.status === 401) {
localStorage.removeItem('token')
window.location.href = '/login'
} else if (resp?.status === 501) {
// LLM 未配置等服务器配置错误
ElMessage.error(resp.data?.message || '服务未配置,请联系管理员')
}
return Promise.reject(resp?.data)
}
)
export default http
```
## 6. AI 聊天状态管理
```javascript
// store/aiChat.js
import { defineStore } from 'pinia'
export const useAiChatStore = defineStore('aiChat', {
state: () => ({
visible: true, // 是否展开
messages: [], // 对话历史
context: null, // 当前页面上下文(productId/courseId)
isStreaming: false, // 是否正在流式响应
}),
actions: {
toggle() { this.visible = !this.visible },
setContext(ctx) { this.context = ctx },
clearMessages() { this.messages = [] },
}
})
```
@@ -0,0 +1,83 @@
# AR03 — 数据库架构设计
> **版本:V1.1 | 引擎:MySQL 8.0 | ORM:SQLAlchemy(现为 GORM)**
> **完整建表 SQL 请见 docs/db_schema.md**
>
> **⚠️ 本文档为 V1.1 设计期历史快照。** 当前实现已切换为 **MySQL 8.0 + FAISS 向量检索**(ORM 由 SQLAlchemy 改为 GORM),并新增岗位/积分/证书/部门/消息等表,以 `docs/db_schema.md` 与 `docs/changelog.md`(V1.4–V1.7)为准。
---
## 1. ER 关系总图
```
user ──< media_file (submitter_id / audit_by)
user ──< exam_record (user_id)
product ──< course (related_product_id)
course ──< question (course_id, optional)
media_file ──< knowledge_chunk (media_file_id)
exam_paper ──< exam_record (paper_id)
```
## 2. 表清单
| # | 表名 | 说明 | 核心字段数 |
|---|------|------|-----------|
| 1 | `user` | 用户账号 | 7 |
| 2 | `product` | 产品信息 | 16 |
| 3 | `course` | 课程内容 | 16 |
| 4 | `media_file` | 素材文件元数据 | 16 |
| 5 | `knowledge_chunk` | AI 知识库文本块 | 6 |
| 6 | `question` | 题库题目 | 11 |
| 7 | `exam_paper` | 考试配置/组卷 | 11 |
| 8 | `exam_record` | 考试记录档案 | 12 |
| 9 | `system_config` | 系统参数配置 | 4 |
## 3. 索引策略
| 表 | 索引 | 类型 | 说明 |
|----|------|------|------|
| `user` | `role`, `status` | BTREE | 角色筛选、状态筛选 |
| `product` | `category`, `status` | BTREE | 分类筛选、状态筛选 |
| `course` | `category`, `status`, `related_product_id` | BTREE | 同上 |
| `media_file` | `status`, `submitter_id`, `(bind_type, bind_id)`, `extracted` | BTREE | 审批列表、绑定查询、提取状态 |
| `knowledge_chunk` | `media_file_id` | BTREE | 关联查询 |
| `knowledge_chunk` | `content` | **FULLTEXT** | AI 知识检索(MySQL 全文索引) |
| `question` | `domain`, `course_id`, `status` | BTREE | 知识域筛选、课程筛选 |
| `exam_paper` | `type`, `status` | BTREE | 考试类型筛选 |
| `exam_record` | `user_id`, `paper_id`, `passed`, `submitted_at` | BTREE | 用户查记录、管理员查全部 |
| `system_config` | `config_key` | UNIQUE | 键查值 |
## 4. AI 知识检索说明
V1.1 使用 **MySQL 全文索引**(FULLTEXT);现已升级为 **MySQL 全文索引(关键词)+ FAISS 向量检索(语义)双路混合召回**:
```sql
-- knowledge_chunk 表已建全文索引(关键词召回)
FULLTEXT INDEX ft_kc_content (content)
-- 检索查询
SELECT * FROM knowledge_chunk
WHERE MATCH(content) AGAINST(:keywords IN NATURAL LANGUAGE MODE)
LIMIT 10
```
**检索流程(当前):**
1. 用户提问 → 关键词 + embedding 向量
2. MySQL FULLTEXT 关键词召回 + FAISS 语义向量召回,双路融合排序
3. 匹配段落作为上下文注入 LLM Prompt
4. LLM 基于上下文生成回答
> **演进说明:** V1.1 曾为避免 embedding 依赖而仅用全文索引;平台升级后引入 FAISS 补足语义召回,见 `docs/db_schema.md`。
## 5. 文件存储策略
- **数据库只存元数据**,不存文件二进制
- 物理文件存储在 `backend/data/media/`
- 目录结构:
```
data/media/
├── upload/ # 上传文件存储(UUID 重命名)
└── _preview_cache/ # 预览缓存(LibreOffice 转 PDF 后存放)
```
- 文件命名:UUID 重命名,杜绝路径穿越
- 文件扩展名白名单:ppt / pptx / pdf / doc / docx / mp4 / png / jpg / jpeg
+140
View File
@@ -0,0 +1,140 @@
# AR04 — 部署架构设计
> **版本:V1.1 | 部署模式:纯本地离线**
> **完整部署步骤请见 docs/deploy.md**
>
> **⚠️ 本文档为 V1.1 设计期历史快照(Docker + FastAPI 拓扑),不再反映当前实现。** 当前部署为 **Go 单二进制 + MySQL 8.0 + FAISS + systemd + Clonezilla 整盘克隆**,无 Docker、无 Python 运行时,以 `docs/deploy.md` 为准。
---
## 1. 部署拓扑
```
┌──────────────────────┐
│ 内网员工浏览器 │
│ http://train.bosun │
└──────────┬───────────┘
│
┌─────▼──────┐
│ Nginx │
│ :80 / :443 │
│ │
│ · 前端静态 │
│ · API 反代 │
│ · SSE 支持 │
└──┬──────┬──┘
│ │
┌──────────────┘ └──────────────┐
│ │
┌─────▼──────┐ ┌────────▼────────┐
│ FastAPI │ │ Vue 静态打包 │
│ :8000 │ │ nginx html/ │
│ │ └─────────────────┘
│ · JWT │
│ · 业务 │
└──┬──┬──┬──┘
│ │ │
┌───────────┘ │ └──────────────┐
│ │ │
┌──▼─────┐ ┌────▼───────┐ ┌──────▼──────────┐
│ MySQL │ │ data/media │ │ LibreOffice │
│ 8.0 │ │ 文件存储 │ │ 预览容器 │
│ :3306 │ │ │ │ :8100 │
└────────┘ └────────────┘ └─────────────────┘
│
│ (文档转换)
▼
┌──────────────┐
│ 内网 LLM │
│ Ollama/ │
│ vLLM/网关 │
│ :11434 │
└──────────────┘
```
## 2. 服务清单
| 服务 | 端口 | 基础镜像/依赖 | 说明 |
|------|------|-------------|------|
| Nginx | 80/443 | nginx:alpine | HTTP 反代 + 前端静态资源 |
| FastAPI | 8000 | python:3.10 | 后端 API(uvicorn 启动) |
| MySQL | 3306 | mysql:8.0 | 数据库 |
| LibreOffice | 8100 | 自定义 Docker 镜像 | 文档转 PDF 预览 |
| LLM 服务 | 11434 | ollama/vllm | 内网 AI 推理 |
## 3. Nginx 关键配置
```nginx
# SPA 路由
location / {
try_files $uri $uri/ /index.html;
}
# API 反代 + SSE
location /api/ {
proxy_pass http://127.0.0.1:8000;
proxy_buffering off;
proxy_cache off;
proxy_read_timeout 300s;
}
# 媒体文件预览(仅内部)
location /media/ {
alias /opt/eaisalestrain/backend/data/media/;
internal;
}
client_max_body_size 2048M;
```
## 4. docker-compose 服务拓扑
```yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_DATABASE: eaisalestrain
MYSQL_USER: eaisalestrain
MYSQL_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
environment:
DATABASE_URL: mysql+pymysql://eaisalestrain:${DB_PASSWORD}@mysql:3306/eaisalestrain
JWT_SECRET: ${JWT_SECRET}
LLM_BASE_URL: http://llm-server:11434/v1
volumes:
- ./data/media:/app/data/media
depends_on:
- mysql
libreoffice:
image: libreoffice-preview:latest
volumes:
- ./data/media:/data/media
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./frontend/dist:/usr/share/nginx/html
- ./nginx.conf:/etc/nginx/conf.d/default.conf
depends_on:
- backend
```
## 5. 安全边界
| 层级 | 措施 |
|------|------|
| 网络 | 仅监听内网,不暴露公网端口 |
| 认证 | JWT token 校验 + bcrypt 密码 |
| 鉴权 | 后端 API role 校验(非前端) |
| 文件 | 白名单扩展名 + UUID 命名 + 只读预览 |
| 数据库 | 独立用户 + 最小权限 |
| LLM | 仅内网地址,严禁公网 API |
+16
View File
@@ -0,0 +1,16 @@
# 02_Architecture — 架构设计
> **命名规则:** `AR{NN}_{描述}.md`
> **用途:** 后端架构、前端架构、数据库架构、部署架构
>
> **⚠️ 本目录 AR 文档为 V1.1 设计期历史快照。** 当前实现已重写为 **Go + Gin + GORM + MySQL 8.0 + FAISS**,以 `docs/changelog.md`、`docs/db_schema.md`、`docs/deploy.md` 为准。
## 文件清单
| 文件 | 说明 |
|------|------|
| `README.md` | 本索引文件 |
| `AR01_Backend_Arch.md` | 后端架构(V1.1 FastAPI 快照;现为 Go + Gin + GORM) |
| `AR02_Frontend_Arch.md` | 前端架构(Vue3 + Element Plus 三栏布局) |
| `AR03_Database_Arch.md` | 数据库架构(MySQL 8.0 + FAISS 向量检索) |
| `AR04_Deploy_Arch.md` | 部署架构(V1.1 Docker 快照;现为单二进制 + systemd) |