文章目录[隐藏]
详细指南:在WordPress中开发集成在线简历生成与职位推荐工具
摘要
本文提供了一份全面的技术指南,详细介绍了如何在WordPress平台中通过代码二次开发,创建一个集在线简历生成与智能职位推荐功能于一体的专业工具。我们将从系统架构设计开始,逐步深入到具体实现步骤,包括数据库设计、前端界面开发、核心功能实现以及系统优化等方面。本指南适合有一定WordPress开发经验的中级开发者,旨在帮助您构建一个功能完善、用户体验优秀的职业服务平台。
一、项目概述与需求分析
1.1 项目背景与目标
在当今数字化招聘市场中,求职者需要一个能够快速创建专业简历并获取个性化职位推荐的平台。而招聘方则希望找到与职位要求高度匹配的候选人。通过WordPress这一广泛使用的内容管理系统,我们可以开发一个集成这两大功能的工具,为用户提供一站式职业发展服务。
本项目的主要目标包括:
- 开发一个用户友好的在线简历创建和编辑系统
- 实现基于用户技能和经验的智能职位推荐算法
- 创建可自定义的简历模板系统
- 确保数据安全性和用户隐私保护
- 提供响应式设计,支持多设备访问
1.2 功能需求详细说明
简历生成模块需求:
- 用户注册与个人资料管理
- 分步骤简历创建向导(个人信息、教育背景、工作经历、技能等)
- 多种专业简历模板选择
- 实时预览功能
- 导出为PDF/Word格式
- 简历分享链接生成
职位推荐模块需求:
- 职位数据库管理
- 基于用户简历内容的智能匹配算法
- 个性化推荐排序
- 职位收藏与申请跟踪
- 推荐职位邮件通知
1.3 技术栈选择
- 核心平台:WordPress 5.8+
- 开发语言:PHP 7.4+,JavaScript (ES6+)
- 前端框架:React/Vue.js(可选,用于复杂交互)
- 数据库:MySQL 5.7+
- PDF生成:TCPDF或Dompdf库
- 缓存机制:Redis或Memcached(可选)
- 安全性:WordPress Nonce,数据验证与清理
二、系统架构设计
2.1 整体架构图
用户层 (前端界面)
↓
表示层 (WordPress主题 + 自定义页面模板)
↓
应用层 (自定义插件 + REST API)
↓
数据层 (WordPress数据库 + 自定义表)
↓
服务层 (第三方API集成:职位数据源)
2.2 数据库设计
2.2.1 核心数据表结构
-- 简历主表
CREATE TABLE wp_resume_builder_resumes (
resume_id INT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT(20) UNSIGNED NOT NULL,
title VARCHAR(255) NOT NULL,
template_id INT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_public TINYINT(1) DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE CASCADE
);
-- 简历内容表(JSON格式存储,便于扩展)
CREATE TABLE wp_resume_builder_content (
content_id INT AUTO_INCREMENT PRIMARY KEY,
resume_id INT NOT NULL,
section_type ENUM('personal', 'education', 'experience', 'skills', 'projects', 'certifications') NOT NULL,
content_data JSON NOT NULL,
display_order INT DEFAULT 0,
FOREIGN KEY (resume_id) REFERENCES wp_resume_builder_resumes(resume_id) ON DELETE CASCADE
);
-- 职位信息表
CREATE TABLE wp_resume_builder_jobs (
job_id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(255) NOT NULL,
company VARCHAR(255) NOT NULL,
description TEXT,
requirements JSON,
location VARCHAR(255),
salary_range VARCHAR(100),
job_type ENUM('full-time', 'part-time', 'contract', 'remote', 'hybrid') DEFAULT 'full-time',
posted_date DATETIME DEFAULT CURRENT_TIMESTAMP,
expiry_date DATETIME,
is_active TINYINT(1) DEFAULT 1
);
-- 用户技能标签表
CREATE TABLE wp_resume_builder_skills (
skill_id INT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT(20) UNSIGNED NOT NULL,
skill_name VARCHAR(100) NOT NULL,
proficiency_level ENUM('beginner', 'intermediate', 'advanced', 'expert') DEFAULT 'intermediate',
years_of_experience DECIMAL(3,1),
FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE CASCADE
);
-- 职位推荐记录表
CREATE TABLE wp_resume_builder_recommendations (
recommendation_id INT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT(20) UNSIGNED NOT NULL,
job_id INT NOT NULL,
match_score DECIMAL(5,2),
viewed TINYINT(1) DEFAULT 0,
applied TINYINT(1) DEFAULT 0,
recommended_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES wp_users(ID) ON DELETE CASCADE,
FOREIGN KEY (job_id) REFERENCES wp_resume_builder_jobs(job_id) ON DELETE CASCADE
);
2.2.2 数据库优化策略
- 为频繁查询的字段添加索引
- 使用InnoDB引擎支持事务和外键
- 定期归档历史数据
- 实施查询缓存机制
2.3 插件架构设计
resume-builder-plugin/
├── resume-builder.php # 主插件文件
├── includes/
│ ├── class-database-handler.php # 数据库操作类
│ ├── class-resume-builder.php # 简历构建核心类
│ ├── class-job-matcher.php # 职位匹配算法类
│ ├── class-pdf-generator.php # PDF生成类
│ └── class-api-handler.php # REST API处理类
├── admin/
│ ├── class-admin-menu.php # 管理菜单
│ ├── class-settings-page.php # 设置页面
│ └── class-job-manager.php # 职位管理界面
├── public/
│ ├── css/ # 前端样式
│ ├── js/ # 前端脚本
│ └── templates/ # 前端模板
├── assets/ # 静态资源
├── templates/ # 简历模板
└── vendor/ # 第三方库
三、开发环境搭建与基础配置
3.1 本地开发环境配置
-
安装本地服务器环境:
- 使用XAMPP、MAMP或Local by Flywheel
- 确保PHP版本≥7.4,MySQL≥5.7
-
设置WordPress开发环境:
# 创建插件目录 mkdir -p wp-content/plugins/resume-builder # 启用WordPress调试模式 # 在wp-config.php中添加 define('WP_DEBUG', true); define('WP_DEBUG_LOG', true); define('WP_DEBUG_DISPLAY', false); -
创建主插件文件:
<?php /** * Plugin Name: 简历生成与职位推荐工具 * Plugin URI: https://yourwebsite.com/ * Description: 一个集在线简历生成与智能职位推荐功能的WordPress插件 * Version: 1.0.0 * Author: 您的名称 * License: GPL v2 or later * Text Domain: resume-builder */ // 防止直接访问 if (!defined('ABSPATH')) { exit; } // 定义插件常量 define('RB_PLUGIN_VERSION', '1.0.0'); define('RB_PLUGIN_PATH', plugin_dir_path(__FILE__)); define('RB_PLUGIN_URL', plugin_dir_url(__FILE__)); define('RB_PLUGIN_BASENAME', plugin_basename(__FILE__)); // 初始化插件 require_once RB_PLUGIN_PATH . 'includes/class-resume-builder-init.php'; // 激活/停用钩子 register_activation_hook(__FILE__, ['Resume_Builder_Init', 'activate']); register_deactivation_hook(__FILE__, ['Resume_Builder_Init', 'deactivate']); // 初始化插件 add_action('plugins_loaded', ['Resume_Builder_Init', 'get_instance']);
3.2 数据库表创建
创建数据库初始化类:
// includes/class-database-handler.php
class Resume_Builder_Database {
private static $instance = null;
private $charset_collate;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
global $wpdb;
$this->charset_collate = $wpdb->get_charset_collate();
}
public function create_tables() {
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
$sql = [];
// 简历主表
$sql[] = "CREATE TABLE {$wpdb->prefix}resume_builder_resumes (
resume_id INT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT(20) UNSIGNED NOT NULL,
title VARCHAR(255) NOT NULL,
template_id INT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
is_public TINYINT(1) DEFAULT 0,
FOREIGN KEY (user_id) REFERENCES {$wpdb->prefix}users(ID) ON DELETE CASCADE
) {$this->charset_collate};";
// 其他表创建语句...
foreach ($sql as $query) {
dbDelta($query);
}
// 添加默认数据
$this->add_default_data();
}
private function add_default_data() {
// 添加默认简历模板
$default_templates = [
[
'name' => '经典专业',
'description' => '简洁专业的简历模板',
'thumbnail' => RB_PLUGIN_URL . 'assets/templates/classic.png',
'file_path' => RB_PLUGIN_PATH . 'templates/classic.php'
],
// 更多模板...
];
// 保存到选项表
update_option('rb_default_templates', $default_templates);
}
public function update_tables() {
// 数据库升级逻辑
}
}
四、简历生成模块开发
4.1 用户界面设计
4.1.1 创建简历构建器短代码
// includes/class-resume-builder.php
class Resume_Builder_Frontend {
public function init() {
// 注册短代码
add_shortcode('resume_builder', [$this, 'render_resume_builder']);
// 注册前端脚本和样式
add_action('wp_enqueue_scripts', [$this, 'enqueue_frontend_assets']);
}
public function enqueue_frontend_assets() {
// 仅在有短代码的页面加载
global $post;
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'resume_builder')) {
// 样式文件
wp_enqueue_style(
'rb-frontend-style',
RB_PLUGIN_URL . 'public/css/frontend.css',
[],
RB_PLUGIN_VERSION
);
// JavaScript文件
wp_enqueue_script(
'rb-frontend-script',
RB_PLUGIN_URL . 'public/js/frontend.js',
['jquery', 'wp-api'],
RB_PLUGIN_VERSION,
true
);
// 本地化脚本
wp_localize_script('rb-frontend-script', 'rb_ajax', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('rb_ajax_nonce'),
'user_id' => get_current_user_id(),
'i18n' => [
'save_success' => __('保存成功', 'resume-builder'),
'save_error' => __('保存失败,请重试', 'resume-builder'),
// 更多翻译字符串...
]
]);
}
}
public function render_resume_builder($atts) {
// 检查用户是否登录
if (!is_user_logged_in()) {
return $this->render_login_prompt();
}
ob_start();
?>
<div id="resume-builder-app" class="resume-builder-container">
<!-- 简历构建器界面 -->
<div class="rb-stepper">
<!-- 步骤指示器 -->
<div class="stepper-header">
<div class="step active" data-step="1">个人信息</div>
<div class="step" data-step="2">教育背景</div>
<div class="step" data-step="3">工作经历</div>
<div class="step" data-step="4">技能专长</div>
<div class="step" data-step="5">预览与导出</div>
</div>
<!-- 步骤内容 -->
<div class="stepper-content">
<!-- 步骤1: 个人信息 -->
<div class="step-content active" id="step-1">
<h3>个人信息</h3>
<form id="personal-info-form">
<div class="form-group">
<label for="full_name">全名</label>
<input type="text" id="full_name" name="full_name" required>
</div>
<!-- 更多表单字段... -->
</form>
</div>
<!-- 其他步骤内容... -->
</div>
<!-- 导航按钮 -->
<div class="stepper-nav">
<button class="btn btn-secondary" id="prev-step">上一步</button>
<button class="btn btn-primary" id="next-step">下一步</button>
<button class="btn btn-success" id="save-resume">保存简历</button>
</div>
</div>
<!-- 实时预览面板 -->
<div class="resume-preview">
<h3>简历预览</h3>
<div id="resume-preview-content">
<!-- 通过JavaScript动态加载预览 -->
</div>
<div class="preview-actions">
<button class="btn btn-outline" id="change-template">更换模板</button>
<button class="btn btn-primary" id="export-pdf">导出PDF</button>
<button class="btn btn-secondary" id="share-resume">分享链接</button>
</div>
</div>
</div>
<?php
return ob_get_clean();
}
private function render_login_prompt() {
ob_start();
?>
<div class="rb-login-prompt">
<h3>请登录以创建简历</h3>
<p>登录后您可以创建、编辑和管理您的专业简历</p>
<div class="login-actions">
<a href="<?php echo wp_login_url(get_permalink()); ?>" class="btn btn-primary">
登录
</a>
<a href="<?php echo wp_registration_url(); ?>" class="btn btn-secondary">
注册
</a>
</div>
</div>
<?php
return ob_get_clean();
}
}
4.1.2 前端JavaScript交互
// public/js/frontend.js
(function($) {
'use strict';
class ResumeBuilder {
constructor() {
this.currentStep = 1;
this.totalSteps = 5;
this.resumeData = {};
this.init();
}
init() {
this.bindEvents();
this.loadUserData();
}
bindEvents() {
// 步骤导航
$('#next-step').on('click', () => this.nextStep());
$('#prev-step').on('click', () => this.prevStep());
// 表单保存
$('.step-content form').on('change', 'input, textarea, select', (e) => {
this.saveStepData($(e.target).closest('form'));
});
// 导出功能
$('#export-pdf').on('click', () => this.exportToPDF());
$('#share-resume').on('click', () => this.generateShareLink());
// 模板切换
$('#change-template').on('click', () => this.showTemplateSelector());
}
nextStep() {
if (this.currentStep < this.totalSteps) {
this.validateCurrentStep();
this.currentStep++;
this.updateStepper();
}
}
prevStep() {
if (this.currentStep > 1) {
this.currentStep--;
this.updateStepper();
}
}
updateStepper() {
// 更新步骤指示器
$('.step').removeClass('active');
$(`.step[data-step="${this.currentStep}"]`).addClass('active');
// 更新内容显示
$('.step-content').removeClass('active');
$(`#step-${this.currentStep}`).addClass('active');
// 更新按钮状态
$('#prev-step').toggle(this.currentStep > 1);
$('#next-step').toggle(this.currentStep < this.totalSteps);
$('#save-resume').toggle(this.currentStep === this.totalSteps);
// 更新预览
this.updatePreview();
}
saveStepData(form) {
const formData = new FormData(form[0]);
const stepData = {};
for (let [key, value] of formData.entries()) {
stepData[key] = value;
}
resumeData[step${this.currentStep}] = stepData;
// 自动保存到服务器
this.autoSaveToServer();
}
autoSaveToServer() {
$.ajax({
url: rb_ajax.ajax_url,
type: 'POST',
data: {
action: 'save_resume_data',
nonce: rb_ajax.nonce,
user_id: rb_ajax.user_id,
resume_data: this.resumeData
},
success: (response) => {
if (response.success) {
console.log('自动保存成功');
}
}
});
}
updatePreview() {
// 使用AJAX获取HTML预览
$.ajax({
url: rb_ajax.ajax_url,
type: 'POST',
data: {
action: 'get_resume_preview',
nonce: rb_ajax.nonce,
resume_data: this.resumeData,
template_id: this.currentTemplate
},
success: (response) => {
if (response.success) {
$('#resume-preview-content').html(response.data.preview);
}
}
});
}
exportToPDF() {
// 生成PDF
window.open(
`${rb_ajax.ajax_url}?action=generate_pdf&nonce=${rb_ajax.nonce}&resume_id=${this.currentResumeId}`,
'_blank'
);
}
generateShareLink() {
$.ajax({
url: rb_ajax.ajax_url,
type: 'POST',
data: {
action: 'generate_share_link',
nonce: rb_ajax.nonce,
resume_id: this.currentResumeId
},
success: (response) => {
if (response.success) {
this.showShareModal(response.data.share_url);
}
}
});
}
showShareModal(shareUrl) {
// 创建分享模态框
const modalHtml = `
<div class="rb-modal" id="share-modal">
<div class="modal-content">
<h3>分享您的简历</h3>
<div class="share-url">
<input type="text" value="${shareUrl}" readonly>
<button class="btn-copy" data-clipboard-text="${shareUrl}">复制</button>
</div>
<div class="share-options">
<button class="share-option" data-platform="linkedin">LinkedIn</button>
<button class="share-option" data-platform="email">电子邮件</button>
<button class="share-option" data-platform="whatsapp">WhatsApp</button>
</div>
</div>
</div>
`;
$('body').append(modalHtml);
this.initClipboard();
}
initClipboard() {
// 初始化剪贴板功能
new ClipboardJS('.btn-copy');
}
}
// 初始化简历构建器
$(document).ready(function() {
if ($('#resume-builder-app').length) {
window.resumeBuilder = new ResumeBuilder();
}
});
})(jQuery);
### 4.2 简历模板系统
#### 4.2.1 模板引擎设计
// includes/class-template-engine.php
class Resume_Builder_Template_Engine {
private $templates = [];
private $current_template = null;
public function __construct() {
$this->load_templates();
}
private function load_templates() {
$template_dir = RB_PLUGIN_PATH . 'templates/';
$template_files = glob($template_dir . '*.php');
foreach ($template_files as $file) {
$template_name = basename($file, '.php');
$template_data = $this->get_template_metadata($file);
$this->templates[$template_name] = [
'name' => $template_data['name'] ?? ucfirst($template_name),
'description' => $template_data['description'] ?? '',
'thumbnail' => $template_data['thumbnail'] ?? '',
'file_path' => $file,
'version' => $template_data['version'] ?? '1.0',
'category' => $template_data['category'] ?? 'general'
];
}
}
private function get_template_metadata($file) {
$metadata = [
'name' => '',
'description' => '',
'thumbnail' => '',
'version' => '1.0',
'category' => 'general'
];
$content = file_get_contents($file);
// 解析模板头部注释
if (preg_match('//**s*n(.*?)*//s', $content, $matches)) {
$comment = $matches[1];
if (preg_match('/Template Name:s*(.+)/', $comment, $name_match)) {
$metadata['name'] = trim($name_match[1]);
}
if (preg_match('/Description:s*(.+)/', $comment, $desc_match)) {
$metadata['description'] = trim($desc_match[1]);
}
}
return $metadata;
}
public function render_template($template_name, $resume_data) {
if (!isset($this->templates[$template_name])) {
$template_name = 'classic'; // 默认模板
}
$template_file = $this->templates[$template_name]['file_path'];
// 提取数据
$data = $this->prepare_template_data($resume_data);
// 开始输出缓冲
ob_start();
// 包含模板文件
include $template_file;
// 获取缓冲内容
$html = ob_get_clean();
// 应用CSS样式
$html = $this->apply_template_styles($template_name, $html);
return $html;
}
private function prepare_template_data($resume_data) {
$data = [
'personal' => $resume_data['step1'] ?? [],
'education' => $this->format_education_data($resume_data['step2'] ?? []),
'experience' => $this->format_experience_data($resume_data['step3'] ?? []),
'skills' => $this->format_skills_data($resume_data['step4'] ?? []),
'summary' => $resume_data['step5']['summary'] ?? ''
];
return $data;
}
private function format_education_data($education_data) {
// 格式化教育背景数据
$formatted = [];
if (isset($education_data['institutions'])) {
foreach ($education_data['institutions'] as $edu) {
$formatted[] = [
'institution' => $edu['school'] ?? '',
'degree' => $edu['degree'] ?? '',
'field' => $edu['field'] ?? '',
'period' => $this->format_period($edu['start_date'] ?? '', $edu['end_date'] ?? ''),
'description' => $edu['description'] ?? '',
'gpa' => $edu['gpa'] ?? ''
];
}
}
// 按时间倒序排列
usort($formatted, function($a, $b) {
return strtotime($b['period']) - strtotime($a['period']);
});
return $formatted;
}
private function format_experience_data($experience_data) {
// 格式化工作经历数据
$formatted = [];
if (isset($experience_data['positions'])) {
foreach ($experience_data['positions'] as $exp) {
$formatted[] = [
'company' => $exp['company'] ?? '',
'position' => $exp['position'] ?? '',
'period' => $this->format_period($exp['start_date'] ?? '', $exp['end_date'] ?? ''),
'description' => $exp['description'] ?? '',
'achievements' => isset($exp['achievements']) ? explode("n", $exp['achievements']) : [],
'skills_used' => isset($exp['skills']) ? explode(',', $exp['skills']) : []
];
}
}
// 按时间倒序排列
usort($formatted, function($a, $b) {
return strtotime($b['period']) - strtotime($a['period']);
});
return $formatted;
}
private function format_skills_data($skills_data) {
// 格式化技能数据
$formatted = [
'technical' => [],
'professional' => [],
'languages' => []
];
if (isset($skills_data['technical_skills'])) {
foreach ($skills_data['technical_skills'] as $skill) {
$formatted['technical'][] = [
'name' => $skill['name'] ?? '',
'level' => $skill['level'] ?? 'intermediate',
'years' => $skill['years'] ?? 0
];
}
}
// 类似处理其他技能类型...
return $formatted;
}
private function format_period($start_date, $end_date) {
$start = date('M Y', strtotime($start_date));
$end = $end_date ? date('M Y', strtotime($end_date)) : '至今';
return "$start - $end";
}
private function apply_template_styles($template_name, $html) {
$css_file = RB_PLUGIN_PATH . "templates/css/{$template_name}.css";
if (file_exists($css_file)) {
$css = file_get_contents($css_file);
$html = "<style>{$css}</style>" . $html;
}
return $html;
}
public function get_available_templates() {
return $this->templates;
}
}
#### 4.2.2 示例简历模板
<!-- templates/classic.php -->
<?php
/**
- Template Name: 经典专业
- Description: 简洁专业的简历模板,适合传统行业
- Version: 1.0
- Category: professional
*/
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?php echo esc_html($data['personal']['full_name'] ?? '个人简历'); ?></title>
</head>
<body class="rb-resume-template classic">
<div class="resume-container">
<!-- 头部信息 -->
<header class="resume-header">
<div class="personal-info">
<h1 class="name"><?php echo esc_html($data['personal']['full_name'] ?? ''); ?></h1>
<div class="contact-info">
<?php if (!empty($data['personal']['email'])): ?>
<span class="contact-item">
<i class="icon-email"></i>
<?php echo esc_html($data['personal']['email']); ?>
</span>
<?php endif; ?>
<?php if (!empty($data['personal']['phone'])): ?>
<span class="contact-item">
<i class="icon-phone"></i>
<?php echo esc_html($data['personal']['phone']); ?>
</span>
<?php endif; ?>
<?php if (!empty($data['personal']['location'])): ?>
<span class="contact-item">
<i class="icon-location"></i>
<?php echo esc_html($data['personal']['location']); ?>
</span>
<?php endif; ?>
<?php if (!empty($data['personal']['linkedin'])): ?>
<span class="contact-item">
<i class="icon-linkedin"></i>
<?php echo esc_html($data['personal']['linkedin']); ?>
</span>
<?php endif; ?>
</div>
</div>
<?php if (!empty($data['personal']['title'])): ?>
<div class="professional-title">
<h2><?php echo esc_html($data['personal']['title']); ?></h2>
</div>
<?php endif; ?>
</header>
<!-- 个人简介 -->
<?php if (!empty($data['summary'])): ?>
<section class="resume-section summary">
<h3 class="section-title">个人简介</h3>
<div class="section-content">
<p><?php echo nl2br(esc_html($data['summary'])); ?></p>
</div>
</section>
<?php endif; ?>
<!-- 工作经历 -->
<?php if (!empty($data['experience'])): ?>
<section class="resume-section experience">
<h3 class="section-title">工作经历</h3>
<div class="section-content">
<?php foreach ($data['experience'] as $job): ?>
<div class="experience-item">
<div class="job-header">
<h4 class="job-title"><?php echo esc_html($job['position']); ?></h4>
<span class="company"><?php echo esc_html($job['company']); ?></span>
<span class="period"><?php echo esc_html($job['period']); ?></span>
</div>
<?php if (!empty($job['description'])): ?>
<div class="job-description">
<p><?php echo nl2br(esc_html($job['description'])); ?></p>
</div>
<?php endif; ?>
<?php if (!empty($job['achievements'])): ?>
<ul class="achievements">
<?php foreach ($job['achievements'] as $achievement): ?>
<?php if (!empty(trim($achievement))): ?>
<li><?php echo esc_html(trim($achievement)); ?></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<!-- 教育背景 -->
<?php if (!empty($data['education'])): ?>
<section class="resume-section education">
<h3 class="section-title">教育背景</h3>
<div class="section-content">
<?php foreach ($data['education'] as $edu): ?>
<div class="education-item">
<h4 class="degree"><?php echo esc_html($edu['degree']); ?></h4>
<span class="institution"><?php echo esc_html($edu['institution']); ?></span>
<span class="period"><?php echo esc_html($edu['period']); ?></span>
<?php if (!empty($edu['field'])): ?>
<div class="field-of-study">专业:<?php echo esc_html($edu['field']); ?></div>
<?php endif; ?>
<?php if (!empty($edu['gpa'])): ?>
<div class="gpa">GPA:<?php echo esc_html($edu['gpa']); ?></div>
<?php endif; ?>
</div>
<?php endforeach; ?>
</div>
</section>
<?php endif; ?>
<!-- 技能专长 -->
<?php if (!empty($data['skills']['technical'])): ?>
<section class="resume-section skills">
<h3 class="section-title">技能专长</h3>
<div class="section-content">
<div class="skills-grid">
<?php foreach ($data['skills']['technical'] as $skill): ?>
<div class="skill-item">
<span class="skill-name"><?php echo esc_html($skill['name']); ?></span>
<div class="skill-level">
<?php for ($i = 1; $i <= 5; $i++): ?>
<span class="level-dot <?php echo $i <= $this->get_level_number($skill['level']) ? 'filled' : ''; ?>"></span>
<?php endfor; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
</section>
<?php endif; ?>
</div>
</body>
</html>
### 4.3 PDF导出功能
// includes/class-pdf-generator.php
class Resume_Builder_PDF_Generator {
private $pdf;
private $template_engine;
public function __construct() {
// 引入TCPDF库
require_once RB_PLUGIN_PATH . 'vendor/tcpdf/tcpdf.php';
$this->template_engine = new Resume_Builder_Template_Engine();
}
public function generate_pdf($resume_id, $template_name = 'classic') {
// 获取简历数据
$resume_data = $this->get_resume_data($resume_id);
if (!$resume_data) {
return false;
}
// 创建PDF实例
$this->pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
// 设置文档信息
$this->pdf->SetCreator('WordPress Resume Builder');
$this->pdf->SetAuthor($resume_data['personal']['full_name'] ?? '');
$this->pdf->SetTitle('简历 - ' . ($resume_data['personal']['full_name'] ?? ''));
$this->pdf->SetSubject('个人简历');
// 设置页眉页脚
$this->pdf->setHeaderFont([PDF_FONT_NAME_MAIN, '', PDF_FONT_SIZE_MAIN]);
$this->pdf->setFooterFont([PDF_FONT_NAME_DATA, '', PDF_FONT_SIZE_DATA]);
// 设置默认等宽字体
$this->pdf->SetDefaultMonospacedFont(PDF_FONT_MONOSPACED);
// 设置边距
$this->pdf->SetMargins(15, 20, 15);
$this->pdf->SetHeaderMargin(5);
$this->pdf->SetFooterMargin(10);
// 设置自动分页
$this->pdf->SetAutoPageBreak(TRUE, 15
