Update README.md to include comprehensive documentation for ThinkPHP 8, featuring installation instructions, new features, sponsorship details, and links to resources.
This commit is contained in:
1
app/.htaccess
Normal file
1
app/.htaccess
Normal file
@@ -0,0 +1 @@
|
||||
deny from all
|
||||
22
app/AppService.php
Normal file
22
app/AppService.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\Service;
|
||||
|
||||
/**
|
||||
* 应用服务类
|
||||
*/
|
||||
class AppService extends Service
|
||||
{
|
||||
public function register()
|
||||
{
|
||||
// 服务注册
|
||||
}
|
||||
|
||||
public function boot()
|
||||
{
|
||||
// 服务启动
|
||||
}
|
||||
}
|
||||
94
app/BaseController.php
Normal file
94
app/BaseController.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app;
|
||||
|
||||
use think\App;
|
||||
use think\exception\ValidateException;
|
||||
use think\Validate;
|
||||
|
||||
/**
|
||||
* 控制器基础类
|
||||
*/
|
||||
abstract class BaseController
|
||||
{
|
||||
/**
|
||||
* Request实例
|
||||
* @var \think\Request
|
||||
*/
|
||||
protected $request;
|
||||
|
||||
/**
|
||||
* 应用实例
|
||||
* @var \think\App
|
||||
*/
|
||||
protected $app;
|
||||
|
||||
/**
|
||||
* 是否批量验证
|
||||
* @var bool
|
||||
*/
|
||||
protected $batchValidate = false;
|
||||
|
||||
/**
|
||||
* 控制器中间件
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [];
|
||||
|
||||
/**
|
||||
* 构造方法
|
||||
* @access public
|
||||
* @param App $app 应用对象
|
||||
*/
|
||||
public function __construct(App $app)
|
||||
{
|
||||
$this->app = $app;
|
||||
$this->request = $this->app->request;
|
||||
|
||||
// 控制器初始化
|
||||
$this->initialize();
|
||||
}
|
||||
|
||||
// 初始化
|
||||
protected function initialize()
|
||||
{}
|
||||
|
||||
/**
|
||||
* 验证数据
|
||||
* @access protected
|
||||
* @param array $data 数据
|
||||
* @param string|array $validate 验证器名或者验证规则数组
|
||||
* @param array $message 提示信息
|
||||
* @param bool $batch 是否批量验证
|
||||
* @return array|string|true
|
||||
* @throws ValidateException
|
||||
*/
|
||||
protected function validate(array $data, string|array $validate, array $message = [], bool $batch = false)
|
||||
{
|
||||
if (is_array($validate)) {
|
||||
$v = new Validate();
|
||||
$v->rule($validate);
|
||||
} else {
|
||||
if (strpos($validate, '.')) {
|
||||
// 支持场景
|
||||
[$validate, $scene] = explode('.', $validate);
|
||||
}
|
||||
$class = false !== strpos($validate, '\\') ? $validate : $this->app->parseClass('validate', $validate);
|
||||
$v = new $class();
|
||||
if (!empty($scene)) {
|
||||
$v->scene($scene);
|
||||
}
|
||||
}
|
||||
|
||||
$v->message($message);
|
||||
|
||||
// 是否批量验证
|
||||
if ($batch || $this->batchValidate) {
|
||||
$v->batch(true);
|
||||
}
|
||||
|
||||
return $v->failException(true)->check($data);
|
||||
}
|
||||
|
||||
}
|
||||
58
app/ExceptionHandle.php
Normal file
58
app/ExceptionHandle.php
Normal file
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
use think\db\exception\DataNotFoundException;
|
||||
use think\db\exception\ModelNotFoundException;
|
||||
use think\exception\Handle;
|
||||
use think\exception\HttpException;
|
||||
use think\exception\HttpResponseException;
|
||||
use think\exception\ValidateException;
|
||||
use think\Response;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 应用异常处理类
|
||||
*/
|
||||
class ExceptionHandle extends Handle
|
||||
{
|
||||
/**
|
||||
* 不需要记录信息(日志)的异常类列表
|
||||
* @var array
|
||||
*/
|
||||
protected $ignoreReport = [
|
||||
HttpException::class,
|
||||
HttpResponseException::class,
|
||||
ModelNotFoundException::class,
|
||||
DataNotFoundException::class,
|
||||
ValidateException::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* 记录异常信息(包括日志或者其它方式记录)
|
||||
*
|
||||
* @access public
|
||||
* @param Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Throwable $exception): void
|
||||
{
|
||||
// 使用内置的方式记录异常日志
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @access public
|
||||
* @param \think\Request $request
|
||||
* @param Throwable $e
|
||||
* @return Response
|
||||
*/
|
||||
public function render($request, Throwable $e): Response
|
||||
{
|
||||
// 添加自定义异常处理机制
|
||||
|
||||
// 其他错误交给系统处理
|
||||
return parent::render($request, $e);
|
||||
}
|
||||
}
|
||||
8
app/Request.php
Normal file
8
app/Request.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
namespace app;
|
||||
|
||||
// 应用请求对象类
|
||||
class Request extends \think\Request
|
||||
{
|
||||
|
||||
}
|
||||
2
app/common.php
Normal file
2
app/common.php
Normal file
@@ -0,0 +1,2 @@
|
||||
<?php
|
||||
// 应用公共文件
|
||||
18
app/controller/Index.php
Normal file
18
app/controller/Index.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
|
||||
class Index extends BaseController
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
return '<style>*{ padding: 0; margin: 0; }</style><iframe src="https://www.thinkphp.cn/welcome?version=' . \think\facade\App::version() . '" width="100%" height="100%" frameborder="0" scrolling="auto"></iframe>';
|
||||
}
|
||||
|
||||
public function hello($name = 'ThinkPHP8')
|
||||
{
|
||||
return 'hello,' . $name;
|
||||
}
|
||||
}
|
||||
66
app/controller/MatchController.php
Normal file
66
app/controller/MatchController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\controller;
|
||||
|
||||
use app\BaseController;
|
||||
use app\service\MatchService;
|
||||
use think\response\Json;
|
||||
|
||||
/**
|
||||
* 岗位简历匹配度控制器
|
||||
*/
|
||||
class MatchController extends BaseController
|
||||
{
|
||||
/**
|
||||
* 计算岗位和简历的匹配度
|
||||
* @return Json
|
||||
*/
|
||||
public function calculate(): Json
|
||||
{
|
||||
try {
|
||||
// 获取请求参数(支持JSON和表单数据)
|
||||
$input = $this->request->param();
|
||||
$position = $input['position'] ?? [];
|
||||
$resume = $input['resume'] ?? [];
|
||||
|
||||
// 如果是JSON请求,尝试从JSON中获取
|
||||
if (empty($position) && empty($resume)) {
|
||||
$jsonData = json_decode($this->request->getContent(), true);
|
||||
if (is_array($jsonData)) {
|
||||
$position = $jsonData['position'] ?? [];
|
||||
$resume = $jsonData['resume'] ?? [];
|
||||
}
|
||||
}
|
||||
|
||||
// 参数验证
|
||||
if (empty($position) || empty($resume)) {
|
||||
return json([
|
||||
'code' => 400,
|
||||
'msg' => '参数错误:岗位信息和简历信息不能为空',
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
|
||||
// 计算匹配度
|
||||
$matchService = new MatchService();
|
||||
$score = $matchService->calculateMatchScore($position, $resume);
|
||||
|
||||
return json([
|
||||
'code' => 200,
|
||||
'msg' => '计算成功',
|
||||
'data' => [
|
||||
'match_score' => $score
|
||||
]
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
return json([
|
||||
'code' => 500,
|
||||
'msg' => '计算失败:' . $e->getMessage(),
|
||||
'data' => null
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
17
app/event.php
Normal file
17
app/event.php
Normal file
@@ -0,0 +1,17 @@
|
||||
<?php
|
||||
// 事件定义文件
|
||||
return [
|
||||
'bind' => [
|
||||
],
|
||||
|
||||
'listen' => [
|
||||
'AppInit' => [],
|
||||
'HttpRun' => [],
|
||||
'HttpEnd' => [],
|
||||
'LogLevel' => [],
|
||||
'LogWrite' => [],
|
||||
],
|
||||
|
||||
'subscribe' => [
|
||||
],
|
||||
];
|
||||
10
app/middleware.php
Normal file
10
app/middleware.php
Normal file
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
// 全局中间件定义文件
|
||||
return [
|
||||
// 全局请求缓存
|
||||
// \think\middleware\CheckRequestCache::class,
|
||||
// 多语言加载
|
||||
// \think\middleware\LoadLangPack::class,
|
||||
// Session初始化
|
||||
// \think\middleware\SessionInit::class
|
||||
];
|
||||
9
app/provider.php
Normal file
9
app/provider.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use app\ExceptionHandle;
|
||||
use app\Request;
|
||||
|
||||
// 容器Provider定义文件
|
||||
return [
|
||||
'think\Request' => Request::class,
|
||||
'think\exception\Handle' => ExceptionHandle::class,
|
||||
];
|
||||
9
app/service.php
Normal file
9
app/service.php
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
use app\AppService;
|
||||
|
||||
// 系统服务定义文件
|
||||
// 服务在完成全局初始化之后执行
|
||||
return [
|
||||
AppService::class,
|
||||
];
|
||||
620
app/service/MatchService.php
Normal file
620
app/service/MatchService.php
Normal file
@@ -0,0 +1,620 @@
|
||||
<?php
|
||||
declare (strict_types = 1);
|
||||
|
||||
namespace app\service;
|
||||
|
||||
/**
|
||||
* 岗位简历匹配度计算服务
|
||||
* 采用硬性条件一票否决 + 软性条件加分机制
|
||||
*/
|
||||
class MatchService
|
||||
{
|
||||
/**
|
||||
* 计算岗位和简历的匹配度
|
||||
* @param array $position 岗位信息
|
||||
* @param array $resume 简历信息
|
||||
* @return int 匹配度分数(0-100分),硬性条件不满足返回0分
|
||||
*/
|
||||
public function calculateMatchScore(array $position, array $resume): int
|
||||
{
|
||||
// 第一步:检查硬性条件(一票否决)
|
||||
$hardCheck = $this->checkHardRequirements($position, $resume);
|
||||
|
||||
if (!$hardCheck['passed']) {
|
||||
return 0; // 硬性条件不满足,直接返回0分
|
||||
}
|
||||
|
||||
// 第二步:计算软性条件匹配度(100分制)
|
||||
$softCheck = $this->calculateSoftRequirements($position, $resume);
|
||||
|
||||
return $softCheck['score'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查硬性条件(一票否决机制)
|
||||
* @param array $position 岗位信息
|
||||
* @param array $resume 简历信息
|
||||
* @return array
|
||||
*/
|
||||
private function checkHardRequirements(array $position, array $resume): array
|
||||
{
|
||||
$positionRequire = $position['position_require'] ?? [];
|
||||
$result = [
|
||||
'passed' => true,
|
||||
'rejection_reasons' => [],
|
||||
'details' => []
|
||||
];
|
||||
|
||||
// 1. 学历要求(硬性)
|
||||
if (!empty($positionRequire['学历要求'])) {
|
||||
$educationCheck = $this->checkEducation($positionRequire['学历要求'], $resume);
|
||||
$result['details']['学历要求'] = $educationCheck;
|
||||
if (!$educationCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
$result['rejection_reasons'][] = $educationCheck['reason'];
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 学位要求(硬性)
|
||||
if (!empty($positionRequire['学位要求'])) {
|
||||
$degreeCheck = $this->checkDegree($positionRequire['学位要求'], $resume);
|
||||
$result['details']['学位要求'] = $degreeCheck;
|
||||
if (!$degreeCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
$result['rejection_reasons'][] = $degreeCheck['reason'];
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 年龄要求(硬性)
|
||||
if (!empty($positionRequire['年龄要求'])) {
|
||||
$ageCheck = $this->checkAge($positionRequire['年龄要求'], $resume);
|
||||
$result['details']['年龄要求'] = $ageCheck;
|
||||
if (!$ageCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
$result['rejection_reasons'][] = $ageCheck['reason'];
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 专业要求(硬性)
|
||||
if (!empty($positionRequire['专业(学科)类别'])) {
|
||||
$majorCheck = $this->checkMajor($positionRequire['专业(学科)类别'], $resume);
|
||||
$result['details']['专业要求'] = $majorCheck;
|
||||
if (!$majorCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
$result['rejection_reasons'][] = $majorCheck['reason'];
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 性别要求(硬性,如果明确要求)
|
||||
$otherConditions = $positionRequire['其他资格条件'] ?? '';
|
||||
if (preg_match('/适合(男|女)性/u', $otherConditions, $matches)) {
|
||||
$genderCheck = $this->checkGender($matches[1], $resume);
|
||||
$result['details']['性别要求'] = $genderCheck;
|
||||
if (!$genderCheck['passed']) {
|
||||
$result['passed'] = false;
|
||||
$result['rejection_reasons'][] = $genderCheck['reason'];
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算软性条件匹配度(100分制)
|
||||
* @param array $position 岗位信息
|
||||
* @param array $resume 简历信息
|
||||
* @return array
|
||||
*/
|
||||
private function calculateSoftRequirements(array $position, array $resume): array
|
||||
{
|
||||
$positionRequire = $position['position_require'] ?? [];
|
||||
$score = 0;
|
||||
$details = [];
|
||||
$maxScore = 100;
|
||||
|
||||
// 1. 专业匹配度(40分)- 即使专业类别符合,也可以根据专业相关性打分
|
||||
$majorScore = $this->scoreMajorMatch($positionRequire['专业(学科)类别'] ?? '', $resume);
|
||||
$score += $majorScore['score'];
|
||||
$details['专业匹配度'] = $majorScore;
|
||||
|
||||
// 2. 学历层次匹配度(20分)- 超过要求的学历可以加分
|
||||
$educationScore = $this->scoreEducationLevel($positionRequire['学历要求'] ?? '', $resume);
|
||||
$score += $educationScore['score'];
|
||||
$details['学历层次匹配度'] = $educationScore;
|
||||
|
||||
// 3. 专业资格条件(20分)- 竞赛获奖等
|
||||
$qualificationScore = $this->scoreQualification($positionRequire['专业资格条件'] ?? '', $resume);
|
||||
$score += $qualificationScore['score'];
|
||||
$details['专业资格条件'] = $qualificationScore;
|
||||
|
||||
// 4. 基层工作经历(10分)
|
||||
$workExpScore = $this->scoreWorkExperience($resume);
|
||||
$score += $workExpScore['score'];
|
||||
$details['基层工作经历'] = $workExpScore;
|
||||
|
||||
// 5. 其他条件匹配(10分)- 如政治面貌、特殊身份等
|
||||
$otherScore = $this->scoreOtherConditions($positionRequire['其他资格条件'] ?? '', $resume);
|
||||
$score += $otherScore['score'];
|
||||
$details['其他条件'] = $otherScore;
|
||||
|
||||
return [
|
||||
'score' => min(100, $score),
|
||||
'max_score' => $maxScore,
|
||||
'details' => $details
|
||||
];
|
||||
}
|
||||
|
||||
// ==================== 硬性条件检查方法 ====================
|
||||
|
||||
/**
|
||||
* 检查学历要求(硬性)
|
||||
*/
|
||||
private function checkEducation(string $requirement, array $resume): array
|
||||
{
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (empty($educations)) {
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '未提供学历信息',
|
||||
'required' => $requirement,
|
||||
'actual' => null
|
||||
];
|
||||
}
|
||||
|
||||
$highestEducation = $this->getHighestEducation($educations);
|
||||
if (empty($highestEducation)) {
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '无法识别最高学历',
|
||||
'required' => $requirement,
|
||||
'actual' => null
|
||||
];
|
||||
}
|
||||
|
||||
$educationLevel = $highestEducation['education_level'] ?? '';
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
];
|
||||
|
||||
$requireLevel = $this->parseEducationRequire($requirement);
|
||||
$actualLevel = $educationLevels[$educationLevel] ?? 0;
|
||||
|
||||
if ($actualLevel >= $requireLevel) {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requirement,
|
||||
'actual' => $educationLevel
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => "学历不符合要求:需要{$requirement},实际为{$educationLevel}",
|
||||
'required' => $requirement,
|
||||
'actual' => $educationLevel
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查学位要求(硬性)
|
||||
*/
|
||||
private function checkDegree(string $requirement, array $resume): array
|
||||
{
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (empty($educations)) {
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '未提供学位信息',
|
||||
'required' => $requirement,
|
||||
'actual' => null
|
||||
];
|
||||
}
|
||||
|
||||
$highestEducation = $this->getHighestEducation($educations);
|
||||
$degree = $highestEducation['degree'] ?? '';
|
||||
|
||||
$degreeLevels = [
|
||||
'学士' => 1,
|
||||
'硕士' => 2,
|
||||
'博士' => 3,
|
||||
];
|
||||
|
||||
$requireLevel = $this->parseDegreeRequire($requirement);
|
||||
$actualLevel = $degreeLevels[$degree] ?? 0;
|
||||
|
||||
if ($actualLevel >= $requireLevel) {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requirement,
|
||||
'actual' => $degree
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => "学位不符合要求:需要{$requirement},实际为{$degree}",
|
||||
'required' => $requirement,
|
||||
'actual' => $degree
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查年龄要求(硬性)
|
||||
*/
|
||||
private function checkAge(string $requirement, array $resume): array
|
||||
{
|
||||
$birthDate = $resume['birth_date'] ?? '';
|
||||
if (empty($birthDate)) {
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '未提供出生日期',
|
||||
'required' => $requirement,
|
||||
'actual' => null
|
||||
];
|
||||
}
|
||||
|
||||
$age = $this->calculateAge($birthDate);
|
||||
|
||||
if (preg_match('/(\d+)周岁以上.*?(\d+)周岁以下/u', $requirement, $matches)) {
|
||||
$minAge = (int)$matches[1];
|
||||
$maxAge = (int)$matches[2];
|
||||
|
||||
if ($age >= $minAge && $age <= $maxAge) {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requirement,
|
||||
'actual' => "{$age}岁"
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => "年龄不符合要求:需要{$minAge}-{$maxAge}岁,实际为{$age}岁",
|
||||
'required' => $requirement,
|
||||
'actual' => "{$age}岁"
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '无法解析年龄要求格式',
|
||||
'required' => $requirement,
|
||||
'actual' => "{$age}岁"
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查专业要求(硬性)
|
||||
*/
|
||||
private function checkMajor(string $requirement, array $resume): array
|
||||
{
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (empty($educations)) {
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => '未提供专业信息',
|
||||
'required' => $requirement,
|
||||
'actual' => null
|
||||
];
|
||||
}
|
||||
|
||||
$matchedMajors = [];
|
||||
foreach ($educations as $education) {
|
||||
$majorName = $education['majors_name'] ?? '';
|
||||
if ($this->isMajorCategoryMatch($majorName, $requirement)) {
|
||||
$matchedMajors[] = $majorName;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($matchedMajors)) {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requirement,
|
||||
'actual' => implode('、', $matchedMajors)
|
||||
];
|
||||
}
|
||||
|
||||
$actualMajors = array_map(function($edu) {
|
||||
return $edu['majors_name'] ?? '';
|
||||
}, $educations);
|
||||
$actualMajors = array_filter($actualMajors);
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => "专业不符合要求:需要{$requirement},实际为" . implode('、', $actualMajors),
|
||||
'required' => $requirement,
|
||||
'actual' => implode('、', $actualMajors)
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查性别要求(硬性)
|
||||
*/
|
||||
private function checkGender(string $requireGender, array $resume): array
|
||||
{
|
||||
$gender = $resume['gender'] ?? '';
|
||||
if ($gender === $requireGender) {
|
||||
return [
|
||||
'passed' => true,
|
||||
'required' => $requireGender,
|
||||
'actual' => $gender
|
||||
];
|
||||
}
|
||||
|
||||
return [
|
||||
'passed' => false,
|
||||
'reason' => "性别不符合要求:需要{$requireGender},实际为{$gender}",
|
||||
'required' => $requireGender,
|
||||
'actual' => $gender
|
||||
];
|
||||
}
|
||||
|
||||
// ==================== 软性条件评分方法 ====================
|
||||
|
||||
/**
|
||||
* 专业匹配度评分(40分)
|
||||
*/
|
||||
private function scoreMajorMatch(string $requirement, array $resume): array
|
||||
{
|
||||
if (empty($requirement)) {
|
||||
return ['score' => 40, 'reason' => '无专业要求'];
|
||||
}
|
||||
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (empty($educations)) {
|
||||
return ['score' => 0, 'reason' => '无专业信息'];
|
||||
}
|
||||
|
||||
$maxScore = 0;
|
||||
foreach ($educations as $education) {
|
||||
$majorName = $education['majors_name'] ?? '';
|
||||
$score = $this->calculateMajorRelevanceScore($majorName, $requirement);
|
||||
$maxScore = max($maxScore, $score);
|
||||
}
|
||||
|
||||
return [
|
||||
'score' => min(40, $maxScore),
|
||||
'max_score' => 40,
|
||||
'reason' => $maxScore >= 40 ? '专业高度匹配' : '专业部分匹配'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 学历层次匹配度评分(20分)
|
||||
*/
|
||||
private function scoreEducationLevel(string $requirement, array $resume): array
|
||||
{
|
||||
$educations = $resume['education'] ?? [];
|
||||
if (empty($educations)) {
|
||||
return ['score' => 0, 'reason' => '无学历信息'];
|
||||
}
|
||||
|
||||
$highestEducation = $this->getHighestEducation($educations);
|
||||
$educationLevel = $highestEducation['education_level'] ?? '';
|
||||
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
];
|
||||
|
||||
$requireLevel = $this->parseEducationRequire($requirement);
|
||||
$actualLevel = $educationLevels[$educationLevel] ?? 0;
|
||||
|
||||
// 刚好满足要求:15分,超过一级:20分,超过两级及以上:20分
|
||||
if ($actualLevel == $requireLevel) {
|
||||
return ['score' => 15, 'max_score' => 20, 'reason' => '刚好满足要求'];
|
||||
} elseif ($actualLevel > $requireLevel) {
|
||||
$exceed = $actualLevel - $requireLevel;
|
||||
return [
|
||||
'score' => 15 + min(5, $exceed * 5),
|
||||
'max_score' => 20,
|
||||
'reason' => "超过要求{$exceed}级"
|
||||
];
|
||||
}
|
||||
|
||||
return ['score' => 0, 'reason' => '未达到要求'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 专业资格条件评分(20分)
|
||||
*/
|
||||
private function scoreQualification(string $requirement, array $resume): array
|
||||
{
|
||||
if (empty($requirement)) {
|
||||
return [
|
||||
'score' => 20,
|
||||
'max_score' => 20,
|
||||
'reason' => '无专业资格要求'
|
||||
];
|
||||
}
|
||||
|
||||
// 这里可以根据简历中的证书、获奖等信息来评分
|
||||
// 由于当前简历数据结构中没有这些字段,暂时返回基础分数
|
||||
// 实际应用中需要扩展简历数据结构
|
||||
|
||||
return [
|
||||
'score' => 0,
|
||||
'max_score' => 20,
|
||||
'reason' => '未提供专业资格证明材料(需扩展简历数据结构)'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 基层工作经历评分(10分)
|
||||
*/
|
||||
private function scoreWorkExperience(array $resume): array
|
||||
{
|
||||
$workExperience = $resume['work_experience'] ?? '';
|
||||
|
||||
if (empty($workExperience) || strpos($workExperience, '无') !== false) {
|
||||
return [
|
||||
'score' => 0,
|
||||
'max_score' => 10,
|
||||
'reason' => '无基层工作经历'
|
||||
];
|
||||
}
|
||||
|
||||
// 可以根据工作年限进一步细化评分
|
||||
return [
|
||||
'score' => 10,
|
||||
'max_score' => 10,
|
||||
'reason' => '有基层工作经历'
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 其他条件评分(10分)
|
||||
*/
|
||||
private function scoreOtherConditions(string $requirement, array $resume): array
|
||||
{
|
||||
if (empty($requirement)) {
|
||||
return ['score' => 10, 'reason' => '无其他条件要求'];
|
||||
}
|
||||
|
||||
$score = 0;
|
||||
$maxScore = 10;
|
||||
|
||||
// 政治面貌等可以根据岗位要求评分
|
||||
// 当前暂不实现,返回基础分数
|
||||
|
||||
return [
|
||||
'score' => 10,
|
||||
'max_score' => $maxScore,
|
||||
'reason' => '其他条件匹配(需根据具体岗位要求细化)'
|
||||
];
|
||||
}
|
||||
|
||||
// ==================== 辅助方法 ====================
|
||||
|
||||
/**
|
||||
* 计算年龄
|
||||
*/
|
||||
private function calculateAge(string $birthDate): int
|
||||
{
|
||||
$birthTimestamp = strtotime($birthDate);
|
||||
$currentTimestamp = time();
|
||||
$age = date('Y', $currentTimestamp) - date('Y', $birthTimestamp);
|
||||
if (date('md', $currentTimestamp) < date('md', $birthTimestamp)) {
|
||||
$age--;
|
||||
}
|
||||
return $age;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最高学历
|
||||
*/
|
||||
private function getHighestEducation(array $educations): ?array
|
||||
{
|
||||
if (empty($educations)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$educationLevels = [
|
||||
'普通本科' => 3,
|
||||
'硕士研究生' => 4,
|
||||
'博士研究生' => 5,
|
||||
];
|
||||
|
||||
$highest = null;
|
||||
$highestLevel = 0;
|
||||
|
||||
foreach ($educations as $education) {
|
||||
$level = $educationLevels[$education['education_level'] ?? ''] ?? 0;
|
||||
if ($level > $highestLevel) {
|
||||
$highestLevel = $level;
|
||||
$highest = $education;
|
||||
}
|
||||
}
|
||||
|
||||
return $highest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析学历要求
|
||||
*/
|
||||
private function parseEducationRequire(string $require): int
|
||||
{
|
||||
if (strpos($require, '本科') !== false) {
|
||||
return 3;
|
||||
}
|
||||
if (strpos($require, '硕士') !== false) {
|
||||
return 4;
|
||||
}
|
||||
if (strpos($require, '博士') !== false) {
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析学位要求
|
||||
*/
|
||||
private function parseDegreeRequire(string $require): int
|
||||
{
|
||||
if (strpos($require, '学士') !== false) {
|
||||
return 1;
|
||||
}
|
||||
if (strpos($require, '硕士') !== false) {
|
||||
return 2;
|
||||
}
|
||||
if (strpos($require, '博士') !== false) {
|
||||
return 3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断专业类别是否匹配(硬性检查)
|
||||
*/
|
||||
private function isMajorCategoryMatch(string $majorName, string $majorRequire): bool
|
||||
{
|
||||
if (empty($majorName) || empty($majorRequire)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$requireCategories = explode(',', $majorRequire);
|
||||
|
||||
foreach ($requireCategories as $category) {
|
||||
$category = trim($category);
|
||||
|
||||
// 计算机科学与技术类
|
||||
if (strpos($category, '计算机') !== false) {
|
||||
$computerKeywords = ['计算机', '软件', '网络', '信息', '数据', '人工智能', '大数据', '网络安全', '信息安全'];
|
||||
foreach ($computerKeywords as $keyword) {
|
||||
if (strpos($majorName, $keyword) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 电气、电子及自动化类
|
||||
if (strpos($category, '电气') !== false || strpos($category, '电子') !== false || strpos($category, '自动化') !== false) {
|
||||
$electronicsKeywords = ['电气', '电子', '自动化', '通信', '电信', '电子信息'];
|
||||
foreach ($electronicsKeywords as $keyword) {
|
||||
if (strpos($majorName, $keyword) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算专业相关性分数(软性评分)
|
||||
*/
|
||||
private function calculateMajorRelevanceScore(string $majorName, string $majorRequire): int
|
||||
{
|
||||
if ($this->isMajorCategoryMatch($majorName, $majorRequire)) {
|
||||
return 40; // 完全匹配
|
||||
}
|
||||
|
||||
// 可以根据专业相似度进一步细化评分
|
||||
// 这里简单返回0分
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user