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:
杨志
2026-01-05 10:10:51 +08:00
parent 66242a9e21
commit e41dd33d23
49 changed files with 2439 additions and 2 deletions

11
.example.env Normal file
View File

@@ -0,0 +1,11 @@
APP_DEBUG = true
DB_TYPE = mysql
DB_HOST = 127.0.0.1
DB_NAME = test
DB_USER = username
DB_PASS = password
DB_PORT = 3306
DB_CHARSET = utf8
DEFAULT_LANG = zh-cn

12
.gitignore vendored Normal file
View File

@@ -0,0 +1,12 @@
*.log
.env
composer.phar
composer.lock
.DS_Store
Thumbs.db
/.idea
/.vscode
/vendor
/.settings
/.buildpath
/.project

42
.travis.yml Normal file
View File

@@ -0,0 +1,42 @@
sudo: false
language: php
branches:
only:
- stable
cache:
directories:
- $HOME/.composer/cache
before_install:
- composer self-update
install:
- composer install --no-dev --no-interaction --ignore-platform-reqs
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Core.zip .
- composer require --update-no-dev --no-interaction "topthink/think-image:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-migration:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-captcha:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-mongo:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-worker:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-helper:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-queue:^1.0"
- composer require --update-no-dev --no-interaction "topthink/think-angular:^1.0"
- composer require --dev --update-no-dev --no-interaction "topthink/think-testing:^1.0"
- zip -r --exclude='*.git*' --exclude='*.zip' --exclude='*.travis.yml' ThinkPHP_Full.zip .
script:
- php think unit
deploy:
provider: releases
api_key:
secure: TSF6bnl2JYN72UQOORAJYL+CqIryP2gHVKt6grfveQ7d9rleAEoxlq6PWxbvTI4jZ5nrPpUcBUpWIJHNgVcs+bzLFtyh5THaLqm39uCgBbrW7M8rI26L8sBh/6nsdtGgdeQrO/cLu31QoTzbwuz1WfAVoCdCkOSZeXyT/CclH99qV6RYyQYqaD2wpRjrhA5O4fSsEkiPVuk0GaOogFlrQHx+C+lHnf6pa1KxEoN1A0UxxVfGX6K4y5g4WQDO5zT4bLeubkWOXK0G51XSvACDOZVIyLdjApaOFTwamPcD3S1tfvuxRWWvsCD5ljFvb2kSmx5BIBNwN80MzuBmrGIC27XLGOxyMerwKxB6DskNUO9PflKHDPI61DRq0FTy1fv70SFMSiAtUv9aJRT41NQh9iJJ0vC8dl+xcxrWIjU1GG6+l/ZcRqVx9V1VuGQsLKndGhja7SQ+X1slHl76fRq223sMOql7MFCd0vvvxVQ2V39CcFKao/LB1aPH3VhODDEyxwx6aXoTznvC/QPepgWsHOWQzKj9ftsgDbsNiyFlXL4cu8DWUty6rQy8zT2b4O8b1xjcwSUCsy+auEjBamzQkMJFNlZAIUrukL/NbUhQU37TAbwsFyz7X0E/u/VMle/nBCNAzgkMwAUjiHM6FqrKKBRWFbPrSIixjfjkCnrMEPw=
file:
- ThinkPHP_Core.zip
- ThinkPHP_Full.zip
skip_cleanup: true
on:
tags: true

32
LICENSE.txt Normal file
View File

