This commit is contained in:
27942
2026-03-06 00:43:40 +08:00
parent 253486b33c
commit 710ba7ec31
13 changed files with 966 additions and 2825 deletions

776
1.html Normal file

File diff suppressed because one or more lines are too long

131
1.py Normal file
View File

@@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
"""
本地脚本:由 DrissionPage (DP) 控制浏览器(本地谷歌 Chrome 或比特浏览器)。
- 使用本地谷歌USE_LOCAL_CHROME = True会启动/连接本机 Chrome。
- 使用比特浏览器USE_LOCAL_CHROME = False需先启动比特浏览器客户端API 端口 54345
"""
from __future__ import annotations
import random
import sys
import time
from pathlib import Path
# 保证从项目根目录运行时可导入 worker 包
_ROOT = Path(__file__).resolve().parent
if str(_ROOT) not in sys.path:
sys.path.insert(0, str(_ROOT))
# ---------- 选择控制对象 ----------
USE_LOCAL_CHROME = True # True=本地谷歌 ChromeFalse=比特浏览器
# 本地谷歌 Chrome 配置(仅当 USE_LOCAL_CHROME=True 时生效)
CHROME_DEBUG_PORT = 9222 # 调试端口;若为 None 则由脚本自动启动 Chrome
CHROME_PATH = None # 例如 r"C:\Program Files\Google\Chrome\Application\chrome.exe"None 用系统默认
# 比特浏览器配置(仅当 USE_LOCAL_CHROME=False 时生效)
BIT_API_BASE = "http://127.0.0.1:54345"
BROWSER_NAME = "测试2"
BROWSER_ID = None
def _connect_local_chrome():
"""连接或启动本地谷歌 Chrome返回 ChromiumPage。"""
from DrissionPage import ChromiumPage, ChromiumOptions
co = ChromiumOptions()
if CHROME_PATH:
co.set_browser_path(CHROME_PATH)
if CHROME_DEBUG_PORT is not None:
# 连接已开启调试端口的 Chrome需先手动启动chrome --remote-debugging-port=9222
co.set_local_port(CHROME_DEBUG_PORT)
print(f"正在连接本机 Chrome调试端口 {CHROME_DEBUG_PORT}...")
page = ChromiumPage(addr_or_opts=co)
else:
# 由 DrissionPage 自动启动 Chrome
print("正在启动本地谷歌 Chrome...")
page = ChromiumPage(addr_or_opts=co)
print("已连接本地 Chrome。")
return page
def _connect_bit_browser():
"""通过比特浏览器 API 打开并连接,返回 ChromiumPage。"""
from worker.bit_browser import BitBrowserAPI
from DrissionPage import ChromiumPage, ChromiumOptions
print("正在连接比特浏览器 API...")
bit_api = BitBrowserAPI(BIT_API_BASE)
print("正在打开比特浏览器...")
cdp_addr, port, browser_id = bit_api.open_browser(
browser_id=BROWSER_ID, name=BROWSER_NAME, remark=None
)
print(f"已打开浏览器 ID={browser_id}, CDP 端口={port}")
co = ChromiumOptions().set_local_port(port=port)
return ChromiumPage(addr_or_opts=co)
def main():
from DrissionPage.errors import NoRectError
if USE_LOCAL_CHROME:
page = _connect_local_chrome()
else:
page = _connect_bit_browser()
page.listen.start('wapi/zpjob/rec/geek/list')
# 示例:打开一个页面(可选)
page.get("https://www.zhipin.com/web/chat/recommend")
res = page.listen.wait()
for i in res.response.body["zpData"]["geekList"]:
print(i)
geekName = i["geekCard"]["geekName"] # 姓名
geekDegree = i["geekCard"]["geekDegree"] # 学历
expectPositionName = i["geekCard"]["expectPositionName"] #期待职位
expectLocationName = i["geekCard"]["expectLocationName"] # 地区
applyStatusDesc = i["geekCard"]["applyStatusDesc"] # 当前状态离职0随时到岗之类的
ageDesc = i["geekCard"]["ageDesc"] # 年龄
lowSalary = i["geekCard"]["lowSalary"] # 最低要求工资
highSalary = i["geekCard"]["highSalary"] # 最高要求工资
"""三个动作1. 找到姓名 2. 滚动到那里 3. 点击打招呼"""
try:
container = page.get_frame("recommendFrame")
except Exception:
container = page
# 1. 找到这个姓名
name_ele = container.ele(f'x://span[contains(text(),"{geekName}")]', timeout=10)
if not name_ele:
name_ele = container.ele(f'x://span[text()="{geekName}"]', timeout=2)
if not name_ele:
raise Exception(f"未找到姓名:{geekName}")
# 2. 滚动到那里
name_ele.run_js("this.scrollIntoView({block: 'center', behavior: 'auto'})")
time.sleep(0.8)
# 3. 点击打招呼(先在该人所在卡片内找)
parent = name_ele.parent()
greet_btn = None
for _ in range(8):
if not parent:
break
greet_btn = parent.ele('x://span[text()="打招呼"]', timeout=0.5) or parent.ele('x://*[contains(text(),"打招呼")]', timeout=0.5)
if greet_btn:
break
parent = parent.parent()
if not greet_btn:
greet_btn = container.ele('x://span[text()="打招呼"]', timeout=2) or container.ele('x://*[contains(text(),"打招呼")]', timeout=1)
if not greet_btn:
raise Exception("未找到「打招呼」按钮")
greet_btn.click(by_js=True)
if __name__ == "__main__":
main()

View File

