This commit is contained in:
张成
2025-12-29 18:35:57 +08:00
parent 2786202212
commit 8fa06435a9
4 changed files with 864 additions and 93 deletions

View File

@@ -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. 更新任务状态为完成
8. 如果 autoDeliver=true继续执行投递流程:
8.1 从数据库获取刚搜索到的职位列表
- 筛选条件: applyStatus = 'pending', sn_code = 账号SN码
8.2 根据简历信息和过滤规则匹配职位
- 距离匹配(基于经纬度)
- 薪资匹配解析salaryDesc
- 工作年限匹配解析jobExperience
- 学历匹配解析jobDegree
- 权重评分
8.3 为每个匹配的职位生成投递指令
- command_type: 'apply_job'
- command_params: {
jobId: job.encryptJobId, // 职位ID必需
encryptBossId: job.encryptBossId, // Boss ID必需
securityId: job.securityId, // 安全ID必需从最新搜索结果获取
brandName: job.brandName, // 公司名称(可选)
jobTitle: job.jobName // 职位名称(可选)
}
8.4 批量执行投递指令(带间隔控制,避免频繁投递)
8.5 保存投递记录 (apply_records)
8.6 更新职位状态 (job_postings.applyStatus = 'applied')
9. 更新任务状态为完成
10. 返回任务信息(包含搜索到的职位数量和投递数量)
```
### 投递职位流程
```
1. 用户/系统调用 createDeliverTask()
2. 创建任务记录 (task_status)
3. 生成搜索指令 (获取职位列表)
- command_type: 'get_job_list'
4. 执行搜索指令
5. 获取职位列表并保存到数据库
6. 根据简历信息和过滤规则匹配职位
- 距离匹配
- 薪资匹配
- 工作年限匹配
- 学历匹配
- 权重评分
7. 为每个匹配的职位生成投递指令
- command_type: 'apply_job'
- command_params: { jobId, encryptBossId, ... }
8. 批量执行投递指令(带间隔控制)
9. 保存投递记录 (apply_records)
10. 更新职位状态 (job_postings.applyStatus)
11. 更新任务状态为完成
```
**说明**:
- 此接口支持两种模式:
- `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. **接口设计**: 搜索和投递在同一接口中完成,不支持单独的投递接口,因为已保存的职位信息可能已过期
## 🔗 相关文件