新增批量
This commit is contained in:
@@ -3,6 +3,8 @@ declare (strict_types = 1);
|
||||
|
||||
namespace app\service;
|
||||
|
||||
use think\facade\Db;
|
||||
|
||||
/**
|
||||
* 岗位简历匹配度计算服务
|
||||
* 采用硬性条件一票否决 + 软性条件加分机制
|
||||
@@ -30,6 +32,225 @@ class MatchService
|
||||
return $softCheck['score'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量匹配查询(基于数据库)
|
||||
* @param int $userId 用户ID
|
||||
* @param int $page 页码,从1开始
|
||||
* @param int $pageSize 每页数量
|
||||
* @param bool $filterZero 是否过滤0分岗位
|
||||
* @return array
|
||||
*/
|
||||
public function batchMatchFromDb(int $userId, int $page = 1, int $pageSize = 20, bool $filterZero = false): array
|
||||
{
|
||||
// 1. 从数据库获取用户简历信息
|
||||
$resume = $this->getUserResumeFromDb($userId);
|
||||
if (empty($resume)) {
|
||||
return [
|
||||
'list' => [],
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
'total' => 0,
|
||||
'total_pages' => 0
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
// 2. 数据库快速过滤岗位
|
||||
$filteredPositions = $this->filterPositionsFromDb($resume);
|
||||
|
||||
// 3. 计算匹配度
|
||||
$results = [];
|
||||
foreach ($filteredPositions as $position) {
|
||||
$score = $this->calculateMatchScore($position, $resume);
|
||||
|
||||
if ($filterZero && $score == 0) {
|
||||
continue; // 过滤0分岗位
|
||||
}
|
||||
|
||||
$results[] = [
|
||||
'position_id' => $position['id'],
|
||||
'match_score' => $score,
|
||||
'position' => $position
|
||||
];
|
||||
}
|
||||
|
||||
// 4. 按匹配度降序排序
|
||||
usort($results, function($a, $b) {
|
||||
return $b['match_score'] - $a['match_score'];
|
||||
});
|
||||
|
||||
// 5. 分页
|
||||
$total = count($results);
|
||||
$totalPages = (int)ceil($total / $pageSize);
|
||||
$offset = ($page - 1) * $pageSize;
|
||||
$paginatedList = array_slice($results, $offset, $pageSize);
|
||||
|
||||
return [
|
||||
'list' => $paginatedList,
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'page_size' => $pageSize,
|
||||
'total' => $total,
|
||||
'total_pages' => $totalPages,
|
||||
'has_more' => $page < $totalPages
|
||||
]
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库获取用户简历信息
|
||||
* @param int $userId 用户ID
|
||||
* @return array
|
||||
*/
|
||||
private function getUserResumeFromDb(int $userId): array
|
||||
{
|
||||
// 获取用户基本信息
|
||||
$user = Db::name('t_user')->where('uid', $userId)->find();
|
||||
if (empty($user)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 构建简历数据结构
|
||||
$resume = [
|
||||
'user_id' => $userId,
|
||||
'birth_date' => $user['birth_date'] ?? '',
|
||||
'gender' => $user['gender'] ?? '',
|
||||
'ethnicity' => $user['ethnicity'] ?? '',
|
||||
'political_status' => $user['political_status'] ?? '',
|
||||
'work_experience' => $user['work_experience'] ?? '',
|
||||
'education' => []
|
||||
];
|
||||
|
||||
// 获取教育经历(假设有教育经历表,表名可能是 t_user_education 或类似)
|
||||
// 先尝试常见的表名
|
||||
$educationTables = ['t_user_education', 'user_education', 't_education', 'education'];
|
||||
$educations = [];
|
||||
|
||||
foreach ($educationTables as $tableName) {
|
||||
try {
|
||||
$educations = Db::name($tableName)->where('user_id', $userId)->select()->toArray();
|
||||
if (!empty($educations)) {
|
||||
break;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
// 表不存在,继续尝试下一个
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到教育经历表,尝试从用户表的JSON字段获取
|
||||
if (empty($educations) && isset($user['education'])) {
|
||||
$educationData = is_string($user['education']) ? json_decode($user['education'], true) : $user['education'];
|
||||
if (is_array($educationData)) {
|
||||
$educations = $educationData;
|
||||
}
|
||||
}
|
||||
|
||||
$resume['education'] = $educations;
|
||||
|
||||
return $resume;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从数据库快速过滤岗位
|
||||
* @param array $resume 简历信息
|
||||
* @return array
|
||||
*/
|
||||
private function filterPositionsFromDb(array $resume): array
|
||||
{
|
||||
$query = Db::name('no_notice_position')
|
||||
->where('deleted_at', null); // 排除已删除的岗位
|
||||
|
||||
// 计算年龄
|
||||
$age = 0;
|
||||
if (!empty($resume['birth_date'])) {
|
||||
$age = $this->calculateAge($resume['birth_date']);
|
||||
}
|
||||
|
||||
// 学历过滤(如果简历有学历信息)
|
||||
if (!empty($resume['education'])) {
|
||||
$highestEducation = $this->getHighestEducation($resume['education']);
|
||||
$educationLevel = $highestEducation['education_level'] ?? '';
|
||||
|
||||
// 学历等级映射
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'本科' => 3,
|
||||
'大学本科' => 3,
|
||||
'本科学历' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'硕士' => 4,
|
||||
'研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
'博士' => 5,
|
||||
];
|
||||
|
||||
$actualLevel = $educationLevels[$educationLevel] ?? 0;
|
||||
|
||||
// 学历要求过滤:岗位要求的学历等级 <= 简历实际学历等级
|
||||
// 这里简化处理,实际可以根据数据库中的具体值调整
|
||||
if ($actualLevel >= 3) {
|
||||
// 如果是本科及以上,可以匹配"本科"、"本科及以上"等要求
|
||||
$query->where(function($q) {
|
||||
$q->where('education_require', 'like', '%本科%')
|
||||
->orWhere('education_require', 'like', '%硕士%')
|
||||
->orWhere('education_require', 'like', '%博士%')
|
||||
->orWhere('education_require', '=', '')
|
||||
->orWhereNull('education_require');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 年龄过滤(年龄要求是文本格式,如"18周岁以上、35周岁以下")
|
||||
// 这里先不过滤,在详细匹配时再检查,因为文本格式难以用SQL精确匹配
|
||||
// 如果需要优化,可以在数据库中添加 age_min 和 age_max 字段
|
||||
|
||||
// 性别过滤
|
||||
if (!empty($resume['gender'])) {
|
||||
$query->where(function($q) use ($resume) {
|
||||
$q->where('sex_require', '不限制')
|
||||
->orWhere('sex_require', $resume['gender'])
|
||||
->orWhere('sex_require', '')
|
||||
->orWhereNull('sex_require');
|
||||
});
|
||||
}
|
||||
|
||||
// 获取过滤后的岗位
|
||||
$positions = $query->select()->toArray();
|
||||
|
||||
// 解析JSON字段
|
||||
foreach ($positions as &$position) {
|
||||
if (!empty($position['position_other_require'])) {
|
||||
$otherRequire = is_string($position['position_other_require'])
|
||||
? json_decode($position['position_other_require'], true)
|
||||
: $position['position_other_require'];
|
||||
|
||||
// 将JSON数据合并到position_require中
|
||||
$position['position_require'] = [
|
||||
'学历要求' => $position['education_require'] ?? '',
|
||||
'学位要求' => $position['degree_require'] ?? '',
|
||||
'年龄要求' => $position['age_require'] ?? '',
|
||||
'性别要求' => $position['sex_require'] ?? '',
|
||||
'专业(学科)类别' => $otherRequire['专业(学科)类别'] ?? '',
|
||||
'专业-本科' => $otherRequire['专业-本科'] ?? '',
|
||||
'专业-硕士' => $otherRequire['专业-硕士'] ?? '',
|
||||
'其他资格条件' => $otherRequire['其他资格条件'] ?? '',
|
||||
'专业资格条件' => $otherRequire['专业资格条件'] ?? '',
|
||||
];
|
||||
} else {
|
||||
$position['position_require'] = [
|
||||
'学历要求' => $position['education_require'] ?? '',
|
||||
'学位要求' => $position['degree_require'] ?? '',
|
||||
'年龄要求' => $position['age_require'] ?? '',
|
||||
'性别要求' => $position['sex_require'] ?? '',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $positions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查硬性条件(一票否决机制)
|
||||
* @param array $position 岗位信息
|
||||
@@ -76,8 +297,10 @@ class MatchService
|
||||
}
|
||||
|
||||
// 4. 专业要求(硬性)
|
||||
if (!empty($positionRequire['专业(学科)类别'])) {
|
||||
$majorCheck = $this->checkMajor($positionRequire['专业(学科)类别'], $resume);
|
||||
// 支持多种专业字段格式:专业(学科)类别、专业-本科、专业-硕士
|
||||
$majorRequire = $this->getMajorRequirement($positionRequire, $resume);
|
||||
if (!empty($majorRequire)) {
|
||||
$majorCheck = $this->checkMajor($majorRequire, $resume);
|
||||
$result['details']['专业要求'] = $majorCheck;
|
||||
if (!$majorCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
@@ -174,8 +397,14 @@ class MatchService
|
||||
$educationLevel = $highestEducation['education_level'] ?? '';
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'本科' => 3,
|
||||
'大学本科' => 3,
|
||||
'本科学历' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'硕士' => 4,
|
||||
'研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
'博士' => 5,
|
||||
];
|
||||
|
||||
$requireLevel = $this->parseEducationRequire($requirement);
|
||||
@@ -245,6 +474,16 @@ class MatchService
|
||||
*/
|
||||
private function checkAge(string $requirement, array $resume): array
|
||||
{
|
||||
// 如果年龄要求为空或"无",直接通过
|
||||
$requirement = trim($requirement);
|
||||
if (empty($requirement) || $requirement === '无' || $requirement === '不限制') {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requirement ?: '无要求',
|
||||
'actual' => '无要求'
|
||||
];
|
||||
}
|
||||
|
||||
$birthDate = $resume['birth_date'] ?? '';
|
||||
if (empty($birthDate)) {
|
||||
return [
|
||||
@@ -396,8 +635,14 @@ class MatchService
|
||||
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'本科' => 3,
|
||||
'大学本科' => 3,
|
||||
'本科学历' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'硕士' => 4,
|
||||
'研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
'博士' => 5,
|
||||
];
|
||||
|
||||
$requireLevel = $this->parseEducationRequire($requirement);
|
||||
@@ -514,8 +759,14 @@ class MatchService
|
||||
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'本科' => 3,
|
||||
'大学本科' => 3,
|
||||
'本科学历' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'硕士' => 4,
|
||||
'研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
'博士' => 5,
|
||||
];
|
||||
|
||||
$highest = null;
|
||||
@@ -566,6 +817,41 @@ class MatchService
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取专业要求(支持多种格式)
|
||||
*/
|
||||
private function getMajorRequirement(array $positionRequire, array $resume): string
|
||||
{
|
||||
// 优先使用 专业(学科)类别
|
||||
if (!empty($positionRequire['专业(学科)类别'])) {
|
||||
return $positionRequire['专业(学科)类别'];
|
||||
}
|
||||
|
||||
// 根据最高学历选择对应的专业要求
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (!empty($educations)) {
|
||||
$highestEducation = $this->getHighestEducation($educations);
|
||||
$educationLevel = $highestEducation['education_level'] ?? '';
|
||||
|
||||
// 判断学历等级
|
||||
$isUndergraduate = in_array($educationLevel, ['普通本科', '本科', '大学本科', '本科学历']);
|
||||
$isGraduate = in_array($educationLevel, ['硕士研究生', '硕士', '研究生', '博士研究生', '博士']);
|
||||
|
||||
if ($isUndergraduate && !empty($positionRequire['专业-本科'])) {
|
||||
return $positionRequire['专业-本科'];
|
||||
}
|
||||
if ($isGraduate && !empty($positionRequire['专业-硕士'])) {
|
||||
return $positionRequire['专业-硕士'];
|
||||
}
|
||||
// 如果没有对应学历的专业要求,尝试使用另一个
|
||||
if ($isGraduate && !empty($positionRequire['专业-本科'])) {
|
||||
return $positionRequire['专业-本科'];
|
||||
}
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断专业类别是否匹配(硬性检查)
|
||||
*/
|
||||
@@ -575,11 +861,17 @@ class MatchService
|
||||
return false;
|
||||
}
|
||||
|
||||
$requireCategories = explode(',', $majorRequire);
|
||||
// 支持多种分隔符:,、、
|
||||
$requireCategories = preg_split('/[,、,]/u', $majorRequire);
|
||||
|
||||
foreach ($requireCategories as $category) {
|
||||
$category = trim($category);
|
||||
|
||||
// 直接匹配专业名称
|
||||
if ($majorName === $category || strpos($majorName, $category) !== false || strpos($category, $majorName) !== false) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 计算机科学与技术类
|
||||
if (strpos($category, '计算机') !== false) {
|
||||
$computerKeywords = ['计算机', '软件', '网络', '信息', '数据', '人工智能', '大数据', '网络安全', '信息安全'];
|
||||
@@ -599,6 +891,20 @@ class MatchService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 教育学类
|
||||
if (strpos($category, '教育') !== false) {
|
||||
if (strpos($majorName, '教育') !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// 心理学类
|
||||
if (strpos($category, '心理') !== false) {
|
||||
if (strpos($majorName, '心理') !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
Reference in New Issue
Block a user