Files
geekgeekrun/plan/webhook_integration.md
rqi14 95c1e54c66 recruiter: add boss auto browse/chat flows, webhook, and candidate tables
- Add recruiter-side automation core and run-core entry
- Extend sqlite-plugin with candidate info + contact logs
- Add UI routes/pages, IPC handlers, progress + log panel
- Document current status and plans under plan/

Made-with: Cursor
2026-03-18 17:37:24 +08:00

205 lines
8.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Webhook / 外部集成
本文档描述招聘端 Webhook 功能的设计、配置结构、数据流与扩展方式,适用于对接 Paperless-ngx、自定义 API 等外部系统。
---
## 1. 功能概述
每轮自动化任务(推荐牛人 + 沟通页)结束后,系统将本轮处理的所有候选人数据汇总成一条 JSON Payload通过 HTTP 请求发送到用户配置的 URL。
支持:
- 开关控制(关闭后任务结束时跳过发送)
- 「保存并测试发送」(用 mock 数据验证接口通不通)
- 「手动触发」(用 mock 数据模拟一次 manual 发送)
- 自定义 Headers用于 Token 认证等)
- Payload 内容裁剪(可关闭某些字段)
- 简历以本地路径或 Base64 附带
---
## 2. 相关文件
| 文件 | 说明 |
|------|------|
| `packages/ui/src/renderer/src/page/MainLayout/WebhookIntegration/index.vue` | 设置页 UI |
| `packages/ui/src/main/features/webhook/index.ts` | 发送逻辑、类型定义、mock 数据生成 |
| `packages/ui/src/main/flow/BOSS_AUTO_BROWSE_AND_CHAT_MAIN/index.ts` | 任务完成后自动触发(`afterChatStarted` 收集 + 轮次结束后发送) |
| `packages/ui/src/main/flow/OPEN_SETTING_WINDOW/ipc/index.ts` | IPC handlersfetch/save/test/trigger |
| `packages/ui/src/renderer/src/router/index.ts` | 路由注册 `WebhookIntegration` |
| `packages/ui/src/renderer/src/page/MainLayout/LeftNavBar/RecruiterPart.vue` | 左侧导航入口 |
配置文件路径:`~/.geekgeekrun/config/webhook.json`(通过 boss-auto-browse 的 `runtime-file-utils.mjs` 读写)
---
## 3. 配置结构webhook.json
```json
{
"enabled": true,
"url": "https://your-paperless.example.com/api/documents/post_document/",
"method": "POST",
"sendMode": "batch",
"contentType": "application/json",
"headers": {
"Authorization": "Token YOUR_TOKEN",
"X-Custom-Header": "value"
},
"payloadOptions": {
"includeBasicInfo": true,
"includeFilterReason": true,
"includeLlmConclusion": true,
"includeResume": "path"
},
"retryTimes": 3,
"retryDelayMs": 1000,
"queueFileOnFailure": false
}
```
**字段说明:**
| 字段 | 类型 | 说明 |
|------|------|------|
| `enabled` | boolean | 是否启用自动触发 |
| `url` | string | 目标 URL须以 http:// 或 https:// 开头) |
| `method` | `"POST"` \| `"PUT"` \| `"PATCH"` | HTTP 方法 |
| `sendMode` | `"batch"` \| `"realtime"` | 轮次结束汇总发送 / 每打招呼后立即发送一条 |
| `contentType` | `"application/json"` \| `"multipart/form-data"` | 请求体格式multipart 时每条候选人一个请求(直传 Paperless 等) |
| `headers` | object | 自定义请求头 key-value 对 |
| `payloadOptions.includeBasicInfo` | boolean | 是否包含候选人基本信息 |
| `payloadOptions.includeFilterReason` | boolean | 是否包含筛选理由/评分 |
| `payloadOptions.includeLlmConclusion` | boolean | 是否包含 LLM 评估结论 |
| `payloadOptions.includeResume` | `"none"` \| `"path"` \| `"base64"` | 简历文件携带方式;**若沟通页开启了 `chatPage.attachmentResume.skipDownload`(见下),`resumeFile` 在 Payload 中将始终为空,与此选项无关** |
| `retryTimes` | number | 失败重试次数0 不重试 |
| `retryDelayMs` | number | 首次重试延迟(毫秒),之后指数退避 |
| `queueFileOnFailure` | boolean | 最终失败时是否写入本地队列文件webhook-failed-queue.jsonl |
---
## 4. Payload 结构
```json
{
"runId": "run-<runRecordId 或时间戳>",
"timestamp": "2026-03-16T10:00:00.000Z",
"summary": {
"total": 10,
"matched": 7,
"skipped": 3
},
"candidates": [
{
"basicInfo": {
"name": "张三",
"education": "本科",
"workExpYears": 3,
"city": "北京",
"salary": "15-25K",
"skills": ["Vue", "React", "TypeScript"]
},
"filterReport": {
"matched": true,
"matchedRules": ["education", "workExp", "skills"],
"score": 85
},
"llmConclusion": "候选人技能与岗位匹配度较高,建议优先沟通。",
"resumeFile": {
"path": "/Users/xxx/.geekgeekrun/storage/resumes/张三.pdf",
"filename": "张三.pdf"
}
}
]
}
```
- `resumeFile.base64` 仅在 `includeResume = "base64"` 时出现
- 若某字段对应的 `payloadOptions` 为 false则该字段在所有候选人对象中省略
- **`chatPage.attachmentResume.skipDownload` 的影响**:若 BOSS 直聘已配置「接收附件简历自动发到邮箱」,可在 `boss-recruiter.json` 中将 `chatPage.attachmentResume.skipDownload` 设为 `true`。此时系统仅发出索取请求、不下载 PDF`resumeFile` 字段在 Payload 中将始终缺失(无论 `payloadOptions.includeResume` 取何值)。若下游系统依赖 `resumeFile`,请保持 `skipDownload: false`(默认值)。
---
## 5. 数据流
```
afterChatStarted hook每次打招呼后
BOSS_AUTO_BROWSE_AND_CHAT_MAIN/index.ts
收集到 sessionCandidates[]candidate.info / matchedRules / score / llmConclusion / resumeFilePath
startBossAutoBrowse + startBossChatPageProcess 均完成
读取 webhook.json
├── enabled=false → 跳过sessionCandidates.length = 0
└── enabled=true, url 非空
features/webhook/index.ts: sendWebhook(config, payload)
├── 按 payloadOptions 过滤字段
├── includeResume="base64" → fs.readFileSync(path).toString('base64')
└── fetch(url, { method, headers, body: JSON.stringify(payload) })
返回 { status, body },记录日志
sessionCandidates.length = 0等待下轮
```
---
## 6. IPC 接口
| Channel | 说明 |
|---------|------|
| `fetch-webhook-config` | 读取 webhook.json不存在时返回 null |
| `save-webhook-config` | payload 为 JSON 字符串,与 existing 合并后写入 |
| `test-webhook` | 用 `buildMockPayload()` 发送到已配置 URL返回 `{ status, body }` |
| `trigger-webhook-manually` | 同上runId 前缀为 `manual-` |
---
## 7. UI 页面说明
**入口:** 「招聘BOSS」左侧导航 → Webhook / 外部集成
**布局:**
1. **基础设置卡片** — 启用开关 / URL 输入 / 请求方法选择
2. **请求头卡片** — 动态 key/value 列表 + 「Authorization Token」「X-API-Key」快速模板按钮
3. **Payload 选项卡片** — 基本信息/筛选报告/LLM 结论 checkbox + 简历携带方式 radio
4. **操作栏** — 仅保存 / 保存并测试发送 / 手动触发
5. **测试结果卡片** — 显示 HTTP 状态码(带颜色 tag+ 格式化响应体
---
## 8. 与 Paperless-ngx 对接示例
Paperless-ngx 的文档上传 API`POST /api/documents/post_document/`
配置示例:
```json
{
"enabled": true,
"url": "http://paperless.local/api/documents/post_document/",
"method": "POST",
"headers": {
"Authorization": "Token <your-paperless-token>"
},
"payloadOptions": {
"includeBasicInfo": true,
"includeFilterReason": true,
"includeLlmConclusion": true,
"includeResume": "base64"
}
}
```
> **注意:** Paperless 上传 API 期望 `multipart/form-data` 格式,而当前实现发送的是 JSON。若需直接上传到 Paperless建议在外部用一个中间服务如 n8n、自定义脚本接收本 webhook JSON再转发给 Paperless。这也是「允许调用自定义 API」的典型用法。
---
## 9. 扩展方向(已实现)
- **逐条实时触发**:配置项 `sendMode: 'realtime'`,在 `afterChatStarted` hook 中每打招呼后立即调用 `sendWebhook` 发送单条候选人;`sendMode: 'batch'` 保持轮次结束汇总发送。
- **支持 multipart/form-data**:配置项 `contentType: 'multipart/form-data'`每条候选人单独一个请求FormData 含 runId、timestamp、summary、candidateJSON、document简历文件支持直传 Paperless 等。
- **重试机制**:配置项 `retryTimes``retryDelayMs`,失败时指数退避重试;`queueFileOnFailure: true` 时最终失败写入 `~/.geekgeekrun/storage/webhook-failed-queue.jsonl`
- **手动触发使用真实数据**`trigger-webhook-manually` 支持第二参数 `useRealData`;为 true 时从 SQLite `CandidateContactLog` + `CandidateInfo` 查最近 50 条联系人组装 payload无数据时回退为 Mock。