新增批量

This commit is contained in:
杨志
2026-01-06 11:23:52 +08:00
parent e41dd33d23
commit cb7ad9a4b7
15 changed files with 1371 additions and 75 deletions

View File

@@ -3,7 +3,7 @@
## 接口地址
```
POST /match/calculate
POST http://work.dhdjy.com/match/calculate
```
## 接口说明

View File

@@ -0,0 +1,243 @@
# 快速匹配实现原理分析
## 为什么其他机构能"秒级"返回匹配结果?
### 核心原理:数据库层面快速过滤 + 只计算符合条件的岗位
## 实现方式对比
### ❌ 当前方案(慢的原因)
```
1. 接收1万个岗位的完整数据JSON数组
2. 在内存中遍历所有岗位
3. 对每个岗位进行完整匹配计算
4. 排序返回
```
**问题**
- 需要传输1万个岗位的完整数据几十MB
- 需要计算所有岗位(即使明显不符合)
- 无法利用数据库索引优化
### ✅ 优化方案(快速实现)
```
1. 岗位数据存储在数据库中
2. 使用SQL WHERE条件快速过滤
3. 只对通过初步筛选的岗位进行详细计算
4. 排序返回
```
**优势**
- 利用数据库索引,查询速度极快(毫秒级)
- 大幅减少需要计算的岗位数量从1万降到几百
- 只传输少量数据
## 具体实现步骤
### 第一步:数据库表结构设计
```sql
-- 岗位表
CREATE TABLE positions (
id INT PRIMARY KEY,
-- 基础信息
position_name VARCHAR(255),
company_name VARCHAR(255),
-- 硬性条件(建立索引)
education_require VARCHAR(50), -- 学历要求:本科、硕士等
degree_require VARCHAR(50), -- 学位要求:学士、硕士等
age_min INT, -- 最小年龄
age_max INT, -- 最大年龄
gender_require VARCHAR(10), -- 性别要求:男、女、不限制
major_require TEXT, -- 专业要求
-- 其他信息
position_require JSON, -- 完整要求JSON格式
created_at TIMESTAMP
);
-- 建立索引
CREATE INDEX idx_education ON positions(education_require);
CREATE INDEX idx_age ON positions(age_min, age_max);
CREATE INDEX idx_gender ON positions(gender_require);
```
### 第二步快速过滤SQL查询
```php
// 根据简历信息,快速过滤岗位
$resume = [
'birth_date' => '1995-03-01', // 计算年龄30岁
'gender' => '男',
'education_level' => '硕士研究生',
'degree' => '硕士',
'major' => '计算机科学与技术'
];
// 计算年龄
$age = calculateAge($resume['birth_date']); // 30岁
// SQL快速过滤利用索引毫秒级响应
$positions = DB::table('positions')
->where(function($query) use ($resume, $age) {
// 学历要求:简历学历 >= 岗位要求
$query->whereIn('education_require', [
'本科', '本科及以上', '硕士', '硕士及以上'
]);
// 年龄要求:在范围内
$query->where('age_min', '<=', $age)
->where('age_max', '>=', $age);
// 性别要求:不限制 或 匹配
$query->where(function($q) use ($resume) {
$q->where('gender_require', '不限制')
->orWhere('gender_require', $resume['gender']);
});
// 专业要求:模糊匹配(或使用专业分类表)
$query->where('major_require', 'like', '%计算机%')
->orWhere('major_require', 'like', '%软件%');
})
->get(); // 可能从1万个筛选到200个
// 只对这200个岗位进行详细匹配计算
foreach ($positions as $position) {
$score = calculateMatchScore($position, $resume);
}
```
### 第三步:性能对比
| 方案 | 需要计算的岗位数 | 计算时间 | 数据库查询时间 |
|------|----------------|---------|---------------|
| **当前方案** | 10,000个 | 10-50秒 | 0秒无数据库 |
| **优化方案** | 200个过滤后 | 0.2-1秒 | 0.01-0.1秒 |
**总时间对比**
- 当前方案10-50秒
- 优化方案0.21-1.1秒(**快50-200倍**
## 关键技术点
### 1. 数据库索引优化
```sql
-- 对常用查询字段建立索引
CREATE INDEX idx_education_age ON positions(education_require, age_min, age_max);
CREATE INDEX idx_gender ON positions(gender_require);
```
### 2. 数据预处理
```php
// 岗位入库时,解析并存储结构化数据
$position = [
'position_require' => [
'学历要求' => '本科及以上',
'年龄要求' => '18周岁以上、35周岁以下',
// ...
]
];
// 解析并存储到独立字段
$position['education_require'] = '本科'; // 标准化
$position['age_min'] = 18;
$position['age_max'] = 35;
```
### 3. 专业匹配优化
```sql
-- 方案A使用专业分类表
CREATE TABLE major_categories (
major_name VARCHAR(100),
category VARCHAR(100),
INDEX idx_category(category)
);
-- 方案B使用全文索引
ALTER TABLE positions ADD FULLTEXT INDEX idx_major(major_require);
```
### 4. 缓存机制
```php
// 缓存常见简历的匹配结果
$cacheKey = "match:resume:{$resumeId}";
$result = Redis::get($cacheKey);
if (!$result) {
// 计算并缓存
$result = calculateMatch($positions, $resume);
Redis::setex($cacheKey, 3600, $result); // 缓存1小时
}
```
## 完整实现流程
```php
public function fastBatchMatch($resume, $page = 1, $pageSize = 20) {
// 1. 解析简历信息
$age = calculateAge($resume['birth_date']);
$education = parseEducation($resume['education']);
// 2. 数据库快速过滤(毫秒级)
$filteredPositions = DB::table('positions')
->where('education_require', '<=', $education['level'])
->where('age_min', '<=', $age)
->where('age_max', '>=', $age)
->where(function($q) use ($resume) {
$q->where('gender_require', '不限制')
->orWhere('gender_require', $resume['gender']);
})
->get(); // 从1万筛选到几百个
// 3. 只计算通过初步筛选的岗位(秒级)
$results = [];
foreach ($filteredPositions as $position) {
$score = $this->calculateMatchScore($position, $resume);
$results[] = [
'position_id' => $position->id,
'match_score' => $score,
'position' => $position
];
}
// 4. 排序+分页
usort($results, fn($a, $b) => $b['match_score'] - $a['match_score']);
$paginated = array_slice($results, ($page - 1) * $pageSize, $pageSize);
return [
'list' => $paginated,
'total' => count($results),
'page' => $page,
'page_size' => $pageSize
];
}
```
## 总结
**其他机构能快速返回的关键**
1.**岗位存储在数据库**不是JSON数组
2.**使用SQL WHERE快速过滤**(利用索引,毫秒级)
3.**只计算通过初步筛选的岗位**从1万降到几百
4.**数据预处理**(结构化存储硬性条件)
5.**索引优化**(对常用查询字段建索引)
**性能提升**
- 数据库过滤1万个 → 200个减少98%
- 计算时间50秒 → 1秒快50倍
- **总响应时间从50秒降到1秒以内**
## 实施建议
1. **如果岗位数据在数据库**直接使用SQL过滤
2. **如果岗位数据是JSON**:考虑导入数据库或建立索引
3. **如果无法改数据库**使用内存索引如Elasticsearch或预计算

View File

@@ -0,0 +1,161 @@
# 批量匹配查询接口说明
## 接口地址
```
POST /match/batch
```
## 接口说明
该接口基于数据库实现快速批量匹配查询传入用户ID自动从数据库读取用户简历信息和所有岗位进行匹配计算并按匹配度排序返回。
## 请求参数
### 请求方式
- **方法**: POST
- **Content-Type**: application/json 或 application/x-www-form-urlencoded
### 请求参数说明
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| user_id | int | 是 | 用户ID |
| page | int | 否 | 页码从1开始默认1 |
| page_size | int | 否 | 每页数量默认20最大100 |
| filter_zero | bool | 否 | 是否过滤0分岗位默认false |
### 请求示例
#### JSON格式请求
```json
{
"user_id": 527,
"page": 1,
"page_size": 20,
"filter_zero": false
}
```
#### 表单格式请求
```
user_id=527&page=1&page_size=20&filter_zero=false
```
## 响应参数
### 成功响应
```json
{
"code": 200,
"msg": "查询成功",
"data": {
"list": [
{
"position_id": 2,
"match_score": 95,
"position": {
"id": 2,
"positon_name": "人工智能与大数据侦察职位二",
"education_require": "本科及以上",
"degree_require": "学士及以上",
...
}
},
{
"position_id": 1,
"match_score": 80,
"position": { ... }
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 150,
"total_pages": 8,
"has_more": true
}
}
}
```
### 错误响应
```json
{
"code": 400,
"msg": "参数错误user_id不能为空且必须为数字",
"data": null
}
```
## 功能说明
### 1. 自动读取用户简历
接口会自动从数据库读取:
- 用户基本信息(`t_user`表)
- 教育经历(自动查找教育经历表)
### 2. 数据库快速过滤
使用SQL WHERE条件快速过滤岗位
- 学历要求过滤
- 性别要求过滤
- 排除已删除的岗位
### 3. 匹配度计算
对通过初步筛选的岗位进行详细匹配计算:
- 硬性条件检查(一票否决)
- 软性条件评分100分制
### 4. 排序和分页
- 按匹配度降序排序
- 支持分页返回
- 可选过滤0分岗位
## 性能说明
- **数据库过滤**利用SQL索引快速筛选从1万个岗位可能筛选到几百个
- **计算时间**:只计算通过初步筛选的岗位,大幅减少计算量
- **响应时间**通常在1-3秒内返回结果
## 注意事项
1. **教育经历表**:系统会自动查找以下表名:
- `t_user_education`
- `user_education`
- `t_education`
- `education`
如果您的教育经历表名不同,需要修改 `getUserResumeFromDb` 方法中的表名列表。
2. **年龄过滤**:由于年龄要求是文本格式(如"18周岁以上、35周岁以下"难以用SQL精确匹配会在详细匹配时检查。如需优化性能建议在数据库中添加 `age_min``age_max` 字段。
3. **专业要求**:专业要求存储在 `position_other_require` 的JSON字段中会在匹配时解析。
## 使用示例
```bash
# 查询用户ID为527的匹配岗位第1页每页20条
curl -X POST http://your-domain.com/match/batch \
-H "Content-Type: application/json" \
-d '{
"user_id": 527,
"page": 1,
"page_size": 20
}'
```
## 后续优化建议
1. **添加年龄字段**:在 `no_notice_position` 表中添加 `age_min``age_max` 字段,提升过滤效率
2. **建立索引**:对 `education_require``sex_require` 等字段建立索引
3. **缓存机制**:如果简历不变,可以缓存匹配结果
4. **专业分类表**:建立专业分类映射表,优化专业匹配

View File

@@ -0,0 +1,169 @@
# 批量岗位匹配方案设计
## 需求分析
- **场景**1万个岗位与1个简历进行匹配
- **要求**:按匹配度排序,支持分页返回
- **性能**:需要考虑内存和计算效率
## 方案设计
### 方案一:全量计算+排序+分页(推荐)
**优点**
- 实现简单
- 排序准确(需要全部计算才能正确排序)
- 支持分页
**缺点**
- 需要计算所有岗位(即使只返回一页)
- 内存占用较大1万个岗位约几MB
**适用场景**:岗位数量 < 5万单次请求可接受
**实现思路**
1. 遍历所有岗位计算匹配度
2. 按匹配度降序排序
3. 根据分页参数截取结果
4. 返回分页数据
### 方案二:分批计算+分页(优化版)
**优点**
- 内存占用可控
- 可以提前终止如果只需要前N个
**缺点**
- 实现复杂
- 需要全部计算才能准确排序
**适用场景**岗位数量 > 5万内存受限
**实现思路**
1. 分批处理岗位如每批1000个
2. 每批计算后合并排序
3. 分页返回
### 方案三:快速过滤+精确计算(最佳性能)
**优点**
- 性能最优
- 可以快速过滤掉0分岗位
**缺点**
- 需要两次遍历(先过滤,再计算)
**适用场景**大部分岗位匹配度为0的情况
**实现思路**
1. 第一遍:快速检查硬性条件,过滤掉明显不匹配的
2. 第二遍:对通过硬性条件的岗位进行详细计算
3. 排序+分页
## 推荐实现:方案一(全量计算+排序+分页)
### API设计
**接口地址**`POST /match/batch`
**请求参数**
```json
{
"positions": [
{ "id": 1, "position_require": {...} },
{ "id": 2, "position_require": {...} }
],
"resume": { ... },
"page": 1, // 页码从1开始
"page_size": 20, // 每页数量默认20
"filter_zero": false // 是否过滤0分岗位默认false
}
```
**响应格式**
```json
{
"code": 200,
"data": {
"list": [
{
"position_id": 2,
"match_score": 95,
"position": { ... } // 可选,是否返回完整岗位信息
}
],
"pagination": {
"page": 1,
"page_size": 20,
"total": 10000,
"total_pages": 500,
"has_more": true
}
}
}
```
### 性能估算
**1万个岗位的计算时间**
- 单个岗位匹配计算:约 1-5ms
- 1万个岗位约 10-50秒串行
- 优化后(减少重复计算):约 5-25秒
**内存占用**
- 单个岗位数据:约 1-5KB
- 1万个岗位约 10-50MB
- 匹配结果:约 5-10MB
- 总计:约 15-60MB可接受
### 优化建议
1. **可选过滤0分岗位**
- 如果 `filter_zero=true`,只返回匹配度>0的岗位
- 可以减少返回数据量
2. **可选:限制岗位数量**
- 如果岗位数量过大可以限制最大处理数量如最多1万个
3. **可选:异步处理**
- 如果计算时间过长,可以考虑异步处理
- 使用队列+轮询的方式
4. **可选:缓存机制**
- 如果简历不变,可以缓存匹配结果
- 使用 Redis 缓存key: resume_id, value: 匹配结果)
## 实现代码结构
```php
// MatchService.php
public function batchMatch(
array $positions,
array $resume,
int $page = 1,
int $pageSize = 20,
bool $filterZero = false
): array {
// 1. 计算所有岗位的匹配度
// 2. 排序
// 3. 过滤(可选)
// 4. 分页
// 5. 返回结果
}
```
## 使用示例
```php
// 控制器中调用
$matchService = new MatchService();
$result = $matchService->batchMatch(
$positions, // 1万个岗位
$resume, // 简历
1, // 第1页
20, // 每页20条
false // 不过滤0分
);
```

View File

@@ -0,0 +1,75 @@
# 数据库查询实现说明
## 需要的信息
为了实现基于数据库的快速匹配查询功能,我需要了解以下信息:
### 1. 岗位表positions/jobs结构
请提供:
- 表名
- 主要字段(特别是硬性条件相关字段):
- 学历要求字段名和存储格式
- 学位要求字段名和存储格式
- 年龄要求字段名和存储格式(是范围还是文本描述)
- 性别要求字段名和存储格式
- 专业要求字段名和存储格式
- 其他要求字段
### 2. 简历表resumes/users结构如果需要从数据库读取
或者告诉我:
- 简历数据是通过接口参数传入,还是需要从数据库查询?
- 如果从数据库查询,请提供表结构和字段信息
### 3. 数据格式示例
请提供:
- 岗位表中学历要求的实际存储值(如:"本科"、"本科及以上"、"3"等)
- 年龄要求的存储格式(如:`age_min=18, age_max=35``"18周岁以上、35周岁以下"`
- 专业要求的存储格式
## 实现方案
根据您提供的数据库结构,我将实现:
1. **快速过滤查询**使用SQL WHERE条件快速筛选符合条件的岗位
2. **匹配度计算**:对筛选后的岗位进行详细匹配计算
3. **排序分页**:按匹配度排序并分页返回
## 请提供的信息格式
可以以以下任一方式提供:
### 方式一SQL建表语句
```sql
CREATE TABLE positions (
id INT,
education_require VARCHAR(50),
...
);
```
### 方式二:表结构描述
```json
{
"table_name": "positions",
"fields": {
"id": "INT PRIMARY KEY",
"education_require": "VARCHAR(50) - 学历要求",
...
}
}
```
### 方式三:示例数据
提供几条实际的岗位数据示例,我可以根据数据格式推断表结构。
## 实现后的功能
- ✅ 接口:`POST /match/batch` - 批量匹配查询
- ✅ 支持分页:`page`, `page_size` 参数
- ✅ 支持过滤0分`filter_zero` 参数
- ✅ 快速响应:利用数据库索引,秒级返回结果