1
This commit is contained in:
@@ -18,6 +18,75 @@
|
||||
- 投递状态跟踪
|
||||
- 投递记录管理
|
||||
|
||||
## 📊 Boss直聘响应数据结构
|
||||
|
||||
### 响应格式示例
|
||||
|
||||
Boss直聘搜索职位列表的响应数据结构如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"code": 0,
|
||||
"message": "Success",
|
||||
"zpData": {
|
||||
"resCount": 450, // 搜索结果总数
|
||||
"hasMore": true, // 是否还有更多
|
||||
"totalCount": 300, // 总职位数
|
||||
"jobList": [ // 职位列表
|
||||
{
|
||||
"encryptJobId": "5ae70dfe114c23ab0nR-2ti-FFpU", // 职位ID(投递必需)
|
||||
"encryptBossId": "b55854108ac215180XZ62N-_FlNT", // Boss ID(投递必需)
|
||||
"securityId": "HP23zbQfaslvy-c1...", // 安全ID(投递必需)
|
||||
"jobName": "全栈软件工程师", // 职位名称
|
||||
"salaryDesc": "25-50K·19薪", // 薪资描述(需解析)
|
||||
"jobExperience": "在校/应届", // 工作经验(需解析)
|
||||
"jobDegree": "学历不限", // 学历要求
|
||||
"city": 101020100, // 城市代码
|
||||
"cityName": "上海", // 城市名称
|
||||
"areaDistrict": "长宁区", // 区域
|
||||
"businessDistrict": "新华路", // 商圈
|
||||
"gps": { // 位置信息(优先使用)
|
||||
"longitude": 121.41902537687392,
|
||||
"latitude": 31.210308153576566
|
||||
},
|
||||
"encryptBrandId": "d283b66de3cefd891H1529q5Flc~", // 公司ID
|
||||
"brandName": "上海大裂谷智能科技", // 公司名称
|
||||
"brandScaleName": "100-499人", // 公司规模
|
||||
"brandIndustry": "人工智能", // 公司行业
|
||||
"brandStageName": "天使轮", // 融资阶段
|
||||
"bossName": "杨明雨", // Boss姓名
|
||||
"bossTitle": "HR", // Boss职位
|
||||
"bossOnline": true, // Boss是否在线
|
||||
"jobLabels": ["在校/应届", "学历不限"], // 职位标签
|
||||
"skills": [], // 技能要求
|
||||
"welfareList": ["带薪年假", "五险一金"], // 福利列表
|
||||
"proxyJob": 0, // 是否外包(0否1是)
|
||||
"industry": 100028 // 行业代码
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 关键字段说明
|
||||
|
||||
1. **投递必需字段**:
|
||||
- `encryptJobId`: 职位ID,投递时必须
|
||||
- `encryptBossId`: Boss ID,投递时必须
|
||||
- `securityId`: 安全ID,投递时必须(每次搜索可能不同)
|
||||
|
||||
2. **位置信息**:
|
||||
- `gps.longitude` 和 `gps.latitude`: 直接使用,无需调用位置服务API
|
||||
- 如果没有gps字段,再使用 `cityName + areaDistrict + businessDistrict + brandName` 调用位置服务
|
||||
|
||||
3. **薪资解析**:
|
||||
- `salaryDesc` 格式多样:`"25-50K·19薪"`、`"20-30K"`、`"面议"` 等
|
||||
- 需要解析出 `salaryMin` 和 `salaryMax`(单位:元)
|
||||
|
||||
4. **工作年限解析**:
|
||||
- `jobExperience` 可能为:`"在校/应届"`、`"3-5年"`、`"1-3年"` 等
|
||||
- 需要解析出 `experienceMin` 和 `experienceMax`
|
||||
|
||||
## 📊 功能架构
|
||||
|
||||
```
|
||||
@@ -244,13 +313,81 @@ async createDeliverTask(params) {
|
||||
|
||||
**任务内容**:
|
||||
1. 完善字段映射(从Boss直聘响应数据提取更多字段)
|
||||
2. 优化位置解析(减少API调用,添加缓存)
|
||||
3. 添加职位状态管理
|
||||
4. 添加职位匹配度字段
|
||||
2. 优化位置解析(优先使用响应中的gps字段,减少API调用)
|
||||
3. 解析薪资范围(从salaryDesc提取min/max)
|
||||
4. 解析工作年限(从jobExperience提取min/max)
|
||||
5. 添加职位状态管理
|
||||
6. 添加职位匹配度字段
|
||||
|
||||
**Boss直聘响应数据字段映射表**:
|
||||
|
||||
| 响应字段 | 数据库字段 | 说明 | 示例值 |
|
||||
|---------|-----------|------|--------|
|
||||
| `encryptJobId` | `jobId` | 职位ID(加密) | "5ae70dfe114c23ab0nR-2ti-FFpU" |
|
||||
| `jobName` | `jobTitle` | 职位名称 | "全栈软件工程师" |
|
||||
| `encryptBrandId` | `companyId` | 公司ID(加密) | "d283b66de3cefd891H1529q5Flc~" |
|
||||
| `brandName` | `companyName` | 公司名称 | "上海大裂谷智能科技" |
|
||||
| `brandScaleName` | `companySize` | 公司规模 | "100-499人" |
|
||||
| `brandIndustry` | `companyIndustry` | 公司行业 | "人工智能" |
|
||||
| `brandStageName` | `brandStage` | 融资阶段 | "天使轮" |
|
||||
| `salaryDesc` | `salary` | 薪资描述 | "25-50K·19薪" |
|
||||
| `salaryDesc` | `salaryMin`, `salaryMax` | 薪资范围(需解析) | 25000, 50000 |
|
||||
| `jobExperience` | `experience` | 工作经验 | "在校/应届" |
|
||||
| `jobExperience` | `experienceMin`, `experienceMax` | 工作年限范围(需解析) | - |
|
||||
| `jobDegree` | `education` | 学历要求 | "学历不限" |
|
||||
| `jobDegree` | `educationLevel` | 学历等级(需映射) | - |
|
||||
| `city` | `city` | 城市代码 | 101020100 |
|
||||
| `cityName` | `cityName` | 城市名称 | "上海" |
|
||||
| `areaDistrict` | `areaDistrict` | 区域 | "长宁区" |
|
||||
| `businessDistrict` | `businessDistrict` | 商圈 | "新华路" |
|
||||
| `gps.longitude` | `longitude` | 经度(优先使用) | 121.41902537687392 |
|
||||
| `gps.latitude` | `latitude` | 纬度(优先使用) | 31.210308153576566 |
|
||||
| `encryptBossId` | `encryptBossId` | Boss ID(投递需要) | "b55854108ac215180XZ62N-_FlNT" |
|
||||
| `securityId` | `securityId` | 安全ID(投递需要) | "HP23zbQfaslvy-c1..." |
|
||||
| `bossName` | `bossName` | Boss姓名 | "杨明雨" |
|
||||
| `bossTitle` | `bossTitle` | Boss职位 | "HR" |
|
||||
| `bossOnline` | `bossOnline` | Boss是否在线 | true |
|
||||
| `jobLabels` | `jobLabels` | 职位标签(JSON) | ["在校/应届", "学历不限"] |
|
||||
| `skills` | `skills` | 技能要求(JSON) | ["Java", "MySQL"] |
|
||||
| `welfareList` | `welfareList` | 福利列表(JSON) | ["带薪年假", "五险一金"] |
|
||||
| `proxyJob` | `isOutsourcing` | 是否外包 | 0/1 |
|
||||
| `industry` | `industry` | 行业代码 | 100028 |
|
||||
|
||||
**关键优化点**:
|
||||
|
||||
1. **位置信息**: 优先使用响应中的 `gps.longitude` 和 `gps.latitude`,避免调用位置服务API
|
||||
- 如果 `gps` 字段存在,直接使用
|
||||
- 如果不存在,再使用 `cityName + areaDistrict + businessDistrict + brandName` 调用位置服务
|
||||
|
||||
2. **薪资解析**: 从 `salaryDesc` 解析薪资范围
|
||||
- 格式示例:`"25-50K·19薪"` → min: 25000, max: 50000
|
||||
- 格式示例:`"20-30K"` → min: 20000, max: 30000
|
||||
- 格式示例:`"面议"` → min: 0, max: 0
|
||||
- 格式示例:`"15K以上"` → min: 15000, max: 999999
|
||||
- 需要处理:K(千)、W(万)、薪(年终奖倍数)
|
||||
|
||||
3. **工作年限解析**: 从 `jobExperience` 解析年限范围
|
||||
- `"在校/应届"` → min: 0, max: 0
|
||||
- `"1-3年"` → min: 1, max: 3
|
||||
- `"3-5年"` → min: 3, max: 5
|
||||
- `"5-10年"` → min: 5, max: 10
|
||||
- `"10年以上"` → min: 10, max: 99
|
||||
|
||||
4. **学历映射**: 将学历描述映射为等级
|
||||
- `"学历不限"` → `"unlimited"`
|
||||
- `"高中"` → `"high_school"`
|
||||
- `"大专"` → `"college"`
|
||||
- `"本科"` → `"bachelor"`
|
||||
- `"硕士"` → `"master"`
|
||||
- `"博士"` → `"doctor"`
|
||||
|
||||
5. **投递必需字段**: 确保保存 `encryptJobId`、`encryptBossId` 和 `securityId`
|
||||
- 这些字段在投递时必须使用
|
||||
- `securityId` 每次搜索可能不同,需要实时保存
|
||||
|
||||
**代码位置**: 第215-308行
|
||||
|
||||
**预计工作量**: 3小时
|
||||
**预计工作量**: 4小时(增加字段解析逻辑)
|
||||
|
||||
---
|
||||
|
||||
@@ -291,48 +428,67 @@ async createDeliverTask(params) {
|
||||
|
||||
---
|
||||
|
||||
### 任务5: 创建搜索任务接口
|
||||
### 任务5: 创建搜索任务接口(支持可选投递)
|
||||
|
||||
**文件**: `api/services/pla_account_service.js`
|
||||
|
||||
**新增方法**: `createSearchJobListTask()`
|
||||
|
||||
**方法签名**:
|
||||
```javascript
|
||||
/**
|
||||
* 创建搜索职位列表任务(支持可选投递)
|
||||
* @param {Object} params - 任务参数
|
||||
* @param {number} params.id - 账号ID
|
||||
* @param {string} params.keyword - 搜索关键词
|
||||
* @param {Object} params.searchParams - 搜索条件(城市、薪资、经验、学历等)
|
||||
* @param {number} params.pageCount - 获取页数
|
||||
* @param {boolean} params.autoDeliver - 是否自动投递(默认false)
|
||||
* @param {Object} params.filterRules - 过滤规则(autoDeliver=true时使用)
|
||||
* @param {number} params.maxCount - 最大投递数量(autoDeliver=true时使用)
|
||||
* @returns {Promise<Object>} 任务创建结果 { taskId, message, jobCount, deliveredCount }
|
||||
*/
|
||||
async createSearchJobListTask(params) {
|
||||
// 1. 验证账号和授权
|
||||
// 2. 创建任务记录 (taskType: 'search_jobs' 或 'auto_deliver')
|
||||
// 3. 生成搜索指令
|
||||
// 4. 执行搜索指令
|
||||
// 5. 等待搜索完成(职位会自动保存到数据库)
|
||||
// 6. 如果 autoDeliver=true:
|
||||
// - 从数据库获取刚搜索到的职位列表
|
||||
// - 根据简历信息和过滤规则匹配职位
|
||||
// - 生成投递指令序列
|
||||
// - 执行投递指令(带间隔控制)
|
||||
// - 保存投递记录
|
||||
// - 更新职位状态
|
||||
// 7. 返回任务信息
|
||||
}
|
||||
```
|
||||
|
||||
**任务内容**:
|
||||
1. 验证账号和授权
|
||||
2. 创建任务记录
|
||||
3. 生成搜索指令
|
||||
4. 执行指令
|
||||
5. 返回任务信息
|
||||
2. 创建任务记录(根据autoDeliver参数设置taskType: 'search_jobs' 或 'auto_deliver')
|
||||
3. 生成搜索指令(command_type: 'get_job_list')
|
||||
4. 执行搜索指令(通过MQTT发送到设备)
|
||||
5. 等待搜索完成(职位会自动保存到数据库)
|
||||
6. 如果 `autoDeliver=true`,继续执行投递流程:
|
||||
- 从数据库获取刚搜索到的职位列表(applyStatus = 'pending')
|
||||
- 根据简历信息和过滤规则匹配职位(距离、薪资、工作年限、学历等)
|
||||
- 为每个匹配的职位生成投递指令(command_type: 'apply_job')
|
||||
- 批量执行投递指令(带间隔控制,避免频繁投递)
|
||||
- 保存投递记录 (apply_records)
|
||||
- 更新职位状态 (job_postings.applyStatus = 'applied')
|
||||
7. 返回任务信息(包含搜索到的职位数量和投递数量)
|
||||
|
||||
**代码位置**: 在 `runCommand()` 方法后添加
|
||||
|
||||
**预计工作量**: 3小时
|
||||
**预计工作量**: 5小时(增加投递逻辑)
|
||||
|
||||
---
|
||||
|
||||
### 任务6: 创建投递任务接口
|
||||
|
||||
**文件**: `api/services/pla_account_service.js`
|
||||
|
||||
**新增方法**: `createDeliverTask()`
|
||||
|
||||
**任务内容**:
|
||||
1. 验证账号和授权
|
||||
2. 创建任务记录
|
||||
3. 生成搜索指令(获取职位列表)
|
||||
4. 等待搜索完成
|
||||
5. 获取匹配的职位
|
||||
6. 生成投递指令序列
|
||||
7. 执行投递指令
|
||||
8. 返回任务信息
|
||||
|
||||
**代码位置**: 在 `createSearchJobListTask()` 方法后添加
|
||||
|
||||
**预计工作量**: 4小时
|
||||
|
||||
---
|
||||
|
||||
### 任务7: 完善指令类型映射
|
||||
### 任务6: 完善指令类型映射
|
||||
|
||||
**文件**: `api/middleware/schedule/command.js`
|
||||
|
||||
@@ -349,7 +505,7 @@ async createDeliverTask(params) {
|
||||
|
||||
---
|
||||
|
||||
### 任务8: 添加搜索条件配置管理
|
||||
### 任务7: 添加搜索条件配置管理
|
||||
|
||||
**文件**: `api/model/pla_account.js`
|
||||
|
||||
@@ -376,16 +532,18 @@ async createDeliverTask(params) {
|
||||
|
||||
## 🔄 工作流程
|
||||
|
||||
### 搜索职位列表流程
|
||||
### 搜索职位列表流程(支持可选投递)
|
||||
|
||||
```
|
||||
1. 用户/系统调用 createSearchJobListTask()
|
||||
- 参数: { id, keyword, searchParams, pageCount, autoDeliver: true/false, filterRules, maxCount }
|
||||
↓
|
||||
2. 创建任务记录 (task_status)
|
||||
- taskType: 'search_jobs' 或 'auto_deliver'(根据autoDeliver参数)
|
||||
↓
|
||||
3. 生成搜索指令 (task_commands)
|
||||
- command_type: 'get_job_list'
|
||||
- command_params: { keyword, city, salary, ... }
|
||||
- command_params: { keyword, city, salary, experience, education, ... }
|
||||
↓
|
||||
4. 执行指令 (通过MQTT发送到设备)
|
||||
↓
|
||||
@@ -393,62 +551,79 @@ async createDeliverTask(params) {
|
||||
↓
|
||||
6. 保存职位到数据库 (job_postings)
|
||||
- 去重处理
|
||||
- 位置解析
|
||||
- 位置解析(优先使用gps字段)
|
||||
- 字段映射
|
||||
- 状态: applyStatus = 'pending'(待投递)
|
||||
↓
|
||||
7. 更新指令状态为完成
|
||||
7. 更新搜索指令状态为完成
|
||||
↓
|
||||
8. 更新任务状态为完成
|
||||
```
|
||||
|
||||
### 投递职位流程
|
||||
|
||||
```
|
||||
1. 用户/系统调用 createDeliverTask()
|
||||
8. 如果 autoDeliver=true,继续执行投递流程:
|
||||
↓
|
||||
2. 创建任务记录 (task_status)
|
||||
8.1 从数据库获取刚搜索到的职位列表
|
||||
- 筛选条件: applyStatus = 'pending', sn_code = 账号SN码
|
||||
↓
|
||||
3. 生成搜索指令 (获取职位列表)
|
||||
- command_type: 'get_job_list'
|
||||
↓
|
||||
4. 执行搜索指令
|
||||
↓
|
||||
5. 获取职位列表并保存到数据库
|
||||
↓
|
||||
6. 根据简历信息和过滤规则匹配职位
|
||||
- 距离匹配
|
||||
- 薪资匹配
|
||||
- 工作年限匹配
|
||||
- 学历匹配
|
||||
8.2 根据简历信息和过滤规则匹配职位
|
||||
- 距离匹配(基于经纬度)
|
||||
- 薪资匹配(解析salaryDesc)
|
||||
- 工作年限匹配(解析jobExperience)
|
||||
- 学历匹配(解析jobDegree)
|
||||
- 权重评分
|
||||
↓
|
||||
7. 为每个匹配的职位生成投递指令
|
||||
8.3 为每个匹配的职位生成投递指令
|
||||
- command_type: 'apply_job'
|
||||
- command_params: { jobId, encryptBossId, ... }
|
||||
- command_params: {
|
||||
jobId: job.encryptJobId, // 职位ID(必需)
|
||||
encryptBossId: job.encryptBossId, // Boss ID(必需)
|
||||
securityId: job.securityId, // 安全ID(必需,从最新搜索结果获取)
|
||||
brandName: job.brandName, // 公司名称(可选)
|
||||
jobTitle: job.jobName // 职位名称(可选)
|
||||
}
|
||||
↓
|
||||
8. 批量执行投递指令(带间隔控制)
|
||||
8.4 批量执行投递指令(带间隔控制,避免频繁投递)
|
||||
↓
|
||||
9. 保存投递记录 (apply_records)
|
||||
8.5 保存投递记录 (apply_records)
|
||||
↓
|
||||
10. 更新职位状态 (job_postings.applyStatus)
|
||||
8.6 更新职位状态 (job_postings.applyStatus = 'applied')
|
||||
↓
|
||||
11. 更新任务状态为完成
|
||||
9. 更新任务状态为完成
|
||||
↓
|
||||
10. 返回任务信息(包含搜索到的职位数量和投递数量)
|
||||
```
|
||||
|
||||
**说明**:
|
||||
- 此接口支持两种模式:
|
||||
- `autoDeliver=false`: 仅搜索,不投递。职位保存到数据库,状态为'pending'
|
||||
- `autoDeliver=true`: 搜索完成后立即投递匹配的职位
|
||||
- **重要**: 投递必须在搜索完成后立即执行,因为 `securityId` 等字段可能有时效性,前端页面变化后这些字段可能失效
|
||||
- 不支持从已保存的职位中选择投递,因为职位信息可能已过期
|
||||
|
||||
## 📊 数据库字段说明
|
||||
|
||||
### job_postings 表需要完善的字段
|
||||
|
||||
| 字段名 | 类型 | 说明 | 状态 |
|
||||
|--------|------|------|------|
|
||||
| `city` | VARCHAR | 城市代码 | 待添加 |
|
||||
| `cityName` | VARCHAR | 城市名称 | 待添加 |
|
||||
| `salaryMin` | INT | 最低薪资(元) | 待添加 |
|
||||
| `salaryMax` | INT | 最高薪资(元) | 待添加 |
|
||||
| `experienceMin` | INT | 最低工作年限 | 待添加 |
|
||||
| `experienceMax` | INT | 最高工作年限 | 待添加 |
|
||||
| `educationLevel` | VARCHAR | 学历等级 | 待添加 |
|
||||
| `matchScore` | DECIMAL | 匹配度评分 | 待添加 |
|
||||
| 字段名 | 类型 | 说明 | 状态 | 数据来源 |
|
||||
|--------|------|------|------|----------|
|
||||
| `city` | VARCHAR | 城市代码 | 待添加 | `job.city` |
|
||||
| `cityName` | VARCHAR | 城市名称 | 待添加 | `job.cityName` |
|
||||
| `areaDistrict` | VARCHAR | 区域 | 待添加 | `job.areaDistrict` |
|
||||
| `businessDistrict` | VARCHAR | 商圈 | 待添加 | `job.businessDistrict` |
|
||||
| `salaryMin` | INT | 最低薪资(元) | 待添加 | 从 `salaryDesc` 解析 |
|
||||
| `salaryMax` | INT | 最高薪资(元) | 待添加 | 从 `salaryDesc` 解析 |
|
||||
| `experienceMin` | INT | 最低工作年限 | 待添加 | 从 `jobExperience` 解析 |
|
||||
| `experienceMax` | INT | 最高工作年限 | 待添加 | 从 `jobExperience` 解析 |
|
||||
| `educationLevel` | VARCHAR | 学历等级 | 待添加 | 从 `jobDegree` 映射 |
|
||||
| `matchScore` | DECIMAL | 匹配度评分 | 待添加 | 计算得出 |
|
||||
| `encryptBossId` | VARCHAR | Boss ID | 已有 | `job.encryptBossId` |
|
||||
| `securityId` | VARCHAR | 安全ID | 待添加 | `job.securityId`(投递必需) |
|
||||
| `bossName` | VARCHAR | Boss姓名 | 待添加 | `job.bossName` |
|
||||
| `bossTitle` | VARCHAR | Boss职位 | 待添加 | `job.bossTitle` |
|
||||
| `bossOnline` | TINYINT | Boss是否在线 | 待添加 | `job.bossOnline` |
|
||||
| `brandStage` | VARCHAR | 融资阶段 | 待添加 | `job.brandStageName` |
|
||||
| `jobLabels` | JSON | 职位标签 | 待添加 | `job.jobLabels` |
|
||||
| `skills` | JSON | 技能要求 | 待添加 | `job.skills` |
|
||||
| `welfareList` | JSON | 福利列表 | 待添加 | `job.welfareList` |
|
||||
| `isOutsourcing` | TINYINT | 是否外包 | 待添加 | `job.proxyJob` |
|
||||
| `industry` | INT | 行业代码 | 待添加 | `job.industry` |
|
||||
|
||||
### pla_account 表需要添加的字段
|
||||
|
||||
@@ -483,15 +658,14 @@ async createDeliverTask(params) {
|
||||
| 任务 | 预计时间 | 优先级 |
|
||||
|------|----------|--------|
|
||||
| 任务1: 完善搜索参数支持 | 2小时 | 高 |
|
||||
| 任务2: 优化职位数据保存 | 3小时 | 高 |
|
||||
| 任务2: 优化职位数据保存 | 4小时 | 高 |
|
||||
| 任务3: 完善自动投递任务搜索条件 | 2小时 | 高 |
|
||||
| 任务4: 优化职位匹配算法 | 4小时 | 高 |
|
||||
| 任务5: 创建搜索任务接口 | 3小时 | 中 |
|
||||
| 任务6: 创建投递任务接口 | 4小时 | 中 |
|
||||
| 任务7: 完善指令类型映射 | 1小时 | 中 |
|
||||
| 任务8: 添加搜索条件配置管理 | 1小时 | 低 |
|
||||
| 任务5: 创建搜索任务接口(支持可选投递) | 5小时 | 高 |
|
||||
| 任务6: 完善指令类型映射 | 1小时 | 中 |
|
||||
| 任务7: 添加搜索条件配置管理 | 1小时 | 低 |
|
||||
|
||||
**总计**: 约20小时
|
||||
**总计**: 约19小时
|
||||
|
||||
## 🚀 开发优先级
|
||||
|
||||
@@ -500,14 +674,70 @@ async createDeliverTask(params) {
|
||||
2. 任务2: 优化职位数据保存
|
||||
3. 任务3: 完善自动投递任务搜索条件
|
||||
4. 任务4: 优化职位匹配算法
|
||||
5. 任务5: 创建搜索任务接口(支持可选投递)
|
||||
|
||||
### 第二阶段(接口完善)
|
||||
5. 任务5: 创建搜索任务接口
|
||||
6. 任务6: 创建投递任务接口
|
||||
7. 任务7: 完善指令类型映射
|
||||
6. 任务6: 完善指令类型映射
|
||||
|
||||
### 第三阶段(配置管理)
|
||||
8. 任务8: 添加搜索条件配置管理
|
||||
7. 任务7: 添加搜索条件配置管理
|
||||
|
||||
## 💡 使用场景说明
|
||||
|
||||
### 场景1: 仅搜索职位列表
|
||||
```javascript
|
||||
// 只搜索职位,不投递
|
||||
const result = await plaAccountService.createSearchJobListTask({
|
||||
id: accountId,
|
||||
keyword: '全栈工程师',
|
||||
searchParams: {
|
||||
city: '101020100',
|
||||
cityName: '上海',
|
||||
salary: '20-30K',
|
||||
experience: '3-5年',
|
||||
education: '本科'
|
||||
},
|
||||
pageCount: 3,
|
||||
autoDeliver: false // 不自动投递
|
||||
});
|
||||
|
||||
// 返回: { taskId: 123, message: '搜索任务已创建', jobCount: 45 }
|
||||
// 职位会自动保存到数据库,状态为 'pending'(待投递)
|
||||
```
|
||||
|
||||
### 场景2: 搜索并自动投递(推荐)
|
||||
```javascript
|
||||
// 搜索职位并自动投递匹配的职位
|
||||
const result = await plaAccountService.createSearchJobListTask({
|
||||
id: accountId,
|
||||
keyword: '全栈工程师',
|
||||
searchParams: {
|
||||
city: '101020100',
|
||||
cityName: '上海',
|
||||
salary: '20-30K',
|
||||
experience: '3-5年',
|
||||
education: '本科'
|
||||
},
|
||||
pageCount: 3,
|
||||
autoDeliver: true, // 自动投递
|
||||
filterRules: {
|
||||
minSalary: 20000,
|
||||
maxSalary: 30000,
|
||||
keywords: ['Vue', 'React'],
|
||||
excludeKeywords: ['外包', '外派']
|
||||
},
|
||||
maxCount: 10 // 最多投递10个职位
|
||||
});
|
||||
|
||||
// 返回: { taskId: 123, message: '搜索并投递任务已创建', jobCount: 45, deliveredCount: 8 }
|
||||
```
|
||||
|
||||
**重要说明**:
|
||||
- **投递必须在搜索完成后立即执行**,因为 `securityId` 等字段可能有时效性
|
||||
- 前端页面变化后,已保存的职位信息中的 `securityId` 可能失效,无法用于投递
|
||||
- 因此不支持从已保存的职位中选择投递,必须在搜索后立即投递
|
||||
- 如果只需要搜索不投递,设置 `autoDeliver: false`
|
||||
- 如果需要搜索并投递,设置 `autoDeliver: true`,系统会根据匹配规则自动投递
|
||||
|
||||
## 📌 注意事项
|
||||
|
||||
@@ -517,6 +747,11 @@ async createDeliverTask(params) {
|
||||
4. **性能优化**: 批量操作需要考虑性能,避免阻塞
|
||||
5. **MQTT通信**: 确保指令参数格式正确,与客户端协议一致
|
||||
6. **数据库事务**: 批量操作需要使用事务保证数据一致性
|
||||
7. **投递时机**: 投递必须在搜索完成后立即执行,因为 `securityId` 等字段可能有时效性,前端页面变化后这些字段可能失效
|
||||
8. **职位状态验证**: 投递前必须验证职位状态(applyStatus = 'pending'),避免重复投递
|
||||
9. **投递必需字段**: 投递时需要 `encryptJobId`、`encryptBossId` 和 `securityId`,这些字段必须从最新搜索结果中获取
|
||||
10. **位置信息**: 优先使用响应中的 `gps` 字段,避免不必要的API调用
|
||||
11. **接口设计**: 搜索和投递在同一接口中完成,不支持单独的投递接口,因为已保存的职位信息可能已过期
|
||||
|
||||
## 🔗 相关文件
|
||||
|
||||
|
||||
@@ -143,6 +143,256 @@ class JobManager {
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* 多条件搜索职位列表(新指令)
|
||||
* @param {string} sn_code - 设备SN码
|
||||
* @param {object} mqttClient - MQTT客户端
|
||||
* @param {object} params - 搜索参数
|
||||
* @returns {Promise<object>} 搜索结果
|
||||
*/
|
||||
async search_jobs_with_params(sn_code, mqttClient, params = {}) {
|
||||
const {
|
||||
keyword = '前端',
|
||||
platform = 'boss',
|
||||
city = '',
|
||||
cityName = '',
|
||||
salary = '',
|
||||
experience = '',
|
||||
education = '',
|
||||
industry = '',
|
||||
companySize = '',
|
||||
financingStage = '',
|
||||
page = 1,
|
||||
pageSize = 20,
|
||||
pageCount = 3
|
||||
} = params;
|
||||
|
||||
console.log(`[工作管理] 开始多条件搜索设备 ${sn_code} 的职位,关键词: ${keyword}, 城市: ${cityName || city}`);
|
||||
|
||||
// 构建完整的搜索参数对象
|
||||
const searchData = {
|
||||
keyword,
|
||||
pageCount
|
||||
};
|
||||
|
||||
// 添加可选搜索条件
|
||||
if (city) searchData.city = city;
|
||||
if (cityName) searchData.cityName = cityName;
|
||||
if (salary) searchData.salary = salary;
|
||||
if (experience) searchData.experience = experience;
|
||||
if (education) searchData.education = education;
|
||||
if (industry) searchData.industry = industry;
|
||||
if (companySize) searchData.companySize = companySize;
|
||||
if (financingStage) searchData.financingStage = financingStage;
|
||||
if (page) searchData.page = page;
|
||||
if (pageSize) searchData.pageSize = pageSize;
|
||||
|
||||
// 通过MQTT指令获取岗位列表(使用新的action)
|
||||
const response = await mqttClient.publishAndWait(sn_code, {
|
||||
platform,
|
||||
action: "search_job_list", // 新的搜索action
|
||||
data: searchData
|
||||
});
|
||||
|
||||
if (!response || response.code !== 200) {
|
||||
console.error(`[工作管理] 多条件搜索职位失败:`, response);
|
||||
throw new Error('多条件搜索职位失败');
|
||||
}
|
||||
|
||||
// 处理职位列表数据
|
||||
let jobs = [];
|
||||
if (Array.isArray(response.data)) {
|
||||
for (const item of response.data) {
|
||||
if (item.data?.zpData?.jobList && Array.isArray(item.data.zpData.jobList)) {
|
||||
jobs = jobs.concat(item.data.zpData.jobList);
|
||||
}
|
||||
}
|
||||
} else if (response.data?.data?.zpData?.jobList) {
|
||||
jobs = response.data.data.zpData.jobList || [];
|
||||
} else if (response.data?.zpData?.jobList) {
|
||||
jobs = response.data.zpData.jobList || [];
|
||||
}
|
||||
|
||||
console.log(`[工作管理] 成功获取岗位数据,共 ${jobs.length} 个岗位`);
|
||||
|
||||
// 保存职位到数据库
|
||||
try {
|
||||
await this.saveJobsToDatabase(sn_code, platform, keyword, jobs);
|
||||
} catch (error) {
|
||||
console.error(`[工作管理] 保存职位到数据库失败:`, error);
|
||||
}
|
||||
|
||||
return {
|
||||
jobs: jobs,
|
||||
keyword: keyword,
|
||||
platform: platform,
|
||||
count: jobs.length
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 搜索并投递职位(新指令)
|
||||
* @param {string} sn_code - 设备SN码
|
||||
* @param {object} mqttClient - MQTT客户端
|
||||
* @param {object} params - 参数
|
||||
* @returns {Promise<object>} 执行结果
|
||||
*/
|
||||
async search_and_deliver(sn_code, mqttClient, params = {}) {
|
||||
const {
|
||||
keyword,
|
||||
searchParams = {},
|
||||
pageCount = 3,
|
||||
filterRules = {},
|
||||
maxCount = 10,
|
||||
platform = 'boss'
|
||||
} = params;
|
||||
|
||||
console.log(`[工作管理] 开始搜索并投递职位,设备: ${sn_code}, 关键词: ${keyword}`);
|
||||
|
||||
// 1. 先执行搜索
|
||||
const searchResult = await this.search_jobs_with_params(sn_code, mqttClient, {
|
||||
keyword,
|
||||
platform,
|
||||
...searchParams,
|
||||
pageCount
|
||||
});
|
||||
|
||||
if (!searchResult || searchResult.count === 0) {
|
||||
return {
|
||||
success: true,
|
||||
jobCount: 0,
|
||||
deliveredCount: 0,
|
||||
message: '未找到职位'
|
||||
};
|
||||
}
|
||||
|
||||
// 2. 等待数据保存完成
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
// 3. 从数据库获取刚搜索到的职位
|
||||
const job_postings = db.getModel('job_postings');
|
||||
const searchedJobs = await job_postings.findAll({
|
||||
where: {
|
||||
sn_code: sn_code,
|
||||
platform: platform,
|
||||
applyStatus: 'pending',
|
||||
keyword: keyword
|
||||
},
|
||||
order: [['create_time', 'DESC']],
|
||||
limit: 1000
|
||||
});
|
||||
|
||||
if (searchedJobs.length === 0) {
|
||||
return {
|
||||
success: true,
|
||||
jobCount: searchResult.count,
|
||||
deliveredCount: 0,
|
||||
message: '未找到待投递的职位'
|
||||
};
|
||||
}
|
||||
|
||||
// 4. 获取简历信息用于匹配
|
||||
const resume_info = db.getModel('resume_info');
|
||||
const resume = await resume_info.findOne({
|
||||
where: {
|
||||
sn_code: sn_code,
|
||||
platform: platform,
|
||||
isActive: true
|
||||
},
|
||||
order: [['last_modify_time', 'DESC']]
|
||||
});
|
||||
|
||||
if (!resume) {
|
||||
return {
|
||||
success: true,
|
||||
jobCount: searchResult.count,
|
||||
deliveredCount: 0,
|
||||
message: '未找到活跃简历,无法投递'
|
||||
};
|
||||
}
|
||||
|
||||
// 5. 获取账号配置
|
||||
const pla_account = db.getModel('pla_account');
|
||||
const account = await pla_account.findOne({
|
||||
where: { sn_code, platform_type: platform }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new Error('账号不存在');
|
||||
}
|
||||
|
||||
const accountConfig = account.toJSON();
|
||||
const resumeData = resume.toJSON();
|
||||
|
||||
// 6. 使用过滤方法进行职位匹配
|
||||
const matchedJobs = await this.filter_jobs_by_rules(searchedJobs, {
|
||||
minSalary: filterRules.minSalary || 0,
|
||||
maxSalary: filterRules.maxSalary || 0,
|
||||
keywords: filterRules.keywords || [],
|
||||
excludeKeywords: filterRules.excludeKeywords || [],
|
||||
accountConfig: accountConfig,
|
||||
resumeInfo: resumeData
|
||||
});
|
||||
|
||||
// 7. 限制投递数量
|
||||
const jobsToDeliver = matchedJobs.slice(0, maxCount);
|
||||
console.log(`[工作管理] 匹配到 ${matchedJobs.length} 个职位,将投递 ${jobsToDeliver.length} 个`);
|
||||
|
||||
// 8. 执行投递
|
||||
let deliveredCount = 0;
|
||||
const apply_records = db.getModel('apply_records');
|
||||
|
||||
for (let i = 0; i < jobsToDeliver.length; i++) {
|
||||
const job = jobsToDeliver[i];
|
||||
const jobData = job.toJSON ? job.toJSON() : job;
|
||||
|
||||
try {
|
||||
// 从原始数据中获取 securityId
|
||||
let securityId = jobData.securityId || '';
|
||||
try {
|
||||
if (jobData.originalData) {
|
||||
const originalData = typeof jobData.originalData === 'string'
|
||||
? JSON.parse(jobData.originalData)
|
||||
: jobData.originalData;
|
||||
securityId = originalData.securityId || securityId;
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(`[工作管理] 解析职位原始数据失败:`, e);
|
||||
}
|
||||
|
||||
// 执行投递
|
||||
const deliverResult = await this.applyJob(sn_code, mqttClient, {
|
||||
jobId: jobData.jobId,
|
||||
encryptBossId: jobData.encryptBossId || '',
|
||||
securityId: securityId,
|
||||
brandName: jobData.companyName || '',
|
||||
jobTitle: jobData.jobTitle || '',
|
||||
companyName: jobData.companyName || '',
|
||||
platform: platform
|
||||
});
|
||||
|
||||
if (deliverResult && deliverResult.success) {
|
||||
deliveredCount++;
|
||||
}
|
||||
|
||||
// 投递间隔控制
|
||||
if (i < jobsToDeliver.length - 1) {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[工作管理] 投递职位失败:`, error);
|
||||
// 继续投递下一个职位
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
jobCount: searchResult.count,
|
||||
deliveredCount: deliveredCount,
|
||||
message: `搜索完成,找到 ${searchResult.count} 个职位,成功投递 ${deliveredCount} 个`
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取岗位列表
|
||||
* @param {string} sn_code - 设备SN码
|
||||
|
||||
@@ -24,6 +24,11 @@ class TaskHandlers {
|
||||
return await this.handleAutoDeliverTask(task);
|
||||
});
|
||||
|
||||
// 搜索职位列表任务(新功能)
|
||||
taskQueue.registerHandler('search_jobs', async (task) => {
|
||||
return await this.handleSearchJobListTask(task);
|
||||
});
|
||||
|
||||
// 自动沟通任务(待实现)
|
||||
taskQueue.registerHandler('auto_chat', async (task) => {
|
||||
return await this.handleAutoChatTask(task);
|
||||
@@ -547,6 +552,180 @@ class TaskHandlers {
|
||||
return { allowed: true, reason: '在允许的时间范围内' };
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理搜索职位列表任务(新功能)
|
||||
* 支持多条件搜索和可选投递
|
||||
* @param {object} task - 任务对象
|
||||
* @returns {Promise<object>} 执行结果
|
||||
*/
|
||||
async handleSearchJobListTask(task) {
|
||||
const { sn_code, taskParams } = task;
|
||||
const {
|
||||
keyword,
|
||||
searchParams = {},
|
||||
pageCount = 3,
|
||||
autoDeliver = false,
|
||||
filterRules = {},
|
||||
maxCount = 10
|
||||
} = taskParams;
|
||||
|
||||
console.log(`[任务处理器] 搜索职位列表任务 - 设备: ${sn_code}, 关键词: ${keyword}, 自动投递: ${autoDeliver}`);
|
||||
|
||||
// 检查授权状态
|
||||
const authorizationService = require('../../services/authorization_service');
|
||||
const authCheck = await authorizationService.checkAuthorization(sn_code, 'sn_code');
|
||||
if (!authCheck.is_authorized) {
|
||||
console.log(`[任务处理器] 搜索职位列表任务 - 设备: ${sn_code} 授权检查失败: ${authCheck.message}`);
|
||||
return {
|
||||
success: false,
|
||||
jobCount: 0,
|
||||
deliveredCount: 0,
|
||||
message: authCheck.message
|
||||
};
|
||||
}
|
||||
|
||||
deviceManager.recordTaskStart(sn_code, task);
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const job_postings = db.getModel('job_postings');
|
||||
const pla_account = db.getModel('pla_account');
|
||||
const resume_info = db.getModel('resume_info');
|
||||
const apply_records = db.getModel('apply_records');
|
||||
const Sequelize = require('sequelize');
|
||||
|
||||
// 1. 获取账号配置
|
||||
const account = await pla_account.findOne({
|
||||
where: { sn_code, platform_type: taskParams.platform || 'boss' }
|
||||
});
|
||||
|
||||
if (!account) {
|
||||
throw new Error('账号不存在');
|
||||
}
|
||||
|
||||
const accountConfig = account.toJSON();
|
||||
|
||||
// 2. 从账号配置中读取搜索条件
|
||||
const searchConfig = accountConfig.search_config
|
||||
? (typeof accountConfig.search_config === 'string'
|
||||
? JSON.parse(accountConfig.search_config)
|
||||
: accountConfig.search_config)
|
||||
: {};
|
||||
|
||||
// 3. 构建完整的搜索参数(任务参数优先,其次账号配置)
|
||||
const searchCommandParams = {
|
||||
sn_code: sn_code,
|
||||
platform: taskParams.platform || accountConfig.platform_type || 'boss',
|
||||
keyword: keyword || accountConfig.keyword || searchConfig.keyword || '',
|
||||
city: searchParams.city || accountConfig.city || searchConfig.city || '',
|
||||
cityName: searchParams.cityName || accountConfig.cityName || searchConfig.cityName || '',
|
||||
salary: searchParams.salary || searchConfig.defaultSalary || '',
|
||||
experience: searchParams.experience || searchConfig.defaultExperience || '',
|
||||
education: searchParams.education || searchConfig.defaultEducation || '',
|
||||
industry: searchParams.industry || searchConfig.industry || '',
|
||||
companySize: searchParams.companySize || searchConfig.companySize || '',
|
||||
financingStage: searchParams.financingStage || searchConfig.financingStage || '',
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageCount: pageCount
|
||||
};
|
||||
|
||||
// 4. 根据是否投递选择不同的指令
|
||||
let searchCommand;
|
||||
if (autoDeliver) {
|
||||
// 使用搜索并投递指令
|
||||
searchCommand = {
|
||||
command_type: 'search_and_deliver',
|
||||
command_name: '搜索并投递职位',
|
||||
command_params: JSON.stringify({
|
||||
keyword: searchCommandParams.keyword,
|
||||
searchParams: {
|
||||
city: searchCommandParams.city,
|
||||
cityName: searchCommandParams.cityName,
|
||||
salary: searchCommandParams.salary,
|
||||
experience: searchCommandParams.experience,
|
||||
education: searchCommandParams.education,
|
||||
industry: searchCommandParams.industry,
|
||||
companySize: searchCommandParams.companySize,
|
||||
financingStage: searchCommandParams.financingStage,
|
||||
page: searchCommandParams.page,
|
||||
pageSize: searchCommandParams.pageSize,
|
||||
pageCount: searchCommandParams.pageCount
|
||||
},
|
||||
filterRules: filterRules,
|
||||
maxCount: maxCount,
|
||||
platform: searchCommandParams.platform
|
||||
}),
|
||||
priority: config.getTaskPriority('search_and_deliver') || 5,
|
||||
sequence: 1
|
||||
};
|
||||
} else {
|
||||
// 使用多条件搜索指令
|
||||
searchCommand = {
|
||||
command_type: 'search_jobs_with_params',
|
||||
command_name: '多条件搜索职位列表',
|
||||
command_params: JSON.stringify(searchCommandParams),
|
||||
priority: config.getTaskPriority('search_jobs_with_params') || 5,
|
||||
sequence: 1
|
||||
};
|
||||
}
|
||||
|
||||
// 5. 执行指令
|
||||
const commandResult = await command.executeCommands(task.id, [searchCommand], this.mqttClient);
|
||||
|
||||
// 6. 处理执行结果
|
||||
let jobCount = 0;
|
||||
let deliveredCount = 0;
|
||||
|
||||
if (autoDeliver) {
|
||||
// 如果使用 search_and_deliver 指令,结果中已包含投递信息
|
||||
if (commandResult && commandResult.results && commandResult.results.length > 0) {
|
||||
const result = commandResult.results[0].result;
|
||||
if (result) {
|
||||
jobCount = result.jobCount || 0;
|
||||
deliveredCount = result.deliveredCount || 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 如果使用 search_jobs_with_params 指令,等待搜索完成并从数据库获取结果
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
const searchedJobs = await job_postings.findAll({
|
||||
where: {
|
||||
sn_code: sn_code,
|
||||
platform: searchCommandParams.platform,
|
||||
applyStatus: 'pending',
|
||||
keyword: searchCommandParams.keyword
|
||||
},
|
||||
order: [['create_time', 'DESC']],
|
||||
limit: 1000
|
||||
});
|
||||
|
||||
jobCount = searchedJobs.length;
|
||||
}
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
deviceManager.recordTaskComplete(sn_code, task, true, duration);
|
||||
|
||||
console.log(`[任务处理器] 搜索职位列表任务完成 - 设备: ${sn_code}, 找到 ${jobCount} 个职位, 投递 ${deliveredCount} 个, 耗时: ${duration}ms`);
|
||||
|
||||
return {
|
||||
success: true,
|
||||
jobCount: jobCount,
|
||||
deliveredCount: deliveredCount,
|
||||
message: autoDeliver
|
||||
? `搜索完成,找到 ${jobCount} 个职位,成功投递 ${deliveredCount} 个`
|
||||
: `搜索完成,找到 ${jobCount} 个职位`
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
deviceManager.recordTaskComplete(sn_code, task, false, duration);
|
||||
console.error(`[任务处理器] 搜索职位列表任务失败 - 设备: ${sn_code}:`, error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理自动沟通任务(待实现)
|
||||
* 功能:自动与HR进行沟通,回复消息等
|
||||
|
||||
@@ -680,13 +680,120 @@ class PlaAccountService {
|
||||
}
|
||||
});
|
||||
|
||||
const taskId = await scheduleManager.taskQueue.addTask(account.sn_code, {
|
||||
taskType: taskType,
|
||||
taskName: `手动任务 - ${taskName}`,
|
||||
taskParams: {
|
||||
keyword: account.keyword,
|
||||
platform: account.platform_type
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
message: '任务已添加到队列',
|
||||
taskId: task.id
|
||||
taskId: taskId
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建搜索职位列表任务(支持可选投递)
|
||||
* @param {Object} params - 任务参数
|
||||
* @param {number} params.id - 账号ID
|
||||
* @param {string} params.keyword - 搜索关键词
|
||||
* @param {Object} params.searchParams - 搜索条件(城市、薪资、经验、学历等)
|
||||
* @param {number} params.pageCount - 获取页数
|
||||
* @param {boolean} params.autoDeliver - 是否自动投递(默认false)
|
||||
* @param {Object} params.filterRules - 过滤规则(autoDeliver=true时使用)
|
||||
* @param {number} params.maxCount - 最大投递数量(autoDeliver=true时使用)
|
||||
* @returns {Promise<Object>} 任务创建结果 { taskId, message, jobCount, deliveredCount }
|
||||
*/
|
||||
async createSearchJobListTask(params) {
|
||||
const pla_account = db.getModel('pla_account');
|
||||
const task_status = db.getModel('task_status');
|
||||
|
||||
const {
|
||||
id,
|
||||
keyword,
|
||||
searchParams = {},
|
||||
pageCount = 3,
|
||||
autoDeliver = false,
|
||||
filterRules = {},
|
||||
maxCount = 10
|
||||
} = params;
|
||||
|
||||
// 1. 验证账号和授权
|
||||
if (!id) {
|
||||
throw new Error('账号ID不能为空');
|
||||
}
|
||||
|
||||
const account = await pla_account.findByPk(id);
|
||||
if (!account) {
|
||||
throw new Error('账号不存在');
|
||||
}
|
||||
|
||||
// 检查账号是否启用
|
||||
if (!account.is_enabled) {
|
||||
throw new Error('账号未启用,无法执行任务');
|
||||
}
|
||||
|
||||
// 检查授权状态
|
||||
const authCheck = await authorizationService.checkAuthorization(id, 'id');
|
||||
if (!authCheck.is_authorized) {
|
||||
throw new Error(authCheck.message);
|
||||
}
|
||||
|
||||
// 检查MQTT客户端
|
||||
if (!scheduleManager.mqttClient) {
|
||||
throw new Error('MQTT客户端未初始化');
|
||||
}
|
||||
|
||||
const sn_code = account.sn_code;
|
||||
const platform = account.platform_type || 'boss';
|
||||
|
||||
// 2. 创建任务记录(使用新的搜索任务类型)
|
||||
const taskType = 'search_jobs';
|
||||
const taskName = autoDeliver ? '搜索并投递职位' : '搜索职位列表';
|
||||
|
||||
const task = await task_status.create({
|
||||
sn_code: sn_code,
|
||||
taskType: taskType,
|
||||
taskName: taskName,
|
||||
taskParams: JSON.stringify({
|
||||
keyword: keyword || account.keyword || '',
|
||||
searchParams: searchParams,
|
||||
pageCount: pageCount,
|
||||
autoDeliver: autoDeliver,
|
||||
filterRules: filterRules,
|
||||
maxCount: maxCount,
|
||||
platform: platform
|
||||
}),
|
||||
status: 'pending',
|
||||
progress: 0
|
||||
});
|
||||
|
||||
console.log(`[账号服务] 创建搜索任务: ${taskName} (ID: ${task.id}, 设备: ${sn_code})`);
|
||||
|
||||
// 3. 将任务添加到队列,由 handleSearchJobListTask 处理
|
||||
const taskId = await scheduleManager.taskQueue.addTask(sn_code, {
|
||||
taskType: taskType,
|
||||
taskName: taskName,
|
||||
taskParams: {
|
||||
keyword: keyword || account.keyword || '',
|
||||
searchParams: searchParams,
|
||||
pageCount: pageCount,
|
||||
autoDeliver: autoDeliver,
|
||||
filterRules: filterRules,
|
||||
maxCount: maxCount,
|
||||
platform: platform
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
taskId: taskId,
|
||||
message: `搜索任务已创建,任务ID: ${taskId}`,
|
||||
jobCount: 0,
|
||||
deliveredCount: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user