# Resume Parse API

一个简单的 Node.js/Express 接口：上传简历文档（支持 `.docx` / `.pdf`），提取文本后返回结构化信息和面试评估内容。

## 运行

1. 安装依赖
   - `npm install`
2. 启动开发模式
   - `npm run dev`
3. 服务启动后默认地址
   - `http://localhost:3000`

## 健康检查

- `GET /health`  
  返回示例：`{ "ok": true, "mysql": { "ok": true, "skipped": false, "database": "Resume" } }`  
  - 未配置 `MYSQL_HOST` 时：`mysql.skipped === true`  
  - 连接失败时：`mysql.ok === false` 且带 `mysql.error`

## MySQL（远程库 Resume）

在 `.env` 中配置（勿提交到 Git，仓库已忽略 `.env`）：

- `MYSQL_HOST`：服务器 IP 或域名（必填）
- `MYSQL_PORT`：默认 `3306`
- `MYSQL_USER`：默认 `root`（按你服务器实际账号修改）
- `MYSQL_PASSWORD`
- `MYSQL_DATABASE`：默认 `Resume`

启动时会在控制台打印 `[mysql] 已连接` 或 `[mysql] 连接失败`。代码里通过 `require("./mysql").getMysqlPool()` 获取连接池。

### 入库说明

- 连接成功后会自动维护表 **`resume_submissions`**。
- **列结构**：与解析结果对齐，**姓名、城市、年龄、期望薪资、教育经历、学历、能力、工作经历、项目经历、技术栈**各占一列（便于查询、统计、以后查重）；**不再写入**整段 `extracted_text`。
- **JSON 列**：`interview_report`（AI 面试题与整块评估）、`pdf_meta`（PDF/OCR 元信息）、`warnings_json`（解析告警列表）。
- 若检测到**旧版表**（曾含 `extracted_text` 列），启动时会 **DROP 后重建**，旧数据会清空，请注意备份。
- 接口响应里仍有 **`db`**：`{ "saved": true, "id": 1 }` 等含义不变。

## 解析接口

### 推荐流程（前端默认）

1. **`POST /api/resume/step1`**（`multipart/form-data`，字段 `file`）  
   - 抽取文本、计算 `file_sha256`、与库比对是否重复（同哈希且已有 `interview_report`）。  
   - **不调 AI**。返回 `parsedPreview`（规则）、`text`、`fileSha256`、`uploadId`（非重复时用于第二步）、`isDuplicate` 等。

2. **`POST /api/resume/consult-ai`**（`application/json`）  
   - Body：`{ "fileSha256": "...", "uploadId": "..." }`（重复时可省略 `uploadId`）。  
   - 若重复：直接返回库内 `interview_report` 与列字段还原的 `parsed`，**不请求模型**。  
   - 若不重复：用 `uploadId` 取内存会话，调用 AI，写入 `resume_submissions`，并清除会话。

### 兼容接口

- `POST /api/parse-word`：一步完成「抽取 + AI + 入库」（脚本或旧客户端可用）。
- 请求类型：`multipart/form-data`
- 文件字段名：`file`

### curl 示例

```bash
curl -X POST "http://localhost:3000/api/parse-word" ^
  -F "file=@D:\test\demo.docx"
```

```bash
curl -X POST "http://localhost:3000/api/parse-word" ^
  -F "file=@D:\test\demo.pdf"
```

### 返回

```json
{
  "filename": "demo.pdf",
  "mimeType": "application/pdf",
  "sourceType": "pdf",
  "text": "解析出的内容（纯文本）",
  "parsed": {
    "姓名": "张三"
  },
  "interviewReport": {
    "interviewQuestions": []
  },
  "warnings": []
}
```

## 说明

- 当前使用 `mammoth` 解析 `.docx`，使用 `pdf-parse` 解析 `.pdf`。
- **扫描版 PDF（几乎无文本层）**：当文本层有效字符数低于阈值时，可启用 OCR：使用 `pdfjs-dist` + `@napi-rs/canvas` 将页面渲染为图片，再用 `tesseract.js` 识别文字。
  - **`PDF_OCR_ENABLED=1` 才会执行 OCR**（默认不开启，避免首次下载模型时请求长时间无响应）。
  - `PDF_OCR_MIN_CHARS`：低于该有效字符数才尝试 OCR，默认 `80`
  - `PDF_OCR_MAX_PAGES`：最多 OCR 的页数，默认 `5`
  - `PDF_OCR_SCALE`：渲染缩放（越大越清晰但更慢），默认 `2`
  - `PDF_OCR_DISABLED`：设为 `1` 可强制关闭 OCR（优先级高于 `PDF_OCR_ENABLED`）
  - `PDF_OCR_LANG`：Tesseract 语言，默认 `chi_sim`。可设为 `eng` 或 `chi_sim+eng` 等
  - `PDF_OCR_INIT_TIMEOUT_MS`：初始化 Tesseract（含首次下载）超时，默认 `120000`
  - `PDF_OCR_PAGE_TIMEOUT_MS`：单页识别超时，默认 `120000`
  - 项目将 `pdfjs-dist` 固定为 `5.4.296`，与 `pdf-parse` 内置版本一致，避免 Worker 与 API 版本不一致。
- 如果你需要支持 `.doc`（旧版 Word），告诉我你的文档来源/格式，我可以再加转换或抽取方案。

