# nest_langchain
**Repository Path**: vmto/nest_langchain
## Basic Information
- **Project Name**: nest_langchain
- **Description**: 简历答题是一个基于 pnpm workspace 的全栈简历演示项目。它把 VueFlow 无限画布、NestJS 模块化后端、跨端 Zod 契约、技术答题、RAG 检索和 AI 内容优化组织成一条可运行的完整业务链路。
- **Primary Language**: JavaScript
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-11-02
- **Last Updated**: 2026-11-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 简历答题 · 无限画布简历
简历答题是一个基于 pnpm workspace 的全栈简历演示项目。它把 VueFlow 无限画布、NestJS 模块化后端、跨端 Zod 契约、技术答题、RAG 检索和 AI 内容优化组织成一条可运行的完整业务链路。
项目包含两个应用和三个公共包:
- `apps/resume-web`:Vue 3 前端。
- `apps/resume-api`:NestJS 后端。
- `packages/contracts`:跨端类型、常量与运行时 Schema。
- `packages/reactivity`:Vue 响应式组合式函数。
- `packages/shared`:无框架通用工具。
## 实际功能
### 用户与鉴权
- 注册、登录、刷新会话和退出。
- access token 短期有效,refresh token 使用 HttpOnly Cookie。
- 前端统一处理 `401` 刷新与请求重放。
- 管理员与普通用户共享登录体系,通过 `is_admin` 区分权限。
### 模板与画布
- 首页展示产品能力,不直接堆叠模板列表。
- `/canvas` 同页切换“精选模板”和“我的画布”。
- 精选模板支持全部、VIP、免费筛选。
- 模板应用前显示 Popconfirm;同一用户对同一模板只能应用一次。
- 从模板创建或空白创建独立的简历文档。
- 简历卡片支持编辑、预览、删除和投稿。
- 我的画布支持导入 `.resume-canvas.json`,可将文件拖入项目区域,创建一份完整还原节点、连线和终点样式的新简历;当前画布从编辑器顶部导出。
### VueFlow 简历编辑器
- 六类内容节点:个人简介、工作经历、项目经历、教育经历、专业技能、AI 建议;另有一个不可删除的简历生成终点。
- 左侧节点名称作为新节点默认标题;节点顶部不再重复显示类型标题。
- 双击内容节点可编辑标题、内容和成果列表,支持多个同类型节点。
- 支持添加、编辑、拖动、缩放、平移、连接终点和右键删除连线。
- 节点之间不能互相连接,所有入简历内容必须先连接终点。
- 只有连接到终点的节点会进入简历预览和 AI 整稿分析,未连接节点只保留在画布中。
- 只有终点节点提供“AI 整稿分析”和“生成简历”按钮。
- 双击终点可打开已连接节点排序,拖拽交换两个节点坐标并同步简历顺序;断线节点不会出现在排序列表。
- 节点编辑弹窗分别对“内容”和“成果列表”提供 AI 润色,按钮位于对应字段标题右侧;两项可独立预览、应用或取消。
- 只有终点节点可选择主题色和装饰线颜色,所有简历 section 统一使用该全局外观。
- 后台模板编辑与画布终点共用同一套主题色、装饰线令牌;后台保存后直接写入模板终点,`skinKey` 不再隐式覆盖颜色。
- 后台模板编辑弹窗实时渲染真实模板简历;画布修改外观时,已打开的独立预览窗口通过同源频道即时同步,无需刷新。
- 自动保存使用 1.2 秒防抖。
- 编辑器历史记录以左侧浮层覆盖节点组件栏;点击条目只读渲染并暂停自动保存,不会自动新增历史;用户仍可手动点击“保存”将预览内容写为新版本。
- 保存请求携带版本号,冲突时返回 `409`,允许载入服务端版本后继续编辑。
- 编辑器顶部支持导出当前画布,以及追加导入画布文件;导入时自动重映射节点 ID,并通过终点恢复有效的简历引用关系。
- `/preview/:id` 只展示简历 section 与页尾,不包含站点导航、简历页头、标题或操作工具栏,可直接打印。
### AI 能力
- LangChain 负责 Prompt、聊天模型和结构化输出调用。
- LangGraph 负责 `generate -> critique -> repair` 条件修复流程。
- Ollama 是默认主模型,本地服务不可用且配置了 DeepSeek Key 时自动兜底。
- 单节点生成结果经过 Zod 校验后才返回前端。
- 模型不可用时使用本地安全规则生成可编辑建议。
- AI 只生成建议,用户确认后通过统一简历保存接口写回。
- DeepSeek 未配置或同样不可用时,使用本地安全规则继续生成可编辑建议。
- Embedding 不可用时使用确定性本地向量,不阻塞答题检索。
### 技术答题与动态 VIP
- 三个题库分类:前端、后端、AI 工程。
- 每个分类每次固定 10 题,服务端判分。
- 首次答题使用静态基础题,后续从数据库题库确定性随机抽取。
- RAG 返回相关证据、来源、向量相关度和综合相关度。
- 历史均分覆盖所有答题记录:均分达到 80 时启用 VIP,跌破 80 时自动失效。
### 投稿与管理后台
- 用户可将本人简历投稿为精选模板,并可更新投稿内容。
- 管理员可管理用户状态和管理员权限。
- 管理员可审核投稿、调整 VIP 要求、启用或禁用模板。
- 管理员模板管理将“投稿审核”和“全部模板”合并为单视图 Tab,Tab 与当前筛选位于同一工具栏行。
- 管理员可编辑模板标题、选择动态皮肤文件、预览模板、开关模板并删除模板。
- 管理员可查看全部答题记录,并按题库分类筛选。
### 设计系统
- 科技蓝主题由 CSS 变量和 Tailwind 语义 token 驱动。
- 统一 hover、active、focus-visible、disabled、loading 和 reduced-motion 状态。
- 页面与业务组件使用 Tailwind `@apply` 和语义化 class。
- 全局最小正文为 14px;独立数字徽标使用 12px,并扩大容器避免裁切。
- 所有实际路由使用统一 `AppNavigation`,移动端自动折叠。
## 全栈架构
```mermaid
flowchart LR
Browser[浏览器]
Web[apps/resume-web
Vue 3 + Vite]
API[apps/resume-api
NestJS]
DB[(MySQL)]
Browser --> Web
Web -->|/api · Axios| API
API -->|TypeORM| DB
Contracts[packages/contracts]
Reactivity[packages/reactivity]
Shared[packages/shared]
Contracts -. 类型与 Zod .-> Web
Contracts -. 类型与 Zod .-> API
Reactivity -. 响应式状态 .-> Web
Shared -. 纯工具函数 .-> Web
Shared -. 纯工具函数 .-> API
```
### 请求链路
1. 页面通过 `src/api/*` 调用 `/api`。
2. Axios 客户端附加 access token,并在必要时刷新登录态。
3. Nest Controller 接收请求,Guard 校验登录或管理员权限。
4. Service 执行归属校验、事务、乐观锁和业务规则。
5. TypeORM 读写 MySQL,统一返回 `{ code, msg, data }`。
6. 前端使用 `@resume/contracts` 中的类型消费响应。
### 后端模块
```text
AuthModule 登录、注册、刷新、会话
TemplatesModule 精选模板与 VIP 可见性
ResumesModule 简历文档、历史快照、保存、恢复、删除、模板克隆
SubmissionsModule 投稿与个人投稿状态
ResumeAiModule LangChain / LangGraph / 整稿分析
QuizModule 题库、判分、历史均分、动态 VIP
RagModule 知识文档、分块、向量与证据召回
AdminModule 用户、投稿、模板和答题记录管理
```
TypeORM 采用 `synchronize: false`,表结构通过 `terminal/apply-*-schema.mjs` 迁移脚本维护。业务表共 10 张:
```text
demo_user
demo_user_session
demo_resume_template
demo_resume_document
demo_resume_history
demo_resume_submission
demo_quiz_attempt
demo_knowledge_document
demo_knowledge_chunk
demo_quiz_question
```
## 五个子包
| 子包 | 简介 | 关键内容 |
| --- | --- | --- |
| [`@resume/web`](apps/resume-web/readme.md) | Vue 3 前端应用 | 路由、Pinia、VueFlow、页面、模板、答题、后台、设计系统 |
| [`@resume/api`](apps/resume-api/readme.md) | NestJS 后端应用 | 鉴权、简历、投稿、题库、RAG、AI、管理员接口 |
| [`@resume/contracts`](packages/contracts/readme.md) | 跨端契约包 | 领域类型、常量、Zod Schema、默认图数据 |
| [`@resume/reactivity`](packages/reactivity/readme.md) | Vue 状态逻辑包 | `useAsyncTask`、`useDebouncedSave`、`useResumeGraph` |
| [`@resume/shared`](packages/shared/readme.md) | 无框架工具包 | JSON、防抖、日期、VIP 判断、本地向量、余弦相似度 |
### `@resume/web`
负责所有用户可见界面:首页、登录、画布工作台、简历编辑器、独立预览、个人中心、答题中心和管理后台。API 调用、登录态、路由守卫和设计令牌都在应用内部收口。
### `@resume/api`
负责 API、鉴权、权限、事务和持久化。Controller 只处理 HTTP 和当前用户,Service 负责业务规则,Entity 跟随业务模块;`AppModule` 只组合模块。
### `@resume/contracts`
同时服务前后端的唯一协议来源。新增接口时先定义类型与 Zod Schema,再实现 API 和页面,避免两端结构漂移。
### `@resume/reactivity`
封装编辑器内可复用的响应式状态生命周期。它不依赖 Router、Toast 或具体 API,通过回调解耦网络请求。
### `@resume/shared`
提供浏览器和 Node.js 都可使用的纯 TypeScript 工具。不得依赖 Vue、NestJS、DOM 或数据库。
## 前端路由
| 路径 | 功能 | 权限 |
| --- | --- | --- |
| `/` | 产品首页 | 公开 |
| `/login` | 登录/注册 | 公开 |
| `/canvas` | 精选模板 + 我的画布 | 模板公开,个人内容需登录 |
| `/editor/:id` | 简历编辑器 | 登录 |
| `/preview/:id` | 新窗口预览 | 登录 |
| `/user` | 个人中心 | 登录 |
| `/quiz` | 技术答题 | 登录 |
| `/skins/:skin` | 动态简历皮肤预览 | 公开 |
| `/admin/users` | 用户管理 | 管理员 |
| `/admin/templates` | 模板与投稿审核 | 管理员 |
| `/admin/quiz-attempts` | 答题记录 | 管理员 |
## 目录结构
```text
nomorepo-demo/
├─ apps/
│ ├─ resume-api/ # NestJS 后端
│ └─ resume-web/ # Vue 3 前端
├─ packages/
│ ├─ contracts/ # 跨端契约
│ ├─ reactivity/ # Vue 响应式工具
│ └─ shared/ # 通用纯函数
├─ scripts/
│ ├─ resume.js # 公共包 esbuild 构建器
│ └─ resume_demo.sql # 数据库结构与演示数据入口
├─ terminal/ # 检查、种子、启动和冒烟脚本
├─ temp/ # 运行日志、状态和验收产物
├─ pnpm-workspace.yaml
└─ package.json
```
`apps/resume-web/src/components/skins` 抽离皮肤纸张、页头、页尾、section、section 标题和装饰线;`apps/resume-web/src/pages/skins` 通过动态路由匹配四个皮肤页面。
## 本地启动
环境要求:
- Node.js
- pnpm 10
- MySQL 8
- Ollama 主模型;DeepSeek 作为可选兜底,仅在配置 API Key 时启用
安装依赖:
```text
pnpm install
```
准备数据库并参考 `apps/resume-api/.env.example` 配置后端环境变量。启动前后端:
```text
node terminal/start-stack.mjs
```
脚本会探测空闲端口,并将实际 Web/API 地址写入 `temp/stack-state.json`。停止:
```text
node terminal/stop-stack.mjs
```
也可以分别运行:
```text
pnpm --filter @resume/api start:dev
pnpm --filter @resume/web dev
```
## 数据初始化
常用脚本:
```text
node terminal/seed-resume-demo.mjs
node terminal/apply-admin-quiz-schema.mjs
node terminal/seed-rag-knowledge.mjs
```
种子脚本应保持幂等;不要在重复执行时清空用户已有简历或未知数据。
## 构建与校验
公共包:
```text
node terminal/check-contracts.mjs
```
后端:
```text
node terminal/check-api.mjs
```
前端:
```text
node terminal/check-web.mjs
```
数据库与文档:
```text
node terminal/check-db.mjs
node terminal/check-readmes.mjs
php terminal/check-files.php
```
核心 HTTP 冒烟:
```text
node terminal/smoke-resume-demo.mjs
```
全部 Node/PHP 检查脚本都从仓库根目录执行,日志和临时文件写入 `temp/`。
## 核心约束
- 客户端不能提交或覆盖 `user_id`,资源归属以服务端登录态为准。
- 简历保存必须使用乐观锁,不允许静默覆盖版本冲突。
- 画布交换文件必须包含版本标记并通过共享 Zod Schema 校验;导入追加节点,不覆盖当前画布,文件上限为 5MB。
- 模板克隆、投稿和 AI 建议不能直接共享可变图对象。
- 答案和判分逻辑保留在服务端。
- VIP 由历史答题均分实时计算,不是永久标记。
- 新接口先更新公共契约,再修改前后端。
- 主题色和装饰线的可选值统一来自 `RESUME_SKIN_COLOR_TOKENS`;皮肤文件只负责纸张明暗和结构,不覆盖颜色令牌。
- 密码、JWT、Cookie、数据库凭据和模型 API Key 不得进入前端或日志。
- 命令执行和自动化检查优先使用 Node/PHP 脚本,避免 Windows 命令壳吞掉特殊字符。
## 延伸文档
- [全栈工程实践系列](docs/article/README.md)
- [后端应用说明](apps/resume-api/readme.md)
- [前端应用说明](apps/resume-web/readme.md)
- [跨端契约](packages/contracts/readme.md)
- [响应式工具](packages/reactivity/readme.md)
- [通用工具](packages/shared/readme.md)
- [Ollama 与模型兜底说明](docs/ollama.md)