@@ -1,317 +0,0 @@
# 联系记录接口文档
## 1. 查询联系记录列表
### 接口信息
- **URL**: `/api/contacts`
- **方法**: `GET`
- **认证**: 需要登录Header 携带 Authorization
### 请求参数
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|--------|------|------|------|------|
| page | integer | 否 | 页码,默认 1 | 1 |
| page_size | integer | 否 | 每页数量,默认 10 | 20 |
| search | string | 否 | 搜索关键词(姓名/岗位/联系方式) | 张三 |
| reply_status | string | 否 | 回复状态筛选 | 已回复 |
| wechat_exchanged | boolean | 否 | 是否交换微信 | true |
| start_date | string | 否 | 开始日期YYYY-MM-DD | 2024-01-01 |
| end_date | string | 否 | 结束日期YYYY-MM-DD | 2024-12-31 |
### 请求示例
```bash
# 基础查询
GET /api/contacts?page=1&page_size=20
# 关键词搜索
GET /api/contacts?search=张三
# 按回复状态筛选
GET /api/contacts?reply_status=已回复
# 按时间范围筛选
GET /api/contacts?start_date=2024-01-01&end_date=2024-12-31
# 组合筛选
GET /api/contacts?search=产品经理&reply_status=已回复&wechat_exchanged=true&start_date=2024-01-01&end_date=2024-12-31
```
### 响应示例
```json
{
"code": 200,
"msg": "success",
"data": {
"total": 150,
"page": 1,
"page_size": 20,
"results": [
{
"id": 1,
"name": "张三",
"position": "产品经理",
"contact": "13800138000",
"reply_status": "已回复",
"wechat_exchanged": true,
"account_id": 1,
"worker_id": "worker-001",
"notes": "沟通顺畅,有意向",
"contacted_at": "2024-03-01T10:30:00",
"created_at": "2024-03-01T10:00:00"
},
{
"id": 2,
"name": "李四",
"position": "前端工程师",
"contact": "wechat:lisi123",
"reply_status": "未回复",
"wechat_exchanged": false,
"account_id": 2,
"worker_id": "worker-002",
"notes": "",
"contacted_at": "2024-03-02T14:20:00",
"created_at": "2024-03-02T14:00:00"
}
]
}
}
```
---
## 2. 导出联系记录为 Excel
### 接口信息
- **URL**: `/api/contacts/export`
- **方法**: `GET`
- **认证**: 需要登录Header 携带 Authorization
- **响应类型**: `application/json`(返回下载链接)
### 请求参数
| 参数名 | 类型 | 必填 | 说明 | 示例 |
|--------|------|------|------|------|
| search | string | 否 | 搜索关键词(姓名/岗位/联系方式) | 张三 |
| reply_status | string | 否 | 回复状态筛选 | 已回复 |
| wechat_exchanged | boolean | 否 | 是否交换微信 | true |
| start_date | string | 否 | 开始日期YYYY-MM-DD | 2024-01-01 |
| end_date | string | 否 | 结束日期YYYY-MM-DD | 2024-12-31 |
### 请求示例
```bash
# 导出所有联系记录
GET /api/contacts/export
# 导出指定时间段的记录
GET /api/contacts/export?start_date=2024-01-01&end_date=2024-12-31
# 导出已回复且交换微信的记录
GET /api/contacts/export?reply_status=已回复&wechat_exchanged=true
# 导出组合筛选结果
GET /api/contacts/export?search=产品经理&start_date=2024-01-01&end_date=2024-03-31&reply_status=已回复
```
### 响应示例
```json
{
"code": 200,
"msg": "success",
"data": {
"download_url": "http://127.0.0.1:8000/media/exports/contacts_export_20240304_153045_a1b2c3d4.xlsx",
"filename": "contacts_export_20240304_153045_a1b2c3d4.xlsx",
"total_records": 150,
"generated_at": "2024-03-04 15:30:45"
}
}
```
### 响应字段说明
| 字段名 | 类型 | 说明 |
|--------|------|------|
| download_url | string | 文件下载链接(完整 URL |
| filename | string | 生成的文件名 |
| total_records | integer | 导出的记录总数 |
| generated_at | string | 文件生成时间 |
### Excel 文件格式
**表头**(蓝色背景,白色粗体文字):
| 列名 | 说明 | 示例 |
|------|------|------|
| ID | 记录 ID | 1 |
| 姓名 | 联系人姓名 | 张三 |
| 岗位 | 应聘岗位 | 产品经理 |
| 联系方式 | 电话或微信 | 13800138000 |
| 回复状态 | 回复状态 | 已回复 |
| 是否交换微信 | 是/否 | 是 |
| Worker ID | Worker 标识 | worker-001 |
| 备注 | 备注信息 | 沟通顺畅,有意向 |
| 联系时间 | 联系时间 | 2024-03-01 10:30:00 |
| 创建时间 | 创建时间 | 2024-03-01 10:00:00 |
### 前端调用示例
```javascript
// 使用 axios
const exportContacts = async (params) => {
try {
const response = await axios.get('/api/contacts/export', {
params: params,
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.data.code === 200) {
const { download_url, filename, total_records } = response.data.data;
// 方式1直接打开下载链接
window.open(download_url, '_blank');
// 方式2使用 a 标签下载
const link = document.createElement('a');
link.href = download_url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 提示用户
console.log(`成功导出 ${total_records} 条记录`);
}
} catch (error) {
console.error('导出失败:', error);
}
};
// 调用示例
exportContacts({
start_date: '2024-01-01',
end_date: '2024-12-31',
reply_status: '已回复'
});
```
```javascript
// 使用 fetch
const exportContacts = async (params) => {
const queryString = new URLSearchParams(params).toString();
try {
const response = await fetch(`/api/contacts/export?${queryString}`, {
headers: {
'Authorization': `Bearer ${token}`
}
});
const result = await response.json();
if (result.code === 200) {
const { download_url, filename, total_records } = result.data;
// 直接下载文件
const link = document.createElement('a');
link.href = download_url;
link.download = filename;
link.click();
alert(`成功导出 ${total_records} 条记录`);
}
} catch (error) {
console.error('导出失败:', error);
}
};
```
```vue
<!-- Vue 3 组件示例 -->
<template>
<button @click="handleExport" :loading="exporting">
导出 Excel
</button>
</template>
<script setup>
import { ref } from 'vue';
import axios from 'axios';
const exporting = ref(false);
const handleExport = async () => {
exporting.value = true;
try {
const response = await axios.get('/api/contacts/export', {
params: {
start_date: '2024-01-01',
end_date: '2024-12-31'
}
});
if (response.data.code === 200) {
const { download_url, total_records } = response.data.data;
// 下载文件
window.open(download_url, '_blank');
// 显示成功提示
ElMessage.success(`成功导出 ${total_records} 条记录`);
}
} catch (error) {
ElMessage.error('导出失败,请重试');
} finally {
exporting.value = false;
}
};
</script>
```
---
## 错误码说明
| 错误码 | 说明 |
|--------|------|
| 200 | 成功 |
| 400 | 请求参数错误 |
| 401 | 未登录或 token 无效 |
| 500 | 服务器内部错误 |
## 注意事项
1. **时间筛选**
- `start_date` 从当天 00:00:00 开始
- `end_date` 到当天 23:59:59 结束
- 时间筛选基于 `created_at` 字段(创建时间)
2. **搜索功能**
- `search` 参数支持模糊匹配
- 同时搜索姓名、岗位、联系方式三个字段
3. **导出机制**
- 导出接口返回下载链接,不直接返回文件流
- 文件保存在服务器 `media/exports/` 目录
- 文件名格式:`contacts_export_{时间戳}_{随机ID}.xlsx`
- 导出接口不分页,会导出所有符合条件的记录
- 建议在导出大量数据时添加时间范围限制
4. **文件管理**
- 导出的文件会保存在服务器上
- 建议定期清理过期的导出文件
- 可以通过定时任务删除超过一定时间的文件
5. **认证要求**
- 所有接口都需要在 Header 中携带 `Authorization` token
- token 格式:`Bearer <token>`
6. **下载链接**
- 返回的 `download_url` 是可直接访问的完整下载 URL
- 前端可直接 `window.open(download_url)``a.href = download_url` 下载

View File

@@ -1,693 +0,0 @@
# BOSS 直聘自动化平台 - API 接口文档
**Base URL**: `http://{服务器IP}:9000`
**认证方式**: Cookie`auth_token`),登录成功后自动通过 `Set-Cookie` 设置,后续请求自动携带。
**请求格式**: 支持 `application/json``multipart/form-data`
**响应规范**: 所有接口的 JSON 响应体均包含 **`code`** 字段,与 HTTP 状态码一致。成功时如 `code: 200``code: 201`;错误时如 `code: 401``code: 404`。原返回数组的接口会包装为 `{"code": 状态码, "data": 原数组}`
---
## 一、健康检查
### GET /health
**说明**: 检查服务器是否正常运行,无需登录。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回服务状态与在线 Worker 数 |
| 500 | 服务器内部错误(少见) |
**请求参数**: 无
**响应示例**:
```json
{
"status": "ok",
"workers_online": 2
}
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| status | string | 服务状态,固定 `"ok"` |
| workers_online | int | 当前在线 Worker 数量 |
---
## 二、认证
### POST /api/auth/login
**说明**: 管理员登录,无需认证。登录成功后通过 `Set-Cookie` 返回 `auth_token`
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回 token 并设置 Cookie |
| 401 | 用户名或密码错误 |
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| username | string | 是 | 用户名 |
| password | string | 是 | 密码 |
**请求示例**:
```json
{
"username": "admin",
"password": "boss_dp_admin"
}
```
**成功响应** (200):
```json
{
"token": "a1b2c3d4e5f6..."
}
```
**失败响应** (401):
```json
{
"detail": "用户名或密码错误"
}
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| token | string | 登录 token同时通过 Cookie 设置) |
**响应 Header**:
```
Set-Cookie: auth_token=a1b2c3d4e5f6...; HttpOnly; Max-Age=31536000; SameSite=Lax
```
---
## 三、Worker 管理
> 以下接口均需登录(携带 `auth_token` Cookie
### GET /api/workers
**说明**: 获取所有已注册 Worker 列表。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回 Worker 数组 |
| 401 | 未登录或 token 失效 |
**请求参数**: 无
**响应示例**:
```json
[
{
"worker_id": "worker-1",
"worker_name": "本机",
"browsers": [
{
"id": "abd63190eea84dc49429962f7bb330a4",
"name": "第一个",
"remark": ""
}
],
"online": true,
"current_task_id": null
}
]
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| worker_id | string | Worker 唯一标识 |
| worker_name | string | Worker 名称(电脑名) |
| browsers | array | 该电脑上的比特浏览器环境列表 |
| browsers[].id | string | 浏览器窗口 ID |
| browsers[].name | string | 浏览器窗口名称(环境名) |
| browsers[].remark | string | 备注 |
| online | boolean | 是否在线 |
| current_task_id | string/null | 当前正在执行的任务 ID |
---
### GET /api/workers/{worker_id}
**说明**: 获取指定 Worker 的详细信息。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回单个 Worker 对象 |
| 401 | 未登录或 token 失效 |
| 404 | Worker 不存在 |
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| worker_id | string | 是 | Worker ID |
**成功响应** (200): 同上单个 Worker 对象
**失败响应** (404):
```json
{
"detail": "Worker worker-99 不存在"
}
```
---
## 四、账号管理
> 以下接口均需登录(携带 `auth_token` Cookie
### POST /api/accounts
**说明**: 添加账号(将浏览器环境名称绑定到指定电脑)。若已存在相同绑定则直接返回已有记录。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 201 | 成功创建或返回已有记录 |
| 400 | 请求参数错误(如缺少 browser_name、worker_id |
| 401 | 未登录或 token 失效 |
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| browser_name | string | 是 | 浏览器环境名称 |
| worker_id | string | 是 | 目标电脑的 Worker ID |
**请求示例**:
```json
{
"browser_name": "第一个",
"worker_id": "worker-1"
}
```
**成功响应** (201):
```json
{
"id": 1,
"worker_id": "worker-1",
"browser_id": "name:第一个",
"browser_name": "第一个",
"boss_username": "",
"is_logged_in": false,
"current_task_id": null,
"current_task_status": null,
"checked_at": null,
"created_at": "2026-02-14T16:00:00",
"updated_at": "2026-02-14T16:00:00",
"worker_name": "本机",
"worker_online": true
}
```
---
### GET /api/accounts
**说明**: 查询所有账号列表,包含电脑名称、在线状态、任务执行状态。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回账号数组 |
| 401 | 未登录或 token 失效 |
**查询参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| worker_id | string | 否 | 按 Worker ID 过滤 |
**响应示例**:
```json
[
{
"id": 1,
"worker_id": "worker-1",
"browser_id": "abd63190eea84dc49429962f7bb330a4",
"browser_name": "第一个",
"boss_username": "某用户",
"is_logged_in": true,
"current_task_id": "3f113d90bb32",
"current_task_status": "success",
"checked_at": "2026-02-14T16:05:00",
"created_at": "2026-02-14T16:00:00",
"updated_at": "2026-02-14T16:05:00",
"worker_name": "本机",
"worker_online": true
}
]
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| id | int | 账号记录 ID |
| worker_id | string | 绑定的 Worker ID |
| browser_id | string | 比特浏览器窗口 ID |
| browser_name | string | 浏览器环境名称 |
| boss_username | string | BOSS 直聘用户名(检测后填充) |
| boss_id | string | BOSS 直聘用户 ID检测登录成功时填充 |
| is_logged_in | boolean | 是否已登录 BOSS |
| current_task_id | string/null | 当前关联的任务 ID |
| current_task_status | string/null | 当前任务状态:`pending` / `dispatched` / `running` / `success` / `failed` |
| checked_at | string/null | 最近一次检测时间ISO 格式) |
| created_at | string | 创建时间 |
| updated_at | string | 更新时间 |
| worker_name | string | 电脑名称(运行时补充) |
| worker_online | boolean | 电脑是否在线(运行时补充) |
---
### GET /api/accounts/{id}
**说明**: 查询单个账号详情。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回单个账号对象 |
| 401 | 未登录或 token 失效 |
| 404 | 账号不存在 |
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | int | 是 | 账号记录 ID |
**成功响应** (200): 同上单个账号对象
**失败响应** (404):
```json
{
"detail": "账号不存在"
}
```
---
### DELETE /api/accounts/{id}
**说明**: 删除指定账号。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功删除 |
| 401 | 未登录或 token 失效 |
| 404 | 账号不存在 |
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| id | int | 是 | 账号记录 ID |
**成功响应** (200):
```json
{
"message": "账号已删除"
}
```
**失败响应** (404):
```json
{
"detail": "账号不存在"
}
```
---
## 五、任务管理
> 以下接口均需登录(携带 `auth_token` Cookie
>
> **统一任务入口**:所有任务(检查登录、招聘等)都通过 `POST /api/tasks` 提交,通过 `task_type` 字段区分任务类型。
### POST /api/tasks
**说明**: 提交新任务。通过 `task_type` 指定任务类型,系统自动路由到目标 Worker 执行。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 201 | 成功,任务已创建并派发 |
| 400 | 未指定 id、boss_id、worker_id 或 account_name |
| 401 | 未登录或 token 失效 |
| 409 | 同一账号已有执行中的任务 |
| 404 | 未找到拥有该浏览器环境的在线 Worker |
| 503 | Worker 不在线 / WebSocket 连接不存在 / 派发失败 |
**请求参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| task_type | string | 是 | 任务类型:`check_login`(检查登录)、`boss_recruit`(招聘) |
| id | int | 否 | 账号 ID直接指定推荐 |
| boss_id | int | 否 | 即 /api/accounts 返回的 id账号主键与 id 等价 |
| worker_id | string | 否 | 目标 Worker ID |
| account_name | string | 否 | 浏览器环境名称(系统自动查找对应 Worker |
| params | object | 否 | 任务附加参数,默认 `{}` |
**路由规则**: `id``account_id``boss_id` 等价(均为账号主键);否则 `worker_id``account_name`
**请求示例 — 检查登录(推荐用 id**:
```json
{
"task_type": "check_login",
"id": 2
}
```
**请求示例 — 招聘任务**:
```json
{
"task_type": "boss_recruit",
"worker_id": "worker-1",
"params": {
"keyword": "Python开发",
"count": 10
}
}
```
**成功响应** (201):
```json
{
"task_id": "3f113d90bb32",
"task_type": "check_login",
"status": "dispatched",
"worker_id": "worker-1",
"account_name": "第一个",
"params": {},
"progress": null,
"result": null,
"error": null,
"created_at": 1739520000.123,
"updated_at": 1739520000.456
}
```
**失败响应**:
| 状态码 | 说明 |
|--------|------|
| 400 | 未指定 id、boss_id、worker_id 或 account_name |
| 401 | 未登录或 token 失效 |
| 409 | 同一账号已有执行中的任务 |
| 404 | 未找到拥有该浏览器环境的在线 Worker |
| 503 | Worker 不在线 / WebSocket 连接不存在 / 派发失败 |
---
### GET /api/tasks
**说明**: 查询任务列表(内存中的任务)。
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回任务数组 |
| 401 | 未登录或 token 失效 |
**查询参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| worker_id | string | 否 | 按 Worker ID 过滤 |
| status | string | 否 | 按状态过滤:`pending` / `dispatched` / `running` / `success` / `failed` / `cancelled` |
| limit | int | 否 | 返回数量限制,默认 50 |
**响应示例**:
```json
[
{
"task_id": "3f113d90bb32",
"task_type": "check_login",
"status": "success",
"worker_id": "worker-1",
"account_name": "第一个",
"params": {},
"progress": "检测完成: 第一个 → 未登录 ()",
"result": {
"browser_id": "abd63190eea84dc49429962f7bb330a4",
"browser_name": "第一个",
"boss_username": "",
"is_logged_in": false
},
"error": null,
"created_at": 1739520000.123,
"updated_at": 1739520030.789
}
]
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| task_id | string | 任务唯一 ID12 位十六进制) |
| task_type | string | 任务类型 |
| status | string | 任务状态:`pending``dispatched``running``success`/`failed` |
| worker_id | string/null | 执行任务的 Worker ID |
| account_name | string/null | 关联的浏览器环境名称 |
| params | object | 任务参数 |
| progress | string/null | 执行进度描述 |
| result | object/null | 任务结果(成功时返回) |
| error | string/null | 错误信息(失败时返回) |
| created_at | float | 创建时间Unix 时间戳) |
| updated_at | float | 最后更新时间Unix 时间戳) |
---
### GET /api/tasks/{account_id}
**说明**: 按账号 ID 查询任务列表(不支持按 task_id 查询)。
- 不传 `page/page_size`:返回任务数组(兼容旧版前端)
-`page``page_size`:返回分页对象
**状态码**:
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回任务数组 |
| 401 | 未登录或 token 失效 |
| 404 | 账号不存在 |
**路径参数**:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| account_id | int | 是 | 账号 ID`/api/accounts` 返回的 `id` |
| status | string | 否 | 按状态过滤:`pending` / `dispatched` / `running` / `success` / `failed` / `cancelled` |
| limit | int | 否 | 兼容参数,不分页模式下表示最多返回条数;分页模式下可作为 `page_size` 默认值 |
| page | int | 否 | 分页页码(从 1 开始) |
| page_size | int | 否 | 每页条数,默认 20最大 200 |
**成功响应** (200):
- 兼容模式:同上任务对象数组
- 分页模式:
```json
{
"total": 123,
"page": 1,
"page_size": 20,
"results": [
{
"task_id": "3f113d90bb32",
"task_type": "check_login",
"status": "success",
"worker_id": "worker-1",
"account_name": "第一个",
"params": {},
"progress": null,
"result": {},
"error": null,
"created_at": "2026-03-01T20:00:00",
"updated_at": "2026-03-01T20:00:00"
}
]
}
```
**失败响应** (404):
```json
{
"detail": "账号 xxx 不存在"
}
```
---
## 六、任务类型说明
| task_type | 说明 | 必要参数 | 返回 result |
|-----------|------|---------|-------------|
| `check_login` | 检测浏览器环境中 BOSS 账号是否已登录 | `account_name`(环境名) | `{ "browser_id", "browser_name", "boss_username", "is_logged_in" }` |
| `boss_recruit` | 执行 BOSS 直聘自动招聘流程 | `worker_id` + `params` | `{ "job_title", "total_processed", "wechat_collected", "phone_collected", "details", "error_count" }` |
`boss_recruit``details` 项示例:
```json
{
"name": "候选人A",
"job": "Python开发",
"wechat": "wxid_xxx",
"phone": "13800138000"
}
```
说明:
- 若自动化流程发生报错,任务状态会标记为 `failed`(不再显示为 `success`)。
- 失败任务也会保留 `result`,便于在任务列表查看已采集到的微信号/手机号数量。
---
## 七、任务状态流转
```
pending → dispatched → running → success
→ failed
→ cancelled
```
| 状态 | 说明 |
|------|------|
| pending | 任务已创建,等待派发 |
| dispatched | 已通过 WebSocket 发送给 Worker |
| running | Worker 正在执行中 |
| success | 执行成功 |
| failed | 执行失败 |
| cancelled | 已取消 |
---
## 八、联系记录(数据展示)
> 以下接口均需登录(携带 `auth_token` Cookie
### GET /api/contacts/query按电话/微信号查询)
**说明**:根据已获取的电话号码或微信号查询联系记录,用于数据展示。对 `contact` 字段做模糊匹配。
**状态码**
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回分页结果 |
| 400 | 未提供 contact 参数 |
| 401 | 未登录或 token 失效 |
**查询参数**
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| contact | string | 是 | 电话号码或微信号(支持部分匹配) |
| page | int | 否 | 页码,默认 1 |
| page_size | int | 否 | 每页条数,默认 20 |
**请求示例**
```
GET /api/contacts/query?contact=13800138000
GET /api/contacts/query?contact=wxid_abc&page=1&page_size=10
```
**成功响应** (200)
```json
{
"total": 2,
"page": 1,
"page_size": 20,
"contact_keyword": "13800138000",
"results": [
{
"id": 1,
"name": "张三",
"position": "Python开发",
"contact": "13800138000",
"reply_status": "已回复",
"wechat_exchanged": true,
"account_id": 1,
"worker_id": "worker-1",
"notes": "",
"contacted_at": "2026-02-24T10:00:00",
"created_at": "2026-02-24T10:00:00"
}
]
}
```
| 返回字段 | 类型 | 说明 |
|---------|------|------|
| total | int | 匹配总数 |
| page | int | 当前页 |
| page_size | int | 每页条数 |
| contact_keyword | string | 本次查询使用的关键词 |
| results | array | 联系记录列表(字段同联系记录模型) |
**失败响应** (400):未提供 contact 参数时
```json
{
"detail": "请提供 contact 参数(电话号码或微信号)"
}
```
---
### GET /api/contacts
**说明**:分页查询联系记录,支持按姓名、岗位、联系方式关键词搜索,以及按回复状态、是否交换微信筛选。
**状态码**
| 状态码 | 说明 |
|--------|------|
| 200 | 成功,返回 total、page、page_size、results |
| 401 | 未登录或 token 失效 |
**查询参数**`search`(关键词)、`reply_status``wechat_exchanged``page``page_size`。返回格式同上(含 `total``page``page_size``results`)。
---
## 九、通用错误格式
所有接口的错误响应统一为:
```json
{
"detail": "错误描述信息"
}
```
| 状态码 | 说明 |
|--------|------|
| 400 | 请求参数错误 |
| 401 | 未登录或 token 失效 |
| 403 | 无权限 |
| 404 | 资源不存在 |
| 503 | Worker 不在线或服务不可用 |

View File

@@ -1,166 +0,0 @@
# BOSS招聘自动化优化说明
## 优化内容
### 1. 添加筛选功能
#### 活跃度筛选
- 支持解析"03月03日"、"昨天"、"今天"、"刚刚"等时间格式
- 筛选条件:
- 今天活跃
- 3天内活跃
- 本周活跃
- 本月活跃
- 不限
#### 年龄筛选
- 从候选人简历中获取年龄信息
- 根据配置的最小年龄和最大年龄进行筛选
#### 学历筛选
- 支持学历等级:初中、高中、中专、大专、本科、硕士、博士
- 候选人学历需要达到或高于要求学历
#### 期望职位筛选
- 根据候选人的期望职位jobName字段进行筛选
- 支持多个职位关键词匹配
### 2. 联系人记录管理
#### 自动保存联系人
- 从聊天中获取到的联系方式(微信号/手机号)自动保存到数据库
- 保存到 `ContactRecord` 表中
- 包含信息:
- 姓名
- 岗位
- 联系方式(微信或手机)
- 回复状态
- 是否交换微信
- 联系时间
- 备注
#### 去重处理
- 检查是否已存在相同姓名和联系方式的记录
- 如果存在则更新,不存在则创建新记录
### 3. 复聊管理
#### 消息过滤
- **过滤自己发送的消息**:只保留对方发送的消息进行分析
- 解决了之前"发送带微信号的消息后,识别到自己消息"的问题
- 通过 `fromId` 字段判断消息来源fromId=0 表示对方发送)
#### 等待回复
- 发送询问微信号后等待最多30秒
- 每3秒检查一次是否有新回复
- 自动识别对方回复中的联系方式
#### 跟进话术
- 如果对方没有回复,可以发送跟进话术
- 支持按岗位配置不同的跟进话术
-`ChatScript` 表中读取话术script_type="followup"
- 如果没有特定岗位话术,使用通用话术
## 使用方法
### 1. 配置筛选条件
在数据库 `filter_config` 表中配置筛选条件:
```python
FilterConfig.objects.create(
name="Python开发筛选",
age_min=22,
age_max=35,
education="本科",
activity="3天内活跃",
positions=["Python开发", "后端开发", "全栈开发"],
is_active=True
)
```
### 2. 配置复聊话术
在数据库 `chat_script` 表中配置话术:
```python
ChatScript.objects.create(
position="Python开发",
script_type="followup",
content="您好,看到您的简历很符合我们的要求,期待与您进一步沟通。",
is_active=True
)
# 通用话术
ChatScript.objects.create(
position="通用",
script_type="followup",
content="您好,期待与您进一步沟通。",
is_active=True
)
```
### 3. 运行招聘任务
任务会自动:
1. 获取候选人列表
2. 应用筛选条件
3. 逐个打开会话
4. 过滤自己的消息,只分析对方消息
5. 如果没有联系方式,发送询问
6. 等待对方回复并识别联系方式
7. 自动保存联系人记录到数据库
8. 如果需要,发送跟进话术
## 数据库表说明
### FilterConfig筛选配置表
- `name`: 配置名称
- `age_min`: 最小年龄
- `age_max`: 最大年龄
- `education`: 学历要求
- `activity`: 活跃度要求
- `positions`: 期望岗位列表JSON
- `is_active`: 是否启用
### ChatScript话术表
- `position`: 岗位类型
- `script_type`: 话术类型first/followup/wechat/closing
- `content`: 话术内容
- `keywords`: 触发关键词
- `is_active`: 是否启用
### ContactRecord联系人记录表
- `name`: 姓名
- `position`: 岗位
- `contact`: 联系方式
- `reply_status`: 回复状态
- `wechat_exchanged`: 是否交换微信
- `notes`: 备注
- `contacted_at`: 联系时间
## 注意事项
1. **活跃度时间解析**
- 支持"03月03日"格式,自动判断年份
- 如果月份大于当前月份,认为是去年的日期
2. **消息过滤**
- 通过 `fromId` 字段区分消息来源
- `fromId=0` 表示对方发送的消息
- 其他值表示自己发送的消息
3. **复聊等待时间**
- 默认等待30秒
- 可以根据实际情况调整 `max_wait` 参数
4. **筛选配置**
- 只有 `is_active=True` 的配置才会生效
- 如果没有启用的配置,跳过筛选,处理所有候选人
## 优化效果
1. **提高效率**:通过筛选减少无效沟通
2. **自动记录**:联系方式自动保存,无需手动整理
3. **智能识别**:过滤自己的消息,只识别对方的联系方式
4. **持续跟进**:支持复聊管理,提高回复率

View File

@@ -1,488 +0,0 @@
# BOSS招聘自动化 - 完整优化说明
## 优化概述
本次优化解决了以下核心问题:
1.**消息过滤问题**:过滤掉自己发送的包含"微信号"等关键词的消息
2.**筛选功能**:支持活跃度、年龄、学历、期望职位筛选
3.**联系人记录**:自动保存聊天中获取的联系方式
4.**复聊管理**:支持多轮复聊、自定义话术、间隔时间控制
---
## 一、消息过滤优化
### 问题描述
之前的代码会识别到自己发送的包含"微信号"三个字的消息,导致误判。
### 解决方案
#### 1. 通过 fromId 区分消息来源
```python
def _filter_my_messages(self, messages: list) -> list:
"""过滤掉自己发送的消息,只保留对方的消息。"""
filtered = []
for msg in messages:
# fromId = 0 表示对方发送的消息
from_id = msg.get("fromId", 0)
if from_id == 0:
filtered.append(msg)
return filtered
```
#### 2. 在等待回复时过滤发送的话术
```python
# 检查最后几条消息
for text in panel_texts[-5:]:
# 过滤掉我们发送的消息
if sent_message in text:
continue
# 过滤掉包含"微信号"但没有真实微信号的消息
if "微信号" in text and not self._extract_wechat(text):
continue
```
---
## 二、筛选功能
### 支持的筛选条件
#### 1. 活跃度筛选
支持的时间格式:
- `"03月03日"` - 自动判断年份
- `"昨天"` - 昨天
- `"今天"` - 今天
- `"刚刚"` - 今天
筛选选项:
- `"今天活跃"` - 今天上线
- `"3天内活跃"` - 3天内上线
- `"本周活跃"` - 7天内上线
- `"本月活跃"` - 30天内上线
- `"不限"` - 不筛选
#### 2. 年龄筛选
从候选人简历的 `resume.age` 字段获取,根据 `age_min``age_max` 筛选。
#### 3. 学历筛选
学历等级:`初中 < 高中 < 中专 < 大专 < 本科 < 硕士 < 博士`
候选人学历需要达到或高于要求学历。
#### 4. 期望职位筛选
从候选人的 `jobName` 字段匹配配置的职位列表。
### 配置示例
```python
FilterConfig.objects.create(
name="Python开发筛选",
age_min=22,
age_max=35,
education="本科",
activity="3天内活跃",
positions=["Python开发", "后端开发", "全栈开发"],
is_active=True
)
```
---
## 三、联系人记录管理
### 自动保存功能
当从聊天中提取到联系方式(微信号或手机号)时,自动保存到 `ContactRecord` 表。
### 保存的信息
- 姓名
- 岗位
- 联系方式(微信或手机)
- 回复状态
- 是否交换微信
- 联系时间
- 备注
### 去重逻辑
- 检查是否已存在相同姓名和联系方式的记录
- 如果存在则更新,不存在则创建
---
## 四、复聊管理系统
### 核心特性
#### 1. 多轮复聊
- 支持配置第1天、第2天、第3天...的不同话术
- 支持配置"往后一直"使用的话术(`day_number=0`
#### 2. 间隔时间控制
- 每条话术都有独立的 `interval_hours` 配置
- 系统自动检查距离上次发送的时间
- 只有超过间隔时间才会发送下一条
#### 3. 自定义话术
- 通过API接口添加、修改、删除话术
- 支持按岗位配置不同的复聊策略
- 支持通用配置作为后备
#### 4. 回复追踪
- 记录每次发送的话术
- 记录是否得到回复
- 记录回复内容和时间
### 复聊流程
```
第1天发送询问微信号
等待24小时
第2天如果没有回复发送跟进话术
等待24小时
第3天如果还没有回复发送第三条话术
等待72小时
往后每隔72小时发送一次"往后一直"的话术
```
### 配置示例
```json
{
"config": {
"name": "Python开发复聊配置",
"position": "Python开发",
"is_active": true
},
"scripts": [
{
"day_number": 1,
"content": "后续沟通会更及时,您方便留一下您的微信号吗?我这边加您。",
"interval_hours": 24
},
{
"day_number": 2,
"content": "您好,不知道您是否方便留个联系方式?",
"interval_hours": 24
},
{
"day_number": 0,
"content": "您好,如果您感兴趣可以随时联系我。",
"interval_hours": 72
}
]
}
```
---
## 五、数据库表结构
### 新增表
#### 1. FollowUpConfig复聊配置表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INT | 主键 |
| name | VARCHAR(128) | 配置名称 |
| position | VARCHAR(64) | 岗位类型 |
| is_active | BOOLEAN | 是否启用 |
| created_at | DATETIME | 创建时间 |
| updated_at | DATETIME | 更新时间 |
#### 2. FollowUpScript复聊话术表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INT | 主键 |
| config_id | INT | 关联的配置ID |
| day_number | INT | 第几天0=往后一直) |
| content | TEXT | 话术内容 |
| interval_hours | INT | 间隔小时数 |
| order | INT | 排序 |
| is_active | BOOLEAN | 是否启用 |
| created_at | DATETIME | 创建时间 |
#### 3. FollowUpRecord复聊记录表
| 字段 | 类型 | 说明 |
|------|------|------|
| id | INT | 主键 |
| contact_id | INT | 联系人ID |
| config_id | INT | 配置ID |
| script_id | INT | 话术ID |
| day_number | INT | 第几天 |
| content | TEXT | 发送的内容 |
| sent_at | DATETIME | 发送时间 |
| got_reply | BOOLEAN | 是否得到回复 |
| reply_content | TEXT | 回复内容 |
| replied_at | DATETIME | 回复时间 |
---
## 六、API接口
### 复聊配置
- `GET /api/followup-configs` - 获取配置列表
- `POST /api/followup-configs` - 创建配置
- `GET /api/followup-configs/{id}` - 获取配置详情
- `PUT /api/followup-configs/{id}` - 更新配置
- `DELETE /api/followup-configs/{id}` - 删除配置
### 复聊话术
- `GET /api/followup-scripts` - 获取话术列表
- `POST /api/followup-scripts` - 创建话术
- `GET /api/followup-scripts/{id}` - 获取话术详情
- `PUT /api/followup-scripts/{id}` - 更新话术
- `DELETE /api/followup-scripts/{id}` - 删除话术
### 复聊记录
- `GET /api/followup-records` - 获取记录列表
- `POST /api/followup-records/send` - 手动发送消息
---
## 七、使用步骤
### 1. 运行数据库迁移
```bash
python server/manage.py migrate
```
### 2. 初始化配置(可选)
```bash
python scripts/init_followup_config.py
```
### 3. 通过API配置复聊策略
#### 创建配置
```bash
POST /api/followup-configs
{
"name": "Python开发复聊",
"position": "Python开发",
"is_active": true
}
```
#### 添加话术
```bash
POST /api/followup-scripts
{
"config_id": 1,
"day_number": 1,
"content": "您的自定义话术",
"interval_hours": 24,
"order": 1,
"is_active": true
}
```
### 4. 运行招聘任务
系统会自动:
- 应用筛选条件
- 过滤自己的消息
- 保存联系人记录
- 按配置进行复聊
---
## 八、关键代码说明
### 1. 消息过滤
```python
# 过滤掉自己发送的消息
filtered_messages = self._filter_my_messages(messages)
has_contact_keyword = self._has_contact_keyword(filtered_messages)
```
### 2. 筛选应用
```python
# 应用筛选条件
friend_list = self._apply_filters(friend_list)
```
### 3. 保存联系人
```python
# 保存并获取contact_id
contact_id = self._save_contact_record(name, job_name, contacts, action_state)
```
### 4. 复聊管理
```python
# 进行复聊管理
reply_result = self._handle_follow_up_chat(tab, name, job_name, contact_id)
```
---
## 九、文件清单
### 代码文件
- `worker/tasks/boss_recruit.py` - 招聘任务处理器(已优化)
- `server/models.py` - 数据模型新增3个表
- `server/serializers.py` - 序列化器新增3个
- `server/api/followup.py` - 复聊配置API新增
- `server/urls.py` - URL路由已更新
- `server/migrations/0004_add_followup_config.py` - 数据库迁移(新增)
### 脚本文件
- `scripts/init_followup_config.py` - 初始化复聊配置
- `scripts/test_recruit_features.py` - 功能测试
### 文档文件
- `BOSS招聘优化说明.md` - 详细功能说明
- `复聊配置API使用指南.md` - API使用指南
- `快速参考指南.md` - 快速参考
- `代码变更清单.md` - 变更记录
- `优化完成总结.md` - 完成总结
- `BOSS招聘自动化完整优化说明.md` - 本文件
---
## 十、测试验证
### 语法检查
```bash
python -m py_compile worker/tasks/boss_recruit.py
```
✅ 通过
### 功能测试
```bash
python scripts/test_recruit_features.py
```
✅ 全部通过
---
## 十一、常见问题
### Q1: 为什么会识别到自己发送的消息?
**A**: 已修复。现在通过 `fromId` 字段区分消息来源,只识别 `fromId=0` 的消息(对方发送的)。
### Q2: 如何配置复聊话术?
**A**: 通过 `/api/followup-scripts` 接口创建话术,设置 `day_number``interval_hours`
### Q3: 如何设置"往后一直"的话术?
**A**: 创建话术时设置 `day_number=0`,系统会在没有特定天数话术时使用它。
### Q4: 复聊间隔时间如何控制?
**A**: 每条话术都有 `interval_hours` 字段,系统会自动检查距离上次发送的时间。
### Q5: 如何手动发送复聊消息?
**A**: 使用 `POST /api/followup-records/send` 接口,传入 `contact_id``content`
### Q6: 联系人记录在哪里查看?
**A**: 通过 `/api/contacts` 接口查询,或在数据库的 `contact_record` 表中查看。
### Q7: 如何为不同岗位配置不同的复聊策略?
**A**: 创建多个 `FollowUpConfig`,设置不同的 `position` 字段。系统会优先匹配岗位配置。
---
## 十二、优化效果
### 提高效率
- 通过筛选减少无效沟通
- 自动化复聊,节省人工时间
### 提高质量
- 消息过滤避免误识别
- 多轮复聊提高回复率
### 数据管理
- 自动保存联系人记录
- 完整的复聊记录追踪
---
## 十三、后续建议
1. **优化筛选条件**:根据实际效果调整筛选参数
2. **优化话术内容**:根据回复率调整话术
3. **添加数据统计**:统计筛选通过率、回复率等
4. **添加黑名单**:避免重复联系
5. **智能话术选择**:根据候选人回复内容智能选择话术
---
## 十四、技术细节
### 时间解析逻辑
```python
if "昨天" in last_time:
last_active = now - timedelta(days=1)
elif "今天" in last_time or "刚刚" in last_time:
last_active = now
elif "月" in last_time and "日" in last_time:
match = re.search(r"(\d+)月(\d+)日", last_time)
if match:
month = int(match.group(1))
day = int(match.group(2))
year = now.year
# 如果月份大于当前月份,说明是去年的
if month > now.month:
year -= 1
last_active = datetime(year, month, day)
```
### 学历比较逻辑
```python
edu_levels = ["初中", "高中", "中专", "大专", "本科", "硕士", "博士"]
candidate_level = next((i for i, edu in enumerate(edu_levels) if edu in candidate_edu), -1)
required_level = next((i for i, edu in enumerate(edu_levels) if edu in required_edu), -1)
return candidate_level >= required_level
```
### 复聊触发逻辑
```python
# 1. 获取该联系人的最后一次复聊记录
last_record = FollowUpRecord.objects.filter(contact_id=contact_id).order_by('-sent_at').first()
# 2. 确定当前是第几天
if not last_record:
day_number = 1 # 第一次复聊
else:
hours_since_last = (now - last_record.sent_at).total_seconds() / 3600
if hours_since_last < last_script.interval_hours:
return # 间隔时间不足,跳过
day_number = last_record.day_number + 1
# 3. 获取该天的话术
script = FollowUpScript.objects.filter(config_id=config.id, day_number=day_number).first()
if not script:
# 使用"往后一直"的话术
script = FollowUpScript.objects.filter(config_id=config.id, day_number=0).first()
```
---
## 十五、完成时间
2026年3月5日
---
## 附录:快速命令
```bash
# 运行数据库迁移
python server/manage.py migrate
# 初始化复聊配置需要Django环境
python scripts/init_followup_config.py
# 测试功能
python scripts/test_recruit_features.py
# 检查语法
python -m py_compile worker/tasks/boss_recruit.py
```

View File

@@ -1,274 +0,0 @@
# BOSS招聘自动化 - 最终使用说明
## 优化完成 ✅
### 核心问题解决
#### 1. ✅ 消息过滤问题
**问题**:识别到自己发送的包含"微信号"三个字的消息
**解决**
- 通过 `fromId` 字段区分消息来源(`fromId=0` 是对方,其他是自己)
- 在等待回复时过滤掉包含发送话术的消息
- 过滤掉包含"微信号"关键词但没有真实微信号的消息
#### 2. ✅ 筛选功能
**新增**
- 活跃度筛选(支持"03月03日"、"昨天"等格式)
- 年龄筛选(从 `resume.age` 获取)
- 学历筛选(支持学历等级比较)
- 期望职位筛选(从 `jobName` 匹配)
#### 3. ✅ 联系人记录
**新增**
- 自动保存到 `ContactRecord`
- 支持去重和更新
- 记录完整信息(姓名、岗位、联系方式、回复状态等)
#### 4. ✅ 复聊管理
**新增**
- 支持多轮复聊第1天、第2天、往后一直
- 支持自定义话术通过API配置
- 支持间隔时间控制(每条话术独立配置)
- 支持按岗位配置不同策略
---
## 快速开始
### 1. 运行数据库迁移
```bash
python server/manage.py migrate
```
### 2. 配置复聊策略通过API
#### 创建配置
```bash
POST /api/followup-configs
{
"name": "Python开发复聊",
"position": "Python开发",
"is_active": true
}
```
#### 添加第1天话术
```bash
POST /api/followup-scripts
{
"config_id": 1,
"day_number": 1,
"content": "后续沟通会更及时,您方便留一下您的微信号吗?我这边加您。",
"interval_hours": 24,
"order": 1,
"is_active": true
}
```
#### 添加第2天话术
```bash
POST /api/followup-scripts
{
"config_id": 1,
"day_number": 2,
"content": "您好,不知道您是否方便留个联系方式?",
"interval_hours": 24,
"order": 1,
"is_active": true
}
```
#### 添加"往后一直"话术
```bash
POST /api/followup-scripts
{
"config_id": 1,
"day_number": 0,
"content": "您好,如果您感兴趣可以随时联系我。",
"interval_hours": 72,
"order": 1,
"is_active": true
}
```
### 3. 配置筛选条件通过API
```bash
POST /api/filters
{
"name": "Python开发筛选",
"age_min": 22,
"age_max": 35,
"education": "本科",
"activity": "3天内活跃",
"positions": ["Python开发", "后端开发", "全栈开发"],
"is_active": true
}
```
### 4. 运行招聘任务
通过API或管理界面启动招聘任务系统会自动
1. 应用筛选条件
2. 过滤自己的消息
3. 保存联系人记录
4. 按配置进行复聊
---
## API接口总览
### 复聊配置
- `GET /api/followup-configs` - 获取配置列表
- `POST /api/followup-configs` - 创建配置
- `PUT /api/followup-configs/{id}` - 更新配置
- `DELETE /api/followup-configs/{id}` - 删除配置
### 复聊话术
- `GET /api/followup-scripts` - 获取话术列表
- `POST /api/followup-scripts` - 创建话术
- `PUT /api/followup-scripts/{id}` - 更新话术
- `DELETE /api/followup-scripts/{id}` - 删除话术
### 复聊记录
- `GET /api/followup-records` - 获取记录列表
- `POST /api/followup-records/send` - 手动发送消息
---
## 复聊配置说明
### day_number 字段
- `1` = 第一天发送
- `2` = 第二天发送
- `3` = 第三天发送
- `0` = 往后一直使用这个话术
### interval_hours 字段
距离上次发送的间隔小时数:
- `24` = 24小时后发送
- `48` = 48小时后发送
- `72` = 72小时后发送
### 复聊逻辑
```
第1天0小时发送第1天话术
↓ 等待24小时
第2天24小时如果没有回复发送第2天话术
↓ 等待24小时
第3天48小时如果还没有回复发送第3天话术
↓ 等待72小时
往后120小时+每隔72小时发送"往后一直"的话术
```
---
## 消息过滤逻辑
### 过滤规则
1. **过滤自己发送的消息**:只保留 `fromId=0` 的消息
2. **过滤发送的话术**:在等待回复时,过滤掉包含发送话术内容的消息
3. **过滤假关键词**:过滤掉包含"微信号"但没有真实微信号的消息
### 示例
```python
# 原始消息
messages = [
{"fromId": 0, "body": {"text": "我的微信是 wx123456"}}, # 对方 ✓
{"fromId": 123, "body": {"text": "您方便留微信号吗?"}}, # 自己 ✗
{"fromId": 0, "body": {"text": "好的test_wx_001"}}, # 对方 ✓
]
# 过滤后只保留对方的消息
filtered = [
{"fromId": 0, "body": {"text": "我的微信是 wx123456"}},
{"fromId": 0, "body": {"text": "好的test_wx_001"}},
]
```
---
## 数据库表
### FollowUpConfig复聊配置
```sql
id, name, position, is_active, created_at, updated_at
```
### FollowUpScript复聊话术
```sql
id, config_id, day_number, content, interval_hours, order, is_active, created_at
```
### FollowUpRecord复聊记录
```sql
id, contact_id, config_id, script_id, day_number, content,
sent_at, got_reply, reply_content, replied_at
```
---
## 文件清单
### 修改的文件
- `worker/tasks/boss_recruit.py` - 招聘任务处理器
- `server/models.py` - 数据模型
- `server/serializers.py` - 序列化器
- `server/urls.py` - URL路由
### 新增的文件
- `server/api/followup.py` - 复聊配置API
- `server/migrations/0004_add_followup_config.py` - 数据库迁移
- `scripts/init_followup_config.py` - 初始化脚本
- `scripts/test_recruit_features.py` - 测试脚本
### 文档文件
- `BOSS招聘优化说明.md`
- `复聊配置API使用指南.md`
- `BOSS招聘自动化完整优化说明.md`
- `快速参考指南.md`
- `代码变更清单.md`
- `优化完成总结.md`
- `BOSS招聘自动化最终使用说明.md`(本文件)
---
## 测试验证
### 语法检查
```bash
python -m py_compile worker/tasks/boss_recruit.py # ✅ 通过
python -m py_compile server/models.py # ✅ 通过
python -m py_compile server/api/followup.py # ✅ 通过
python -m py_compile server/serializers.py # ✅ 通过
```
### 功能测试
```bash
python scripts/test_recruit_features.py # ✅ 全部通过
```
---
## 注意事项
1. **运行迁移**:首次使用前必须运行 `python server/manage.py migrate`
2. **配置优先级**:先匹配岗位配置,没有则使用通用配置
3. **间隔控制**:系统会自动检查间隔时间,避免频繁发送
4. **消息识别**:依赖 `fromId` 字段确保API返回包含此字段
---
## 完成时间
2026年3月5日
---
## 联系支持
如有问题,请查看:
- `复聊配置API使用指南.md` - API详细说明
- `BOSS招聘自动化完整优化说明.md` - 完整技术文档
- `快速参考指南.md` - 快速参考

59
scroll_and_greet.py Normal file
View File

@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
"""按姓名找到求职人 → 滚动到该人 → 点击打招呼DrissionPage三个动作"""
import time
# 求职人姓名
姓名 = "王先生"
def do_greet(page, 姓名, container=None):
"""三个动作1. 找到姓名 2. 滚动到那里 3. 点击打招呼"""
if container is None:
try:
container = page.get_frame("recommendFrame")
except Exception:
container = page
# 1. 找到这个姓名
name_ele = container.ele(f'x://span[contains(text(),"{姓名}")]', timeout=10)
if not name_ele:
name_ele = container.ele(f'x://span[text()="{姓名}"]', timeout=2)
if not name_ele:
raise Exception(f"未找到姓名:{姓名}")
# 2. 滚动到那里
name_ele.run_js("this.scrollIntoView({block: 'center', behavior: 'auto'})")
time.sleep(0.8)
# 3. 点击打招呼(先在该人所在卡片内找)
parent = name_ele.parent()
greet_btn = None
for _ in range(8):
if not parent:
break
greet_btn = parent.ele('x://span[text()="打招呼"]', timeout=0.5) or parent.ele('x://*[contains(text(),"打招呼")]', timeout=0.5)
if greet_btn:
break
parent = parent.parent()
if not greet_btn:
greet_btn = container.ele('x://span[text()="打招呼"]', timeout=2) or container.ele('x://*[contains(text(),"打招呼")]', timeout=1)
if not greet_btn:
raise Exception("未找到「打招呼」按钮")
greet_btn.click(by_js=True)
if __name__ == "__main__":
import sys
from pathlib import Path
_root = Path(__file__).resolve().parent
if str(_root) not in sys.path:
sys.path.insert(0, str(_root))
from worker.bit_browser import BitBrowserAPI
from DrissionPage import ChromiumPage, ChromiumOptions
bit_api = BitBrowserAPI("http://127.0.0.1:54345")
cdp_addr, port, browser_id = bit_api.open_browser(browser_id=None, name="测试2", remark=None)
page = ChromiumPage(addr_or_opts=ChromiumOptions().set_local_port(port=port))
page.get("https://www.zhipin.com/web/geek/recommend")
time.sleep(2)
do_greet(page, 姓名)

View File

@@ -1,157 +0,0 @@
# 代码变更清单
## 修改的文件
### 1. worker/tasks/boss_recruit.py
**状态**: ✅ 已修改并通过语法检查
**主要变更**:
#### 导入语句
- 添加: `from datetime import datetime, timedelta`
#### 主流程修改 (_recruit_flow_like_script)
- 添加筛选逻辑: `friend_list = self._apply_filters(friend_list)`
- 添加消息过滤: `filtered_messages = self._filter_my_messages(messages)`
- 修改关键词检查: 使用 `filtered_messages` 而不是 `messages`
- 添加复聊管理: `reply_result = self._handle_follow_up_chat(tab, name, friend_job_name)`
- 添加保存联系人: `self._save_contact_record(name, friend_job_name, contacts, action_state)`
- 修改联系方式提取: 使用 `filtered_messages` 而不是 `messages`
#### 新增方法 (共7个)
1. `_apply_filters(friend_list)` - 应用筛选条件
2. `_check_activity(last_time, activity_filter)` - 检查活跃度
3. `_check_education(candidate_edu, required_edu)` - 检查学历
4. `_filter_my_messages(messages)` - 过滤自己的消息
5. `_handle_follow_up_chat(tab, name, job_name)` - 处理复聊管理
6. `_send_follow_up_script(tab, job_name)` - 发送跟进话术
7. `_save_contact_record(name, job_name, contacts, action_state)` - 保存联系人记录
## 新增的文件
### 1. BOSS招聘优化说明.md
**状态**: ✅ 已创建
**内容**: 详细的功能说明、使用方法、数据库表说明
### 2. 优化完成总结.md
**状态**: ✅ 已创建
**内容**: 完成的优化内容、测试验证、关键问题解决方案
### 3. 快速参考指南.md
**状态**: ✅ 已创建
**内容**: 核心优化点、快速开始、常见问题
### 4. scripts/init_recruit_test_data.py
**状态**: ✅ 已创建
**功能**: 初始化测试数据(筛选配置、话术配置)
### 5. scripts/test_recruit_features.py
**状态**: ✅ 已创建并通过测试
**功能**: 测试新增功能(时间解析、消息过滤、联系方式提取、学历筛选)
## 测试结果
### 语法检查
```bash
python -m py_compile worker/tasks/boss_recruit.py
```
**结果**: ✅ 通过
### 功能测试
```bash
python scripts/test_recruit_features.py
```
**结果**: ✅ 全部通过
- 时间解析测试: 5/5 通过
- 消息过滤测试: 通过
- 联系方式提取测试: 通过
- 学历筛选测试: 5/5 通过
## 核心问题解决
### 问题1: 识别到自己发送的微信号 ✅
**解决方案**:
- 添加 `_filter_my_messages()` 方法
- 通过 `fromId` 字段区分消息来源
- 只保留 `fromId=0` 的消息(对方发送的)
### 问题2: 联系人没有保存到数据库 ✅
**解决方案**:
- 添加 `_save_contact_record()` 方法
- 提取到联系方式后自动保存到 `ContactRecord`
- 支持去重和更新
### 问题3: 只发送一句话,没有复聊 ✅
**解决方案**:
- 添加 `_handle_follow_up_chat()` 方法
- 发送后等待30秒每3秒检查一次
- 如果没有回复,发送跟进话术
### 问题4: 活跃度时间格式不统一 ✅
**解决方案**:
- 添加 `_check_activity()` 方法
- 支持"03月03日"、"昨天"、"今天"等多种格式
- 自动判断年份
### 问题5: 缺少筛选功能 ✅
**解决方案**:
- 添加 `_apply_filters()` 方法
- 支持活跃度、年龄、学历、期望职位筛选
-`FilterConfig` 表读取配置
## 数据库依赖
### 已存在的表
- `FilterConfig` - 筛选配置表
- `ChatScript` - 话术表
- `ContactRecord` - 联系人记录表
### 需要的字段
所有必需字段已在现有表中定义,无需额外迁移。
## 使用步骤
1. **初始化测试数据**
```bash
python scripts/init_recruit_test_data.py
```
2. **运行功能测试**(可选)
```bash
python scripts/test_recruit_features.py
```
3. **启动招聘任务**
通过API或管理界面启动系统会自动应用所有优化功能。
## 注意事项
1. 确保 `FilterConfig` 表中有 `is_active=True` 的配置
2. 建议配置通用话术(`position="通用"`)作为后备
3. 复聊等待时间默认30秒可根据需要调整
4. 消息过滤依赖 `fromId` 字段确保API返回包含此字段
## 文档位置
- 详细说明: `BOSS招聘优化说明.md`
- 完成总结: `优化完成总结.md`
- 快速参考: `快速参考指南.md`
- 变更清单: `代码变更清单.md`(本文件)
## 代码统计
- 修改文件: 1个
- 新增方法: 7个
- 新增文档: 4个
- 新增脚本: 2个
- 代码行数: +260行
- 测试通过: 100%
## 完成时间
2026年3月5日

View File

@@ -1,174 +0,0 @@
# BOSS招聘自动化优化完成总结
## 优化完成时间
2026年3月5日
## 已完成的优化内容
### 1. ✅ 筛选功能
#### 活跃度筛选
- ✅ 支持解析"03月03日"格式的时间
- ✅ 支持解析"昨天"、"今天"、"刚刚"等相对时间
- ✅ 自动判断年份(如果月份大于当前月份,认为是去年)
- ✅ 支持多种活跃度筛选条件今天活跃、3天内活跃、本周活跃、本月活跃
#### 年龄筛选
- ✅ 从候选人简历的 `resume.age` 字段获取年龄
- ✅ 根据配置的 `age_min``age_max` 进行筛选
#### 学历筛选
- ✅ 从候选人简历的 `resume.education` 字段获取学历
- ✅ 支持学历等级比较:初中 < 高中 < 中专 < 大专 < 本科 < 硕士 < 博士
- 候选人学历需要达到或高于要求学历
#### 期望职位筛选
- 从候选人的 `jobName` 字段获取期望职位
- 支持多个职位关键词匹配配置在 `FilterConfig.positions` 字段
### 2. ✅ 联系人记录管理
#### 自动保存功能
- 从聊天中提取到联系方式后自动保存到 `ContactRecord`
- 保存信息包括姓名岗位联系方式回复状态是否交换微信联系时间备注
- 去重处理检查是否已存在相同姓名和联系方式的记录
- 如果存在则更新不存在则创建新记录
### 3. ✅ 复聊管理
#### 消息过滤(核心功能)
- **过滤自己发送的消息**通过 `fromId` 字段判断消息来源
- `fromId=0` 表示对方发送的消息
- 其他 `fromId` 值表示自己发送的消息
- 只保留对方的消息进行联系方式识别
- **解决了之前"发送带微信号的消息后,识别到自己消息"的问题**
#### 等待回复功能
- 发送询问微信号后等待最多30秒
- 每3秒检查一次是否有新回复
- 自动识别对方回复中的联系方式
- 记录是否得到回复是否提取到联系方式
#### 跟进话术功能
- 如果对方没有回复可以发送跟进话术
- 支持按岗位配置不同的跟进话术
- `ChatScript` 表中读取话术`script_type="followup"`
- 如果没有特定岗位话术使用通用话术`position="通用"`
## 代码修改文件
### 主要修改文件
- `worker/tasks/boss_recruit.py` - 招聘任务处理器已优化
### 新增方法
1. `_apply_filters()` - 应用筛选条件
2. `_check_activity()` - 检查活跃度
3. `_check_education()` - 检查学历
4. `_filter_my_messages()` - 过滤自己的消息
5. `_handle_follow_up_chat()` - 处理复聊管理
6. `_send_follow_up_script()` - 发送跟进话术
7. `_save_contact_record()` - 保存联系人记录
### 修改的方法
- `_recruit_flow_like_script()` - 主流程添加了筛选消息过滤复聊管理保存联系人记录
## 测试验证
### 功能测试
所有功能测试通过`scripts/test_recruit_features.py`
- 时间解析测试5/5 通过
- 消息过滤测试成功过滤掉自己发送的消息
- 联系方式提取测试正确提取微信号和手机号
- 学历筛选测试5/5 通过
### 语法检查
Python语法检查通过`python -m py_compile`
## 使用说明
### 1. 初始化测试数据
```bash
python scripts/init_recruit_test_data.py
```
这将创建
- 筛选配置示例Python开发筛选配置
- 话术配置示例首次回复跟进回复微信交换等
### 2. 配置筛选条件
在数据库 `filter_config` 表中配置或通过管理界面配置
- 年龄范围
- 学历要求
- 活跃度要求
- 期望职位列表
### 3. 配置复聊话术
在数据库 `chat_script` 表中配置或通过管理界面配置
- 按岗位配置不同的话术
- 配置通用话术作为后备
### 4. 运行招聘任务
任务会自动执行以下流程
1. 获取候选人列表
2. 应用筛选条件活跃度年龄学历职位
3. 逐个打开会话
4. **过滤自己的消息,只分析对方消息**
5. 如果没有联系方式发送询问
6. 等待对方回复并识别联系方式
7. **自动保存联系人记录到数据库**
8. 如果需要发送跟进话术
## 关键问题解决
### 问题1识别到自己发送的微信号
**原因**之前没有区分消息来源所有消息都进行联系方式识别
**解决方案**
- 添加 `_filter_my_messages()` 方法
- 通过 `fromId` 字段判断消息来源
- 只保留 `fromId=0` 的消息对方发送的
- 在提取联系方式前先过滤消息
### 问题2联系人没有保存到数据库
**原因**之前只是收集联系方式没有保存到数据库
**解决方案**
- 添加 `_save_contact_record()` 方法
- 在提取到联系方式后自动保存
- 支持去重和更新
### 问题3只发送一句话没有复聊
**原因**之前只发送一次询问不等待回复
**解决方案**
- 添加 `_handle_follow_up_chat()` 方法
- 发送后等待30秒每3秒检查一次
- 如果没有回复发送跟进话术
- 记录回复状态
### 问题4活跃度时间格式不统一
**原因**BOSS直聘返回的时间格式多样"03月03日"、"昨天"
**解决方案**
- 添加 `_check_activity()` 方法
- 支持多种时间格式解析
- 自动判断年份
## 注意事项
1. **筛选配置**确保 `FilterConfig` 表中有 `is_active=True` 的配置
2. **话术配置**建议配置通用话术作为后备
3. **等待时间**复聊等待时间默认30秒可根据需要调整
4. **消息识别**依赖 `fromId` 字段确保API返回的消息包含此字段
## 后续建议
1. 可以添加更多的筛选条件如工作经验期望薪资等
2. 可以优化复聊策略如根据对方回复内容智能选择话术
3. 可以添加数据统计功能如筛选通过率回复率等
4. 可以添加黑名单功能避免重复联系
## 文档
- 详细说明`BOSS招聘优化说明.md`
- 测试脚本`scripts/test_recruit_features.py`
- 初始化脚本`scripts/init_recruit_test_data.py`

View File

@@ -1,361 +0,0 @@
# 复聊配置 API 使用指南
## API 端点
### 1. 复聊配置管理
#### 获取配置列表
```http
GET /api/followup-configs
```
查询参数:
- `position`: 岗位类型(可选)
- `is_active`: 是否启用(可选)
响应示例:
```json
[
{
"id": 1,
"name": "Python开发复聊配置",
"position": "Python开发",
"is_active": true,
"scripts": [
{
"id": 1,
"day_number": 1,
"content": "后续沟通会更及时,您方便留一下您的微信号吗?我这边加您。",
"interval_hours": 24,
"order": 1
}
]
}
]
```
#### 创建配置
```http
POST /api/followup-configs
Content-Type: application/json
{
"name": "Python开发复聊配置",
"position": "Python开发",
"is_active": true
}
```
#### 更新配置
```http
PUT /api/followup-configs/{id}
Content-Type: application/json
{
"name": "Python开发复聊配置更新",
"is_active": true
}
```
#### 删除配置
```http
DELETE /api/followup-configs/{id}
```
### 2. 复聊话术管理
#### 获取话术列表
```http
GET /api/followup-scripts?config_id=1
```
查询参数:
- `config_id`: 配置ID可选
- `day_number`: 第几天(可选)
#### 创建话术
```http
POST /api/followup-scripts
Content-Type: application/json
{
"config_id": 1,
"day_number": 1,
"content": "您好,期待与您进一步沟通。",
"interval_hours": 24,
"order": 1,
"is_active": true
}
```
**字段说明**
- `day_number`:
- `1` = 第一天
- `2` = 第二天
- `0` = 往后一直使用这个话术
- `interval_hours`: 距离上次发送的间隔小时数
- `order`: 同一天有多条话术时的排序
#### 更新话术
```http
PUT /api/followup-scripts/{id}
Content-Type: application/json
{
"content": "更新后的话术内容",
"interval_hours": 48
}
```
#### 删除话术
```http
DELETE /api/followup-scripts/{id}
```
### 3. 复聊记录查询
#### 获取记录列表
```http
GET /api/followup-records?contact_id=1
```
查询参数:
- `contact_id`: 联系人ID可选
- `config_id`: 配置ID可选
- `got_reply`: 是否得到回复(可选)
- `page`: 页码默认1
- `page_size`: 每页数量默认20
响应示例:
```json
{
"total": 10,
"page": 1,
"page_size": 20,
"results": [
{
"id": 1,
"contact_id": 1,
"config_id": 1,
"script_id": 1,
"day_number": 1,
"content": "您好,期待与您进一步沟通。",
"sent_at": "2026-03-05T10:30:00Z",
"got_reply": true,
"reply_content": "好的,我的微信是 wx123456",
"replied_at": "2026-03-05T10:35:00Z"
}
]
}
```
#### 手动发送复聊消息
```http
POST /api/followup-records/send
Content-Type: application/json
{
"contact_id": 1,
"content": "您好,我想和您聊聊这个职位。"
}
```
## 使用场景
### 场景1创建Python开发的复聊配置
```bash
# 1. 创建配置
curl -X POST http://localhost:8000/api/followup-configs \
-H "Content-Type: application/json" \
-d '{
"name": "Python开发复聊配置",
"position": "Python开发",
"is_active": true
}'
# 假设返回的 config_id = 1
# 2. 添加第1天的话术
curl -X POST http://localhost:8000/api/followup-scripts \
-H "Content-Type: application/json" \
-d '{
"config_id": 1,
"day_number": 1,
"content": "后续沟通会更及时,您方便留一下您的微信号吗?我这边加您。",
"interval_hours": 24,
"order": 1,
"is_active": true
}'
# 3. 添加第2天的话术
curl -X POST http://localhost:8000/api/followup-scripts \
-H "Content-Type: application/json" \
-d '{
"config_id": 1,
"day_number": 2,
"content": "您好,不知道您是否方便留个联系方式?",
"interval_hours": 24,
"order": 1,
"is_active": true
}'
# 4. 添加"往后一直"的话术
curl -X POST http://localhost:8000/api/followup-scripts \
-H "Content-Type: application/json" \
-d '{
"config_id": 1,
"day_number": 0,
"content": "您好,如果您感兴趣可以随时联系我。",
"interval_hours": 72,
"order": 1,
"is_active": true
}'
```
### 场景2查看某个联系人的复聊记录
```bash
curl http://localhost:8000/api/followup-records?contact_id=1
```
### 场景3手动发送复聊消息
```bash
curl -X POST http://localhost:8000/api/followup-records/send \
-H "Content-Type: application/json" \
-d '{
"contact_id": 1,
"content": "您好,我想和您聊聊这个职位。"
}'
```
## 复聊逻辑说明
### 自动复聊流程
1. **第一次联系**:发送询问微信号的消息
2. **等待回复**等待30秒检查是否有回复
3. **第1天**如果没有回复发送第1天的话术
4. **第2天**如果还没有回复且距离上次发送超过24小时发送第2天的话术
5. **第3天及以后**:继续按配置的间隔时间发送话术
6. **往后一直**:当没有特定天数的话术时,使用 `day_number=0` 的话术
### 消息过滤逻辑
**问题**:之前会识别到自己发送的包含"微信号"三个字的消息
**解决方案**
1. 通过 `fromId` 字段区分消息来源
2. 只保留 `fromId=0` 的消息(对方发送的)
3. 在等待回复时,过滤掉包含发送话术内容的消息
4. 过滤掉包含"微信号"关键词但没有真实微信号的消息
### 间隔时间控制
- 每条话术都有 `interval_hours` 字段
- 系统会检查距离上次发送的时间
- 只有超过间隔时间才会发送下一条
- 避免频繁打扰候选人
## 数据库表结构
### FollowUpConfig复聊配置表
```sql
CREATE TABLE follow_up_config (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(128),
position VARCHAR(64),
is_active BOOLEAN DEFAULT TRUE,
created_at DATETIME,
updated_at DATETIME
);
```
### FollowUpScript复聊话术表
```sql
CREATE TABLE follow_up_script (
id INT PRIMARY KEY AUTO_INCREMENT,
config_id INT,
day_number INT, -- 1=第一天, 2=第二天, 0=往后一直
content TEXT,
interval_hours INT DEFAULT 24,
order INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at DATETIME
);
```
### FollowUpRecord复聊记录表
```sql
CREATE TABLE follow_up_record (
id INT PRIMARY KEY AUTO_INCREMENT,
contact_id INT,
config_id INT,
script_id INT,
day_number INT,
content TEXT,
sent_at DATETIME,
got_reply BOOLEAN DEFAULT FALSE,
reply_content TEXT,
replied_at DATETIME
);
```
## 前端集成示例
### Vue.js 示例
```javascript
// 获取复聊配置列表
async function getFollowUpConfigs() {
const response = await fetch('/api/followup-configs');
const configs = await response.json();
return configs;
}
// 创建复聊配置
async function createFollowUpConfig(data) {
const response = await fetch('/api/followup-configs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
return await response.json();
}
// 添加话术
async function addFollowUpScript(configId, scriptData) {
const response = await fetch('/api/followup-scripts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
config_id: configId,
...scriptData
})
});
return await response.json();
}
// 查看复聊记录
async function getFollowUpRecords(contactId) {
const response = await fetch(`/api/followup-records?contact_id=${contactId}`);
const data = await response.json();
return data.results;
}
```
## 注意事项
1. **配置优先级**:先匹配岗位配置,如果没有则使用通用配置
2. **话术顺序**:按 `day_number``order` 排序
3. **间隔控制**:系统会自动检查间隔时间,避免频繁发送
4. **消息过滤**:只识别对方发送的消息,避免误识别
5. **自动保存**:提取到联系方式后自动保存到 `ContactRecord`
## 测试建议
1. 先创建一个测试配置设置较短的间隔时间如1小时
2. 运行招聘任务,观察复聊是否正常工作
3. 检查 `FollowUpRecord` 表,确认记录是否正确保存
4. 根据实际效果调整话术内容和间隔时间

View File

@@ -1,148 +0,0 @@
# BOSS招聘自动化 - 快速参考指南
## 核心优化点
### 1. 筛选功能 ✅
```python
# 在 FilterConfig 表中配置
{
"age_min": 22,
"age_max": 35,
"education": "本科",
"activity": "3天内活跃",
"positions": ["Python开发", "后端开发"]
}
```
### 2. 消息过滤 ✅(解决识别自己消息的问题)
```python
# 通过 fromId 字段区分消息来源
# fromId = 0 -> 对方发送的消息
# fromId != 0 -> 自己发送的消息
filtered_messages = [msg for msg in messages if msg.get("fromId", 0) == 0]
```
### 3. 自动保存联系人 ✅
```python
# 提取到联系方式后自动保存到 ContactRecord 表
ContactRecord.objects.create(
name=name,
position=job_name,
contact=wechat_or_phone,
reply_status="已回复" if got_reply else "未回复",
wechat_exchanged=exchange_confirmed,
contacted_at=timezone.now()
)
```
### 4. 复聊管理 ✅
```python
# 发送询问后等待30秒每3秒检查一次
# 如果没有回复,发送跟进话术
if action_state["send_success"]:
reply_result = self._handle_follow_up_chat(tab, name, job_name)
```
## 时间格式支持
| 格式 | 示例 | 解析结果 |
|------|------|----------|
| 月日格式 | "03月03日" | 2026-03-03 或 2025-03-03 |
| 相对时间 | "昨天" | 当前日期 - 1天 |
| 相对时间 | "今天" | 当前日期 |
| 相对时间 | "刚刚" | 当前日期 |
## 学历等级
```
初中 < 高中 < 中专 < 大专 < 本科 < 硕士 < 博士
```
候选人学历需要 >= 要求学历
## 活跃度筛选
| 配置值 | 含义 |
|--------|------|
| "今天活跃" | 最后上线时间在今天 |
| "3天内活跃" | 最后上线时间在3天内 |
| "本周活跃" | 最后上线时间在7天内 |
| "本月活跃" | 最后上线时间在30天内 |
| "不限" | 不筛选活跃度 |
## 话术类型
| script_type | 说明 | 使用场景 |
|-------------|------|----------|
| first | 首次回复 | 第一次联系候选人 |
| followup | 跟进回复 | 候选人没有回复时 |
| wechat | 微信交换 | 询问微信号 |
| closing | 结束语 | 结束对话 |
## 快速开始
### 1. 初始化测试数据
```bash
python scripts/init_recruit_test_data.py
```
### 2. 运行功能测试
```bash
python scripts/test_recruit_features.py
```
### 3. 启动招聘任务
通过API或管理界面启动招聘任务系统会自动
- 应用筛选条件
- 过滤自己的消息
- 保存联系人记录
- 进行复聊管理
## 常见问题
### Q1: 为什么识别到了自己发送的微信号?
**A**: 已修复。现在通过 `fromId` 字段过滤消息,只识别对方发送的消息。
### Q2: 联系人记录在哪里查看?
**A**: 在 `ContactRecord` 表中,可以通过 `/api/contacts` 接口查询。
### Q3: 如何配置不同岗位的话术?
**A**: 在 `ChatScript` 表中,设置 `position` 字段为岗位名称,`script_type` 为话术类型。
### Q4: 筛选条件不生效?
**A**: 检查 `FilterConfig` 表中是否有 `is_active=True` 的配置。
### Q5: 如何调整复聊等待时间?
**A**: 修改 `_handle_follow_up_chat()` 方法中的 `max_wait` 参数默认30秒
## 数据库表
### FilterConfig筛选配置
- `age_min`, `age_max` - 年龄范围
- `education` - 学历要求
- `activity` - 活跃度要求
- `positions` - 期望职位列表JSON数组
- `is_active` - 是否启用
### ChatScript话术配置
- `position` - 岗位类型
- `script_type` - 话术类型
- `content` - 话术内容
- `is_active` - 是否启用
### ContactRecord联系人记录
- `name` - 姓名
- `position` - 岗位
- `contact` - 联系方式
- `reply_status` - 回复状态
- `wechat_exchanged` - 是否交换微信
- `contacted_at` - 联系时间
## 代码位置
- 主文件:`worker/tasks/boss_recruit.py`
- 测试脚本:`scripts/test_recruit_features.py`
- 初始化脚本:`scripts/init_recruit_test_data.py`
- 详细说明:`BOSS招聘优化说明.md`
- 完成总结:`优化完成总结.md`

View File

@@ -1,47 +0,0 @@
# 部署说明
## 一、服务器部署
在服务器上,只需执行:
```bash
python app.py
```
即可启动中央服务器Django + Channels + 隧道)。
---
## 二、客户端部署(线下有比特浏览器的电脑)
客户电脑可能不懂代码,需要提供 **GUI 程序**PyQt5完成「更新」和「启动」。
### 方式 A源码运行需安装 Python
1. 将项目拷贝到客户电脑
2. 安装依赖:`pip install -r requirements.txt`
3. 运行 GUI`python run_client.py`
### 方式 B打包为 exe推荐无需 Python
1. 安装 PyInstaller`pip install pyinstaller`
2. 在项目根目录执行打包:`pyinstaller --clean build_client.spec`
3.`dist/BOSS直聘Worker客户端.exe` 发给客户下载后先保存到本地文件夹如桌面再双击运行GUI 与 Worker 已合并为同一程序,无需 worker.exe
### GUI 功能说明
| 按钮 | 功能 |
|------|------|
| **更新代码** | 在项目目录执行 `git pull`,拉取最新自动化代码 |
| **启动** | 启动 Worker连接服务器并执行任务 |
配置项说明:
- **服务器地址**:中央服务器 WebSocket 地址,如 `ws://8.137.99.82:9000/ws`
- **Worker ID**:本机 Worker 唯一标识
- **Worker 名称**便于识别的名称如「电脑A」
> **说明**
> - 若使用「更新」功能,需在客户电脑安装 Git并将 exe 放在项目git 仓库)目录内。
> - 若不使用「更新」,可只分发打包后的 exe客户只需填写服务器地址等信息后点击「启动」即可。
> - 不要直接在浏览器下载弹窗里点“打开”,请先保存到本地后再运行,避免在临时目录运行导致依赖加载失败。