@@ -0,0 +1,32 @@
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
版权所有Copyright © 2006-2025 by ThinkPHP (http://thinkphp.cn)
All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
Apache Licence是著名的非盈利开源组织Apache采用的协议。
该协议和BSD类似鼓励代码共享和尊重原作者的著作权
允许代码修改,再作为开源或商业软件发布。需要满足
的条件:
1 需要给代码的用户一份Apache Licence
2 如果你修改了代码,需要在被修改的文件中说明;
3 在延伸的代码中(修改和有源代码衍生的代码中)需要
带有原来代码中的协议,商标,专利声明和其他原来作者规
定需要包含的说明;
4 如果再发布的产品中包含一个Notice文件则在Notice文
件中需要带有本协议内容。你可以在Notice中增加自己的
许可但不可以表现为对Apache Licence构成更改。
具体的协议参考http://www.apache.org/licenses/LICENSE-2.0
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -1,2 +1,77 @@
# work_dhd_back_end ![](https://www.thinkphp.cn/uploads/images/20230630/300c856765af4d8ae758c503185f8739.png)
ThinkPHP 8
===============
## 特性
* 基于PHP`8.0+`重构
* 升级`PSR`依赖
* 依赖`think-orm`3.0+版本
* 全新的`think-dumper`服务,支持远程调试
* 支持`6.0`/`6.1`无缝升级
> ThinkPHP8的运行环境要求PHP8.0+
现在开始,你可以使用官方提供的[ThinkChat](https://chat.topthink.com/)让你在学习ThinkPHP的旅途中享受私人AI助理服务
![](https://www.topthink.com/uploads/assistant/20230630/4d1a3f0ad2958b49bb8189b7ef824cb0.png)
ThinkPHP生态服务由[顶想云](https://www.topthink.com)TOPThink Cloud提供为生态提供专业的开发者服务和价值之选。
## 文档
[完全开发手册](https://doc.thinkphp.cn)
## 赞助
全新的[赞助计划](https://www.thinkphp.cn/sponsor)可以让你通过我们的网站、手册、欢迎页及GIT仓库获得巨大曝光同时提升企业的品牌声誉也更好保障ThinkPHP的可持续发展。
[![](https://www.thinkphp.cn/sponsor/special.svg)](https://www.thinkphp.cn/sponsor/special)
[![](https://www.thinkphp.cn/sponsor.svg)](https://www.thinkphp.cn/sponsor)
## 安装
~~~
composer create-project topthink/think tp
~~~
启动服务
~~~
cd tp
php think run
~~~
然后就可以在浏览器中访问
~~~
http://localhost:8000
~~~
如果需要更新框架使用
~~~
composer update topthink/framework
~~~
## 命名规范
`ThinkPHP`遵循PSR-2命名规范和PSR-4自动加载规范。
## 参与开发
直接提交PR或者Issue即可
## 版权信息
ThinkPHP遵循Apache2开源协议发布并提供免费使用。
本项目包含的第三方源码和二进制文件之版权信息另行标注。
版权所有Copyright © 2006-2024 by ThinkPHP (http://thinkphp.cn) All rights reserved。
ThinkPHP® 商标和著作权所有者为上海顶想信息科技有限公司。
更多细节参阅 [LICENSE.txt](LICENSE.txt)

1
app/.htaccess Normal file
View File

@@ -0,0 +1 @@
deny from all

22
app/AppService.php Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,8 @@
<?php
namespace app;
// 应用请求对象类
class Request extends \think\Request
{
}

2
app/common.php Normal file
View File

@@ -0,0 +1,2 @@
<?php
// 应用公共文件

18
app/controller/Index.php Normal file
View 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;
}
}

View 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
View File

@@ -0,0 +1,17 @@
<?php
// 事件定义文件
return [
'bind' => [
],
'listen' => [
'AppInit' => [],
'HttpRun' => [],
'HttpEnd' => [],
'LogLevel' => [],
'LogWrite' => [],
],
'subscribe' => [
],
];

10
app/middleware.php Normal file
View 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
View 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
View File

@@ -0,0 +1,9 @@
<?php
use app\AppService;
// 系统服务定义文件
// 服务在完成全局初始化之后执行
return [
AppService::class,
];

View 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;
}
}

49
composer.json Normal file
View File

@@ -0,0 +1,49 @@
{
"name": "topthink/think",
"description": "the new thinkphp framework",
"type": "project",
"keywords": [
"framework",
"thinkphp",
"ORM"
],
"homepage": "https://www.thinkphp.cn/",
"license": "Apache-2.0",
"authors": [
{
"name": "liu21st",
"email": "liu21st@gmail.com"
},
{
"name": "yunwuxin",
"email": "448901948@qq.com"
}
],
"require": {
"php": ">=8.0.0",
"topthink/framework": "^8.0",
"topthink/think-orm": "^3.0|^4.0",
"topthink/think-filesystem": "^2.0|^3.0"
},
"require-dev": {
"topthink/think-dumper": "^1.0",
"topthink/think-trace": "^2.0"
},
"autoload": {
"psr-4": {
"app\\": "app"
},
"psr-0": {
"": "extend/"
}
},
"config": {
"preferred-install": "dist"
},
"scripts": {
"post-autoload-dump": [
"@php think service:discover",
"@php think vendor:publish"
]
}
}

30
config/app.php Normal file
View File

@@ -0,0 +1,30 @@
<?php
// +----------------------------------------------------------------------
// | 应用设置
// +----------------------------------------------------------------------
return [
// 应用的命名空间
'app_namespace' => '',
// 是否启用路由
'with_route' => true,
// 默认应用
'default_app' => 'index',
// 默认时区
'default_timezone' => 'Asia/Shanghai',
// 应用映射(自动多应用模式有效)
'app_map' => [],
// 域名绑定(自动多应用模式有效)
'domain_bind' => [],
// 禁止URL访问的应用列表自动多应用模式有效
'deny_app_list' => [],
// 异常页面的模板文件
'exception_tmpl' => app()->getThinkPath() . 'tpl/think_exception.tpl',
// 错误显示信息,非调试模式有效
'error_message' => '页面错误!请稍后再试~',
// 显示错误信息
'show_error_msg' => false,
];

29
config/cache.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 缓存设置
// +----------------------------------------------------------------------
return [
// 默认缓存驱动
'default' => 'file',
// 缓存连接方式配置
'stores' => [
'file' => [
// 驱动方式
'type' => 'File',
// 缓存保存目录
'path' => '',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
// 缓存标签前缀
'tag_prefix' => 'tag:',
// 序列化机制 例如 ['serialize', 'unserialize']
'serialize' => [],
],
// 更多的缓存连接
],
];

9
config/console.php Normal file
View File

@@ -0,0 +1,9 @@
<?php
// +----------------------------------------------------------------------
// | 控制台配置
// +----------------------------------------------------------------------
return [
// 指令定义
'commands' => [
],
];

20
config/cookie.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | Cookie设置
// +----------------------------------------------------------------------
return [
// cookie 保存时间
'expire' => 0,
// cookie 保存路径
'path' => '/',
// cookie 有效域名
'domain' => '',
// cookie 启用安全传输
'secure' => false,
// httponly设置
'httponly' => false,
// 是否使用 setcookie
'setcookie' => true,
// samesite 设置,支持 'strict' 'lax'
'samesite' => '',
];

63
config/database.php Normal file
View File

@@ -0,0 +1,63 @@
<?php
return [
// 默认使用的数据库连接配置
'default' => env('DB_DRIVER', 'mysql'),
// 自定义时间查询规则
'time_query_rule' => [],
// 自动写入时间戳字段
// true为自动识别类型 false关闭
// 字符串则明确指定时间字段类型 支持 int timestamp datetime date
'auto_timestamp' => true,
// 时间字段取出后的默认时间格式
'datetime_format' => 'Y-m-d H:i:s',
// 时间字段配置 配置格式create_time,update_time
'datetime_field' => '',
// 数据库连接配置信息
'connections' => [
'mysql' => [
// 数据库类型
'type' => env('DB_TYPE', 'mysql'),
// 服务器地址
'hostname' => env('DB_HOST', '127.0.0.1'),
// 数据库名
'database' => env('DB_NAME', ''),
// 用户名
'username' => env('DB_USER', 'root'),
// 密码
'password' => env('DB_PASS', ''),
// 端口
'hostport' => env('DB_PORT', '3306'),
// 数据库连接参数
'params' => [],
// 数据库编码
'charset' => env('DB_CHARSET', 'utf8mb4'),
// 数据库表前缀
'prefix' => env('DB_PREFIX', ''),
// 数据库部署方式:0 集中式(单一服务器),1 分布式(主从服务器)
'deploy' => 0,
// 数据库读写是否分离 主从式有效
'rw_separate' => false,
// 读写分离后 主服务器数量
'master_num' => 1,
// 指定从服务器序号
'slave_no' => '',
// 是否严格检查字段是否存在
'fields_strict' => true,
// 是否需要断线重连
'break_reconnect' => false,
// 监听SQL
'trigger_sql' => env('APP_DEBUG', true),
// 开启字段缓存
'fields_cache' => false,
],
// 更多的数据库配置信息
],
];

24
config/filesystem.php Normal file
View File

@@ -0,0 +1,24 @@
<?php
return [
// 默认磁盘
'default' => 'local',
// 磁盘列表
'disks' => [
'local' => [
'type' => 'local',
'root' => app()->getRuntimePath() . 'storage',
],
'public' => [
// 磁盘类型
'type' => 'local',
// 磁盘路径
'root' => app()->getRootPath() . 'public/storage',
// 磁盘路径对应的外部URL路径
'url' => '/storage',
// 可见性
'visibility' => 'public',
],
// 更多的磁盘配置信息
],
];

29
config/lang.php Normal file
View File

@@ -0,0 +1,29 @@
<?php
// +----------------------------------------------------------------------
// | 多语言设置
// +----------------------------------------------------------------------
return [
// 默认语言
'default_lang' => env('DEFAULT_LANG', 'zh-cn'),
// 自动侦测浏览器语言
'auto_detect_browser' => true,
// 允许的语言列表
'allow_lang_list' => [],
// 多语言自动侦测变量名
'detect_var' => 'lang',
// 是否使用Cookie记录
'use_cookie' => true,
// 多语言cookie变量
'cookie_var' => 'think_lang',
// 多语言header变量
'header_var' => 'think-lang',
// 扩展语言包
'extend_list' => [],
// Accept-Language转义为对应语言包名称
'accept_language' => [
'zh-hans-cn' => 'zh-cn',
],
// 是否支持语言分组
'allow_group' => false,
];

45
config/log.php Normal file
View File

@@ -0,0 +1,45 @@
<?php
// +----------------------------------------------------------------------
// | 日志设置
// +----------------------------------------------------------------------
return [
// 默认日志记录通道
'default' => 'file',
// 日志记录级别
'level' => [],
// 日志类型记录的通道 ['error'=>'email',...]
'type_channel' => [],
// 关闭全局日志写入
'close' => false,
// 全局日志处理 支持闭包
'processor' => null,
// 日志通道列表
'channels' => [
'file' => [
// 日志记录方式
'type' => 'File',
// 日志保存目录
'path' => '',
// 单文件日志写入
'single' => false,
// 独立日志级别
'apart_level' => [],
// 最大日志文件数量
'max_files' => 0,
// 使用JSON格式记录
'json' => false,
// 日志处理
'processor' => null,
// 关闭通道日志写入
'close' => false,
// 日志输出格式化
'format' => '[%s][%s] %s',
// 是否实时写入
'realtime_write' => false,
],
// 其它日志通道配置
],
];

8
config/middleware.php Normal file
View File

@@ -0,0 +1,8 @@
<?php
// 中间件配置
return [
// 别名或分组
'alias' => [],
// 优先级设置,此数组中的中间件会按照数组中的顺序优先执行
'priority' => [],
];

55
config/route.php Normal file
View File

@@ -0,0 +1,55 @@
<?php
// +----------------------------------------------------------------------
// | 路由设置
// +----------------------------------------------------------------------
return [
// pathinfo分隔符
'pathinfo_depr' => '/',
// 是否开启路由延迟解析
'url_lazy_route' => false,
// 是否强制使用路由
'url_route_must' => false,
// 是否区分大小写
'url_case_sensitive' => false,
// 自动扫描子目录分组
'route_auto_group' => false,
// 合并路由规则
'route_rule_merge' => false,
// 路由是否完全匹配
'route_complete_match' => false,
// 去除斜杠
'remove_slash' => false,
// 默认的路由变量规则
'default_route_pattern' => '[\w\.]+',
// URL伪静态后缀
'url_html_suffix' => 'html',
// 访问控制器层名称
'controller_layer' => 'controller',
// 空控制器名
'empty_controller' => 'Error',
// 是否使用控制器后缀
'controller_suffix' => false,
// 默认模块名(开启自动多模块有效)
'default_module' => 'index',
// 默认控制器名
'default_controller' => 'Index',
// 默认操作名
'default_action' => 'index',
// 操作方法后缀
'action_suffix' => '',
// 非路由变量是否使用普通参数方式用于URL生成
'url_common_param' => true,
// 操作方法的参数绑定方式 route get param
'action_bind_param' => 'get',
// 请求缓存规则 true为自动规则
'request_cache_key' => true,
// 请求缓存有效期
'request_cache_expire' => null,
// 全局请求缓存排除规则
'request_cache_except' => [],
// 请求缓存的Tag
'request_cache_tag' => '',
// API版本header变量
'api_version' => 'Api-Version',
];

19
config/session.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | 会话设置
// +----------------------------------------------------------------------
return [
// session name
'name' => 'PHPSESSID',
// SESSION_ID的提交变量,解决flash上传跨域
'var_session_id' => '',
// 驱动方式 支持file cache
'type' => 'file',
// 存储连接标识 当type使用cache的时候有效
'store' => null,
// 过期时间
'expire' => 1440,
// 前缀
'prefix' => '',
];

10
config/trace.php Normal file
View File

@@ -0,0 +1,10 @@
<?php
// +----------------------------------------------------------------------
// | Trace设置 开启调试模式后有效
// +----------------------------------------------------------------------
return [
// 内置Html和Console两种方式 支持扩展
'type' => 'Html',
// 读取的日志通道名
'channel' => '',
];

25
config/view.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | 模板设置
// +----------------------------------------------------------------------
return [
// 模板引擎类型使用Think
'type' => 'Think',
// 默认模板渲染规则 1 解析为小写+下划线 2 全部转换小写 3 保持操作方法
'auto_rule' => 1,
// 模板目录名
'view_dir_name' => 'view',
// 模板后缀
'view_suffix' => 'html',
// 模板文件名分隔符
'view_depr' => DIRECTORY_SEPARATOR,
// 模板引擎普通标签开始标记
'tpl_begin' => '{',
// 模板引擎普通标签结束标记
'tpl_end' => '}',
// 标签库标签开始标记
'taglib_begin' => '{',
// 标签库标签结束标记
'taglib_end' => '}',
];

View File

@@ -0,0 +1,241 @@
# 岗位简历匹配度接口说明
## 接口地址
```
POST /match/calculate
```
## 接口说明
该接口用于计算岗位信息和简历信息的匹配度,采用**硬性条件一票否决 + 软性条件加分**的双层匹配机制,符合公考职位匹配的实际需求。
**返回结果**仅返回匹配度分数0-100分硬性条件不满足时返回0分。
## 匹配机制说明
### 匹配流程
1. **硬性条件检查(一票否决)**
- 首先检查硬性条件,如学历、学位、年龄、专业、性别等
- 任一硬性条件不满足直接返回0分不进行软性条件评分
- 所有硬性条件满足后,进入软性条件评分
2. **软性条件评分100分制**
- 在通过硬性条件筛选的基础上,对软性条件进行评分
- 评分维度包括:专业匹配度、学历层次匹配度、专业资格条件、基层工作经历、其他条件等
- 最终得分范围为 0-100 分
## 请求参数
### 请求方式
- **方法**: POST
- **Content-Type**: application/json 或 application/x-www-form-urlencoded
### 请求参数说明
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| position | object | 是 | 岗位信息对象 |
| resume | object | 是 | 简历信息对象 |
### 请求示例
#### JSON格式请求
```json
{
"position": {
"id": 1,
"base_info": {
"岗位名称": "人工智能与大数据侦察职位一",
"招考单位": "市级公安机关",
"招录人数": "10",
"岗位代码": "45150001"
},
"position_info": {
"岗位名称": "人工智能与大数据侦察职位一",
"招考单位": "市级公安机关",
"招录人数": "10",
"岗位代码": "45150001"
},
"position_require": {
"学历要求": "本科及以上",
"学位要求": "学士及以上",
"年龄要求": "18周岁以上、35周岁以下。",
"专业(学科)类别": "计算机科学与技术类,电气、电子及自动化类",
"其他资格条件": "适合男性。符合人民警察录用条件。",
"专业资格条件": "曾参加人工智能、大数据、计算机领域竞赛,获个人三等奖或团体三等奖及以上。"
}
},
"resume": {
"user_id": 527,
"birth_date": "1995-03-01",
"gender": "男",
"work_experience": "3年基层工作年限",
"education": [
{
"education_level": "硕士研究生",
"degree": "硕士",
"majors_name": "计算机科学与技术"
}
]
}
}
```
## 响应参数
### 成功响应
```json
{
"code": 200,
"msg": "计算成功",
"data": {
"match_score": 90
}
}
```
### 错误响应
```json
{
"code": 400,
"msg": "参数错误:岗位信息和简历信息不能为空",
"data": null
}
```
## 匹配度分数说明
### 分数含义
- **0分**:硬性条件不满足(如年龄超限、专业不匹配、学历不达标等),一票否决
- **1-100分**:通过硬性条件筛选后的软性条件匹配度
### 硬性条件(一票否决)
以下条件不满足时直接返回0分
1. **学历要求**:必须达到或超过岗位要求的学历
2. **学位要求**:必须达到或超过岗位要求的学位
3. **年龄要求**:必须在岗位要求的年龄范围内
4. **专业要求**:专业必须属于岗位要求的专业类别
5. **性别要求**:如果岗位明确要求性别,必须匹配
### 软性条件评分规则100分制
在通过硬性条件筛选后,进行软性条件评分:
1. **专业匹配度40分**
- 专业完全匹配40分
- 专业部分匹配:根据匹配程度评分
2. **学历层次匹配度20分**
- 刚好满足要求15分
- 超过要求一级20分
- 超过要求两级及以上20分
3. **专业资格条件20分**
- 根据专业竞赛获奖、证书等情况评分
- 无要求时给满分
4. **基层工作经历10分**
- 有基层工作经历10分
- 无基层工作经历0分
5. **其他条件10分**
- 根据政治面貌、特殊身份等其他条件评分
- 无要求时给满分
## 使用示例
### 示例1硬性条件不满足
**请求:**
```json
{
"position": {
"position_require": {
"年龄要求": "18周岁以上、35周岁以下。",
"专业(学科)类别": "计算机科学与技术类"
}
},
"resume": {
"birth_date": "1989-03-01",
"education": [
{
"majors_name": "逻辑学"
}
]
}
}
```
**响应:**
```json
{
"code": 200,
"msg": "计算成功",
"data": {
"match_score": 0
}
}
```
**说明**年龄36岁超过35岁上限或专业不匹配硬性条件不满足返回0分。
### 示例2通过硬性条件
**请求:**
```json
{
"position": {
"position_require": {
"学历要求": "本科及以上",
"年龄要求": "18周岁以上、35周岁以下。",
"专业(学科)类别": "计算机科学与技术类"
}
},
"resume": {
"birth_date": "1995-03-01",
"work_experience": "3年基层工作年限",
"education": [
{
"education_level": "硕士研究生",
"degree": "硕士",
"majors_name": "计算机科学与技术"
}
]
}
}
```
**响应:**
```json
{
"code": 200,
"msg": "计算成功",
"data": {
"match_score": 100
}
}
```
**说明**所有硬性条件满足软性条件评分100分。
## 注意事项
1. **硬性条件优先**硬性条件不满足时直接返回0分不进行软性条件评分
2. **匹配度分数**只有在通过硬性条件筛选后才会计算匹配度分数0-100分
3. **数据结构**:专业资格条件评分需要简历中包含相关证书、获奖等信息,当前数据结构暂不支持,需要扩展
4. **专业匹配**:专业匹配基于关键词匹配,实际应用中可能需要更专业的专业分类映射表
## 使用场景
该接口适用于:
- 公考职位与考生简历匹配
- 事业单位招聘匹配
- 其他需要硬性条件筛选的招聘场景

2
extend/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

8
public/.htaccess Normal file
View File

@@ -0,0 +1,8 @@
<IfModule mod_rewrite.c>
Options +FollowSymlinks -Multiviews
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

25
public/index.php Normal file
View File

@@ -0,0 +1,25 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\App;
// [ 应用入口文件 ]
require __DIR__ . '/../vendor/autoload.php';
// 执行HTTP应用并响应
$http = (new App())->http;
$response = $http->run();
$response->send();
$http->end($response);

2
public/robots.txt Normal file
View File

@@ -0,0 +1,2 @@
User-agent: *
Disallow:

19
public/router.php Normal file
View File

@@ -0,0 +1,19 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
// $Id$
if (is_file($_SERVER["DOCUMENT_ROOT"] . $_SERVER["SCRIPT_NAME"])) {
return false;
} else {
$_SERVER["SCRIPT_FILENAME"] = __DIR__ . '/index.php';
require __DIR__ . "/index.php";
}

2
public/static/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

20
route/app.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2018 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
use think\facade\Route;
Route::get('think', function () {
return 'hello,ThinkPHP8!';
});
Route::get('hello/:name', 'index/hello');
// 岗位简历匹配度计算接口
Route::post('match/calculate', 'match/calculate');

2
runtime/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
*
!.gitignore

123
test_match_new.php Normal file
View File

@@ -0,0 +1,123 @@
<?php
// 测试新的岗位简历匹配度计算机制
require __DIR__ . '/vendor/autoload.php';
// 岗位信息
$position = [
"id" => 1,
"base_info" => [
"岗位名称" => "人工智能与大数据侦察职位一",
"招考单位" => "市级公安机关",
"招录人数" => "10",
"岗位代码" => "45150001"
],
"position_info" => [
"岗位名称" => "人工智能与大数据侦察职位一",
"招考单位" => "市级公安机关",
"招录人数" => "10",
"岗位代码" => "45150001",
"备注" => "考生按照总成绩由高到低的顺序依次在对应的职位计划表中选择具体工作岗位。",
"招录机关" => "自治区公安厅",
"职位序号" => "1",
"职位简介" => "从事公安机关人工智能研发、大数据系统建设管理、数据信息挖掘及分析研判等专业技术工作。"
],
"position_require" => [
"学历要求" => "本科及以上",
"学位要求" => "学士及以上",
"年龄要求" => "18周岁以上、35周岁以下。",
"回避要求" => "报考人员不得报考与市公安局领导班子成员、内设机构领导班子成员(如刑侦支队支队长、副支队长等)存在规定回避情形的职位(回避情形含夫妻关系、直系血亲关系、三代以内旁系血亲关系以及近姻亲关系)。",
"专业资格条件" => "曾参加人工智能、大数据、计算机领域竞赛,获个人三等奖或团体三等奖及以上,提供官方证明文件。",
"其他资格条件" => "适合男性。符合人民警察录用条件。单侧矫正视力低于5.0不合格。",
"专业(学科)类别" => "计算机科学与技术类,电气、电子及自动化类"
]
];
// 简历信息
$resume = [
"user_id" => 527,
"birth_date" => "1989-03-01",
"gender" => "",
"ethnicity" => "汉族",
"work_experience" => "无基层工作年限",
"education" => [
[
"id" => 1,
"education_level" => "普通本科",
"degree" => "学士",
"majors_name" => "逻辑学"
],
[
"id" => 2,
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_name" => "伦理学"
]
]
];
// 加载ThinkPHP框架
$app = new think\App();
$app->initialize();
// 创建匹配服务实例
$matchService = new app\service\MatchService();
// 计算匹配度
$result = $matchService->calculateMatchScore($position, $resume);
// 输出结果
echo "========================================\n";
echo "岗位简历匹配度测试结果(新机制)\n";
echo "========================================\n\n";
echo "【硬性条件检查结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "✓ 通过硬性条件筛选\n\n";
foreach ($result['hard_requirements']['details'] as $key => $detail) {
if (isset($detail['passed'])) {
$status = $detail['passed'] ? '✓' : '✗';
echo "{$status} {$key}: ";
if ($detail['passed']) {
echo "通过 (要求: {$detail['required']}, 实际: {$detail['actual']})\n";
} else {
echo "不通过 - {$detail['reason']}\n";
}
}
}
} else {
echo "✗ 未通过硬性条件筛选\n\n";
echo "不通过原因:\n";
foreach ($result['rejection_reasons'] as $reason) {
echo " - {$reason}\n";
}
echo "\n详细检查结果:\n";
foreach ($result['hard_requirements']['details'] as $key => $detail) {
if (isset($detail['passed'])) {
$status = $detail['passed'] ? '✓' : '✗';
echo "{$status} {$key}: ";
if ($detail['passed']) {
echo "通过 (要求: {$detail['required']}, 实际: {$detail['actual']})\n";
} else {
echo "不通过 - {$detail['reason']}\n";
}
}
}
}
echo "\n【软性条件评分结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "总分: {$result['score']}/{$result['soft_requirements']['max_score']}\n\n";
foreach ($result['soft_requirements']['details'] as $key => $detail) {
echo "{$key}: {$detail['score']}/{$detail['max_score']}分 - {$detail['reason']}\n";
}
} else {
echo "(未通过硬性条件筛选,不进行软性条件评分)\n";
}
echo "\n========================================\n";
echo "最终结果: " . ($result['qualified'] ? "✓ 符合条件,匹配度 {$result['score']}/100分" : "✗ 不符合条件") . "\n";
echo "========================================\n";

82
test_match_pass.php Normal file
View File

@@ -0,0 +1,82 @@
<?php
// 测试通过硬性条件的案例
require __DIR__ . '/vendor/autoload.php';
// 岗位信息
$position = [
"position_require" => [
"学历要求" => "本科及以上",
"学位要求" => "学士及以上",
"年龄要求" => "18周岁以上、35周岁以下。",
"其他资格条件" => "适合男性。",
"专业(学科)类别" => "计算机科学与技术类,电气、电子及自动化类"
]
];
// 简历信息(符合硬性条件)
$resume = [
"birth_date" => "1995-03-01", // 29岁符合年龄要求
"gender" => "",
"work_experience" => "3年基层工作年限",
"education" => [
[
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_name" => "计算机科学与技术"
]
]
];
// 加载ThinkPHP框架
$app = new think\App();
$app->initialize();
// 创建匹配服务实例
$matchService = new app\service\MatchService();
// 计算匹配度
$result = $matchService->calculateMatchScore($position, $resume);
// 输出结果
echo "========================================\n";
echo "岗位简历匹配度测试(通过硬性条件案例)\n";
echo "========================================\n\n";
echo "【硬性条件检查结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "✓ 通过硬性条件筛选\n\n";
foreach ($result['hard_requirements']['details'] as $key => $detail) {
if (isset($detail['passed'])) {
$status = $detail['passed'] ? '✓' : '✗';
echo "{$status} {$key}: ";
if ($detail['passed']) {
echo "通过 (要求: {$detail['required']}, 实际: {$detail['actual']})\n";
} else {
echo "不通过 - {$detail['reason']}\n";
}
}
}
} else {
echo "✗ 未通过硬性条件筛选\n";
foreach ($result['rejection_reasons'] as $reason) {
echo " - {$reason}\n";
}
}
echo "\n【软性条件评分结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "总分: {$result['score']}/{$result['soft_requirements']['max_score']}\n\n";
foreach ($result['soft_requirements']['details'] as $key => $detail) {
echo "{$key}: {$detail['score']}/{$detail['max_score']}分 - {$detail['reason']}\n";
}
} else {
echo "(未通过硬性条件筛选,不进行软性条件评分)\n";
}
echo "\n========================================\n";
echo "最终结果: " . ($result['qualified'] ? "✓ 符合条件,匹配度 {$result['score']}/100分" : "✗ 不符合条件") . "\n";
echo "========================================\n";

198
test_match_real.php Normal file
View File

@@ -0,0 +1,198 @@
<?php
// 使用简历-岗位.md中的真实数据测试
require __DIR__ . '/vendor/autoload.php';
// 岗位信息(从简历-岗位.md
$position = [
"id" => 1,
"base_info" => [
"岗位名称" => "人工智能与大数据侦察职位一",
"招考单位" => "市级公安机关",
"招录人数" => "10",
"岗位代码" => "45150001"
],
"position_info" => [
"岗位名称" => "人工智能与大数据侦察职位一",
"招考单位" => "市级公安机关",
"招录人数" => "10",
"岗位代码" => "45150001",
"备注" => "考生按照总成绩由高到低的顺序依次在对应的职位计划表中选择具体工作岗位。",
"招录机关" => "自治区公安厅",
"职位序号" => "1",
"职位简介" => "从事公安机关人工智能研发、大数据系统建设管理、数据信息挖掘及分析研判等专业技术工作。"
],
"position_require" => [
"学历要求" => "本科及以上",
"学位要求" => "学士及以上",
"年龄要求" => "18周岁以上、35周岁以下。",
"回避要求" => "报考人员不得报考与市公安局领导班子成员、内设机构领导班子成员(如刑侦支队支队长、副支队长等)存在规定回避情形的职位(回避情形含夫妻关系、直系血亲关系、三代以内旁系血亲关系以及近姻亲关系)。",
"专业资格条件" => "曾参加人工智能、大数据、计算机领域竞赛,获个人三等奖或团体三等奖及以上,提供官方证明文件。",
"其他资格条件" => "适合男性。符合人民警察录用条件。单侧矫正视力低于5.0不合格。",
"专业(学科)类别" => "计算机科学与技术类,电气、电子及自动化类"
]
];
// 简历信息(从简历-岗位.md
$resume = [
"user_id" => 527,
"exam_city" => [
[
"name" => "贵州省",
"child" => [
[
"name" => "贵阳市",
"city_id" => 604132
],
[
"name" => "六盘水市",
"city_id" => 605834
],
[
"name" => "遵义市",
"city_id" => 607029
]
],
"city_id" => 604131
]
],
"exam_type_id" => [3, 8],
"birth_date" => "1989-03-01",
"gender" => "",
"ethnicity" => "汉族",
"city_id" => 609311,
"origin_city_id" => 609311,
"political_status" => "群众",
"special_identity" => "无特殊身份/其他特特身份",
"is_young" => "",
"grassroots_program" => "",
"work_experience" => "无基层工作年限",
"exam_type_name" => ["事业单位", "公务员"],
"city_name" => "贵州,安顺,西秀",
"origin_city_name" => "贵州,安顺,西秀",
"education" => [
[
"id" => 1,
"user_id" => 527,
"education_level" => "普通本科",
"degree" => "学士",
"majors_id" => 1016,
"education_type" => "全日制",
"graduate_date" => "2012-07-01",
"majors_name" => "逻辑学"
],
[
"id" => 2,
"user_id" => 527,
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_id" => 513,
"education_type" => "全日制",
"graduate_date" => "2015-07-01",
"majors_name" => "伦理学"
]
]
];
// 加载ThinkPHP框架
$app = new think\App();
$app->initialize();
// 创建匹配服务实例
$matchService = new app\service\MatchService();
// 计算匹配度
$result = $matchService->calculateMatchScore($position, $resume);
// 输出结果
echo "========================================\n";
echo "岗位简历匹配度测试(真实数据)\n";
echo "========================================\n\n";
echo "【岗位信息】\n";
echo "----------------------------------------\n";
echo "岗位名称: " . $position['base_info']['岗位名称'] . "\n";
echo "招考单位: " . $position['base_info']['招考单位'] . "\n";
echo "学历要求: " . $position['position_require']['学历要求'] . "\n";
echo "学位要求: " . $position['position_require']['学位要求'] . "\n";
echo "年龄要求: " . $position['position_require']['年龄要求'] . "\n";
echo "专业要求: " . $position['position_require']['专业(学科)类别'] . "\n";
echo "其他条件: " . $position['position_require']['其他资格条件'] . "\n\n";
echo "【简历信息】\n";
echo "----------------------------------------\n";
$highestEdu = null;
$highestLevel = 0;
$educationLevels = ['普通本科' => 3, '硕士研究生' => 4, '博士研究生' => 5];
foreach ($resume['education'] as $edu) {
$level = $educationLevels[$edu['education_level']] ?? 0;
if ($level > $highestLevel) {
$highestLevel = $level;
$highestEdu = $edu;
}
}
echo "最高学历: " . ($highestEdu['education_level'] ?? '未知') . "\n";
echo "最高学位: " . ($highestEdu['degree'] ?? '未知') . "\n";
echo "专业: ";
foreach ($resume['education'] as $edu) {
echo $edu['majors_name'] . " ";
}
echo "\n";
$age = date('Y') - date('Y', strtotime($resume['birth_date']));
if (date('md') < date('md', strtotime($resume['birth_date']))) {
$age--;
}
echo "年龄: {$age}岁 (出生日期: {$resume['birth_date']})\n";
echo "性别: " . $resume['gender'] . "\n";
echo "基层工作经历: " . $resume['work_experience'] . "\n\n";
echo "【硬性条件检查结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "✓ 通过硬性条件筛选\n\n";
foreach ($result['hard_requirements']['details'] as $key => $detail) {
if (isset($detail['passed'])) {
$status = $detail['passed'] ? '✓' : '✗';
echo "{$status} {$key}: ";
if ($detail['passed']) {
echo "通过 (要求: {$detail['required']}, 实际: {$detail['actual']})\n";
} else {
echo "不通过 - {$detail['reason']}\n";
}
}
}
} else {
echo "✗ 未通过硬性条件筛选\n\n";
echo "不通过原因:\n";
foreach ($result['rejection_reasons'] as $reason) {
echo " - {$reason}\n";
}
echo "\n详细检查结果:\n";
foreach ($result['hard_requirements']['details'] as $key => $detail) {
if (isset($detail['passed'])) {
$status = $detail['passed'] ? '✓' : '✗';
echo "{$status} {$key}: ";
if ($detail['passed']) {
echo "通过 (要求: {$detail['required']}, 实际: {$detail['actual']})\n";
} else {
echo "不通过 - {$detail['reason']}\n";
}
}
}
}
echo "\n【软性条件评分结果】\n";
echo "----------------------------------------\n";
if ($result['qualified']) {
echo "总分: {$result['score']}/{$result['soft_requirements']['max_score']}\n\n";
foreach ($result['soft_requirements']['details'] as $key => $detail) {
echo "{$key}: {$detail['score']}/{$detail['max_score']}分 - {$detail['reason']}\n";
}
} else {
echo "(未通过硬性条件筛选,不进行软性条件评分)\n";
}
echo "\n========================================\n";
echo "最终结果: " . ($result['qualified'] ? "✓ 符合条件,匹配度 {$result['score']}/100分" : "✗ 不符合条件") . "\n";
echo "========================================\n";

80
test_match_simple.php Normal file
View File

@@ -0,0 +1,80 @@
<?php
// 测试简化后的匹配度接口(只返回分数)
require __DIR__ . '/vendor/autoload.php';
// 岗位信息
$position = [
"position_require" => [
"学历要求" => "本科及以上",
"学位要求" => "学士及以上",
"年龄要求" => "18周岁以上、35周岁以下。",
"其他资格条件" => "适合男性。",
"专业(学科)类别" => "计算机科学与技术类,电气、电子及自动化类"
]
];
// 简历信息1不通过硬性条件年龄超限、专业不匹配
$resume1 = [
"birth_date" => "1989-03-01", // 36岁
"gender" => "",
"work_experience" => "无基层工作年限",
"education" => [
[
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_name" => "逻辑学"
],
[
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_name" => "伦理学"
]
]
];
// 简历信息2通过硬性条件
$resume2 = [
"birth_date" => "1995-03-01", // 30岁
"gender" => "",
"work_experience" => "3年基层工作年限",
"education" => [
[
"education_level" => "硕士研究生",
"degree" => "硕士",
"majors_name" => "计算机科学与技术"
]
]
];
// 加载ThinkPHP框架
$app = new think\App();
$app->initialize();
// 创建匹配服务实例
$matchService = new app\service\MatchService();
echo "========================================\n";
echo "岗位简历匹配度测试(简化版 - 只返回分数)\n";
echo "========================================\n\n";
// 测试案例1不通过硬性条件
echo "【测试案例1不通过硬性条件】\n";
echo "----------------------------------------\n";
echo "简历信息36岁专业逻辑学、伦理学\n";
$score1 = $matchService->calculateMatchScore($position, $resume1);
echo "匹配度分数: {$score1}/100分\n";
echo "说明: 硬性条件不满足年龄超限、专业不匹配返回0分\n\n";
// 测试案例2通过硬性条件
echo "【测试案例2通过硬性条件】\n";
echo "----------------------------------------\n";
echo "简历信息30岁专业计算机科学与技术有基层工作经历\n";
$score2 = $matchService->calculateMatchScore($position, $resume2);
echo "匹配度分数: {$score2}/100分\n";
echo "说明: 通过硬性条件筛选,进行软性条件评分\n\n";
echo "========================================\n";
echo "测试完成\n";
echo "========================================\n";

11
think Normal file
View File

@@ -0,0 +1,11 @@
#!/usr/bin/env php
<?php
use think\App;
// 命令行入口文件
// 加载基础文件
require __DIR__ . '/vendor/autoload.php';
// 应用初始化
(new App())->console->run();

1
view/README.md Normal file
View File

@@ -0,0 +1 @@
如果不使用模板,可以删除该目录

100
简历-岗位.md Normal file
View File

@@ -0,0 +1,100 @@
岗位信息如下:
```json
{
"id": 1,
"base_info": {
"岗位名称": "人工智能与大数据侦察职位一",
"招考单位": "市级公安机关",
"招录人数": "10",
"岗位代码": "45150001"
},
"position_info": {
"岗位名称": "人工智能与大数据侦察职位一",
"招考单位": "市级公安机关",
"招录人数": "10",
"岗位代码": "45150001",
"备注": "考生按照总成绩由高到低的顺序依次在对应的职位计划表中选择具体工作岗位。",
"招录机关": "自治区公安厅",
"职位序号": "1",
"职位简介": "从事公安机关人工智能研发、大数据系统建设管理、数据信息挖掘及分析研判等专业技术工作。"
},
"position_require": {
"学历要求": "本科及以上",
"学位要求": "学士及以上",
"年龄要求": "18周岁以上、35周岁以下。",
"回避要求": "报考人员不得报考与市公安局领导班子成员、内设机构领导班子成员(如刑侦支队支队长、副支队长等)存在规定回避情形的职位(回避情形含夫妻关系、直系血亲关系、三代以内旁系血亲关系以及近姻亲关系)。",
"专业资格条件": "曾参加人工智能、大数据、计算机领域竞赛,获个人三等奖或团体三等奖及以上,提供官方证明文件。大赛赛项包括但不限于以下类型:\n一是由省级及以上党委、政府及其组成部门如网信办、教育厅、公安厅、工信厅、总工会等主办或指导举办的大赛如全国人工智能大赛NAIC、“兴智杯”全国人工智能创新应用大赛、中国人工智能大赛、“数境杯”数据智能创新应用大赛、中国国际大学生创新大赛、“挑战杯”全国大学生课外学术科技作品竞赛、省级职业技能大赛等\n二是由科研机构主办或指导举办的大赛如全国大学生大数据分析技术技能大赛、中国高校计算机大赛-人工智能创意赛、中国研究生人工智能创新大赛、“美亚杯”中国电子数据取证大赛、“数证杯”电子数据取证分析大赛、“智警杯”大数据技能竞赛、全国大学生开源情报数据采集与分析大赛等;\n三是由大学联合主办的大赛如中国高校计算机大赛-大数据挑战赛、IKCEST“一带一路”国际大数据竞赛、CCF大数据与计算智能大赛简称CCF BDCI\n四是由企业主办类如阿里云天池大数据竞赛、百度AI开发者大会——百度人工智能安全对抗赛、腾讯广告算法大赛等。",
"其他资格条件": "适合男性。符合人民警察录用条件。单侧矫正视力低于5.0不合格。",
"专业(学科)类别": "计算机科学与技术类,电气、电子及自动化类"
}
}
```
简历信息如下:
```json
{
"user_id": 527,
"exam_city": [
{
"name": "贵州省",
"child": [
{
"name": "贵阳市",
"city_id": 604132
},
{
"name": "六盘水市",
"city_id": 605834
},
{
"name": "遵义市",
"city_id": 607029
}
],
"city_id": 604131
}
],
"exam_type_id": [
3,
8
],
"birth_date": "1989-03-01",
"gender": "男",
"ethnicity": "汉族",
"city_id": 609311,
"origin_city_id": 609311,
"political_status": "群众",
"special_identity": "无特殊身份/其他特特身份",
"is_young": "否",
"grassroots_program": "无",
"work_experience": "无基层工作年限",
"exam_type_name": [
"事业单位",
"公务员"
],
"city_name": "贵州,安顺,西秀",
"origin_city_name": "贵州,安顺,西秀",
"education": [
{
"id": 1,
"user_id": 527,
"education_level": "普通本科",
"degree": "学士",
"majors_id": 1016,
"education_type": "全日制",
"graduate_date": "2012-07-01",
"majors_name": "逻辑学"
},
{
"id": 2,
"user_id": 527,
"education_level": "硕士研究生",
"degree": "硕士",
"majors_id": 513,
"education_type": "全日制",
"graduate_date": "2015-07-01",
"majors_name": "伦理学"
}
]
}
```