文章目录[隐藏]
WordPress会议室预订与资源调度管理系统开发详细教程
引言:为什么需要会议室预订与资源调度系统
在现代办公环境中,会议室和各种共享资源(如投影仪、车辆、设备等)的高效管理是企业运营的重要环节。传统的人工预约方式不仅效率低下,还容易引发时间冲突和资源浪费。随着远程办公和混合工作模式的普及,一个数字化的资源调度系统变得尤为重要。
WordPress作为全球最流行的内容管理系统,不仅适用于博客和网站建设,通过代码二次开发,完全可以实现会议室预订与资源调度这样的专业功能。本教程将详细指导您如何从零开始,在WordPress平台上开发一个功能完善的会议室预订与资源调度管理系统。
第一部分:系统需求分析与规划
1.1 核心功能需求
在开始开发之前,我们需要明确系统应具备的核心功能:
- 会议室管理:添加、编辑、删除会议室,设置容量、设备配置等信息
- 资源管理:管理可预订资源(投影仪、白板、视频会议设备等)
- 预订功能:用户可查看可用时间段并进行预订
- 冲突检测:自动检测时间冲突,避免重复预订
- 日历视图:直观展示会议室和资源的占用情况
- 用户权限管理:不同用户角色拥有不同权限
- 通知系统:预订确认、提醒、变更通知
- 报表统计:使用频率统计、资源利用率分析
1.2 技术架构设计
我们将采用以下技术架构:
- 前端:HTML5、CSS3、JavaScript(jQuery)、FullCalendar.js
- 后端:PHP(WordPress核心)、MySQL数据库
- 通信:AJAX实现无刷新操作
- 安全性:WordPress非ces、数据验证、权限检查
1.3 数据库设计
我们需要创建以下自定义数据表:
wp_meeting_rooms:存储会议室信息wp_resources:存储可预订资源信息wp_bookings:存储预订记录wp_booking_resources:预订与资源的关联表
第二部分:开发环境搭建与基础配置
2.1 开发环境准备
首先确保您具备以下环境:
- WordPress 5.0+ 安装
- PHP 7.4+ 版本
- MySQL 5.6+ 数据库
- 代码编辑器(VS Code、Sublime Text等)
2.2 创建自定义插件
我们将创建一个独立的插件来实现所有功能,确保与主题分离,便于维护和迁移。
在WordPress的wp-content/plugins/目录下创建新文件夹meeting-room-booking-system,并在其中创建主插件文件:
<?php
/**
* Plugin Name: 会议室预订与资源调度管理系统
* Plugin URI: https://yourwebsite.com/
* Description: 一个功能完整的会议室与资源预订管理系统
* Version: 1.0.0
* Author: 您的名称
* License: GPL v2 or later
* Text Domain: mr-booking
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('MRB_PLUGIN_PATH', plugin_dir_path(__FILE__));
define('MRB_PLUGIN_URL', plugin_dir_url(__FILE__));
define('MRB_VERSION', '1.0.0');
// 初始化插件
require_once MRB_PLUGIN_PATH . 'includes/class-init.php';
2.3 创建数据库表
在插件初始化类中,添加创建数据库表的代码:
class MRB_Init {
public function __construct() {
// 激活插件时创建表
register_activation_hook(__FILE__, array($this, 'create_tables'));
// 加载其他组件
$this->load_dependencies();
}
public function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
// 会议室表
$rooms_table = $wpdb->prefix . 'meeting_rooms';
$rooms_sql = "CREATE TABLE IF NOT EXISTS $rooms_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
description text,
capacity smallint NOT NULL DEFAULT 10,
location varchar(200),
amenities text,
status tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// 资源表
$resources_table = $wpdb->prefix . 'resources';
$resources_sql = "CREATE TABLE IF NOT EXISTS $resources_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
type varchar(50) NOT NULL,
description text,
quantity smallint NOT NULL DEFAULT 1,
status tinyint(1) DEFAULT 1,
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id)
) $charset_collate;";
// 预订表
$bookings_table = $wpdb->prefix . 'bookings';
$bookings_sql = "CREATE TABLE IF NOT EXISTS $bookings_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
room_id mediumint(9) NOT NULL,
user_id bigint(20) NOT NULL,
title varchar(200) NOT NULL,
description text,
start_time datetime NOT NULL,
end_time datetime NOT NULL,
attendees smallint DEFAULT 1,
status varchar(20) DEFAULT 'confirmed',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY room_id (room_id),
KEY user_id (user_id),
KEY start_time (start_time),
KEY end_time (end_time)
) $charset_collate;";
// 预订资源关联表
$booking_resources_table = $wpdb->prefix . 'booking_resources';
$booking_resources_sql = "CREATE TABLE IF NOT EXISTS $booking_resources_table (
id mediumint(9) NOT NULL AUTO_INCREMENT,
booking_id mediumint(9) NOT NULL,
resource_id mediumint(9) NOT NULL,
quantity smallint NOT NULL DEFAULT 1,
PRIMARY KEY (id),
KEY booking_id (booking_id),
KEY resource_id (resource_id)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($rooms_sql);
dbDelta($resources_sql);
dbDelta($bookings_sql);
dbDelta($booking_resources_sql);
}
private function load_dependencies() {
// 加载其他类文件
require_once MRB_PLUGIN_PATH . 'includes/class-admin.php';
require_once MRB_PLUGIN_PATH . 'includes/class-frontend.php';
require_once MRB_PLUGIN_PATH . 'includes/class-ajax.php';
require_once MRB_PLUGIN_PATH . 'includes/class-shortcodes.php';
}
}
new MRB_Init();
第三部分:后台管理界面开发
3.1 创建管理菜单
在class-admin.php中,添加管理菜单功能:
class MRB_Admin {
public function __construct() {
add_action('admin_menu', array($this, 'add_admin_menus'));
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
public function add_admin_menus() {
// 主菜单
add_menu_page(
'会议室预订系统',
'会议室预订',
'manage_options',
'mrb-dashboard',
array($this, 'dashboard_page'),
'dashicons-calendar-alt',
30
);
// 子菜单
add_submenu_page(
'mrb-dashboard',
'会议室管理',
'会议室',
'manage_options',
'mrb-rooms',
array($this, 'rooms_page')
);
add_submenu_page(
'mrb-dashboard',
'资源管理',
'资源',
'manage_options',
'mrb-resources',
array($this, 'resources_page')
);
add_submenu_page(
'mrb-dashboard',
'预订管理',
'预订',
'manage_options',
'mrb-bookings',
array($this, 'bookings_page')
);
add_submenu_page(
'mrb-dashboard',
'系统设置',
'设置',
'manage_options',
'mrb-settings',
array($this, 'settings_page')
);
}
public function enqueue_admin_scripts($hook) {
// 仅在我们的插件页面加载脚本
if (strpos($hook, 'mrb-') !== false) {
wp_enqueue_style('mrb-admin-style', MRB_PLUGIN_URL . 'assets/css/admin.css', array(), MRB_VERSION);
wp_enqueue_script('mrb-admin-script', MRB_PLUGIN_URL . 'assets/js/admin.js', array('jquery'), MRB_VERSION, true);
// 本地化脚本,传递数据到JS
wp_localize_script('mrb-admin-script', 'mrb_admin', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mrb_admin_nonce')
));
}
}
public function dashboard_page() {
include MRB_PLUGIN_PATH . 'templates/admin/dashboard.php';
}
public function rooms_page() {
include MRB_PLUGIN_PATH . 'templates/admin/rooms.php';
}
public function resources_page() {
include MRB_PLUGIN_PATH . 'templates/admin/resources.php';
}
public function bookings_page() {
include MRB_PLUGIN_PATH . 'templates/admin/bookings.php';
}
public function settings_page() {
include MRB_PLUGIN_PATH . 'templates/admin/settings.php';
}
}
3.2 会议室管理界面
创建templates/admin/rooms.php文件:
<div class="wrap mrb-admin-wrap">
<h1 class="wp-heading-inline">会议室管理</h1>
<a href="#" class="page-title-action" id="mrb-add-room">添加新会议室</a>
<hr class="wp-header-end">
<!-- 添加/编辑会议室表单 (默认隐藏) -->
<div id="mrb-room-form-container" style="display:none;">
<h2 id="mrb-form-title">添加会议室</h2>
<form id="mrb-room-form" method="post">
<?php wp_nonce_field('mrb_save_room', 'mrb_room_nonce'); ?>
<input type="hidden" id="room_id" name="room_id" value="0">
<table class="form-table">
<tr>
<th scope="row"><label for="room_name">会议室名称</label></th>
<td><input type="text" id="room_name" name="room_name" class="regular-text" required></td>
</tr>
<tr>
<th scope="row"><label for="room_capacity">容量</label></th>
<td><input type="number" id="room_capacity" name="room_capacity" min="1" max="500" value="10" required></td>
</tr>
<tr>
<th scope="row"><label for="room_location">位置</label></th>
<td><input type="text" id="room_location" name="room_location" class="regular-text"></td>
</tr>
<tr>
<th scope="row"><label for="room_description">描述</label></th>
<td><textarea id="room_description" name="room_description" rows="5" class="large-text"></textarea></td>
</tr>
<tr>
<th scope="row"><label for="room_amenities">设备配置</label></th>
<td>
<textarea id="room_amenities" name="room_amenities" rows="3" class="large-text" placeholder="例如:投影仪、白板、视频会议系统"></textarea>
<p class="description">每行一个设备,或使用逗号分隔</p>
</td>
</tr>
<tr>
<th scope="row"><label for="room_status">状态</label></th>
<td>
<select id="room_status" name="room_status">
<option value="1">可用</option>
<option value="0">不可用</option>
</select>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button button-primary">保存会议室</button>
<button type="button" class="button" id="mrb-cancel-form">取消</button>
</p>
</form>
</div>
<!-- 会议室列表 -->
<div id="mrb-rooms-list">
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th scope="col" width="5%">ID</th>
<th scope="col" width="20%">名称</th>
<th scope="col" width="10%">容量</th>
<th scope="col" width="20%">位置</th>
<th scope="col" width="25%">设备配置</th>
<th scope="col" width="10%">状态</th>
<th scope="col" width="10%">操作</th>
</tr>
</thead>
<tbody id="mrb-rooms-table-body">
<!-- 通过AJAX加载数据 -->
<tr>
<td colspan="7">加载中...</td>
</tr>
</tbody>
</table>
</div>
</div>
第四部分:前端预订界面开发
4.1 创建前端短代码
在class-shortcodes.php中,创建用于前端显示的短代码:
class MRB_Shortcodes {
public function __construct() {
add_shortcode('meeting_room_booking', array($this, 'booking_calendar_shortcode'));
add_shortcode('meeting_room_list', array($this, 'room_list_shortcode'));
add_action('wp_enqueue_scripts', array($this, 'enqueue_frontend_scripts'));
}
public function enqueue_frontend_scripts() {
global $post;
// 仅在包含我们短代码的页面加载脚本
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'meeting_room_booking')) {
// FullCalendar库
wp_enqueue_style('fullcalendar-css', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css', array(), '5.10.1');
wp_enqueue_script('fullcalendar-js', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js', array('jquery'), '5.10.1', true);
// 本地化日历
wp_enqueue_script('fullcalendar-locale', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/locales/zh-cn.js', array('fullcalendar-js'), '5.10.1', true);
// 插件前端样式和脚本
wp_enqueue_style('mrb-frontend-style', MRB_PLUGIN_URL . 'assets/css/frontend.css', array(), MRB_VERSION);
wp_enqueue_script('mrb-frontend-script', MRB_PLUGIN_URL . 'assets/js/frontend.js', array('jquery', 'fullcalendar-js'), MRB_VERSION, true);
// 传递数据到前端JS
wp_localize_script('mrb-frontend-script', 'mrb_frontend', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('mrb_frontend_nonce'),
'current_user_id' => get_current_user_id(),
'calendar_locale' => get_locale(),
'time_format' => get_option('time_format', 'H:i'),
'date_format' => get_option('date_format', 'Y-m-d')
));
}
}
public function booking_calendar_shortcode($atts) {
// 检查用户是否登录
if (!is_user_logged_in()) {
return '<div class="mrb-login-required">请先登录系统以预订会议室。</div>';
}
ob_start();
include MRB_PLUGIN_PATH . 'templates/frontend/booking-calendar.php';
return ob_get_clean();
}
public function room_list_shortcode($atts) {
ob_start();
include MRB_PLUGIN_PATH . 'templates/frontend/room-list.php';
return ob_get_clean();
}
}
4.2 预订日历界面
创建templates/frontend/booking-calendar.php:
<div class="mrb-booking-container">
<div class="mrb-booking-header">
<h2>会议室预订系统</h2>
<div class="mrb-user-info">
欢迎,<?php echo wp_get_current_user()->display_name; ?>!
</div>
</div>
<div class="mrb-booking-main">
<div class="mrb-sidebar">
<div class="mrb-room-filter">
<h3>筛选会议室</h3>
<div class="mrb-filter-section">
<label for="mrb-filter-capacity">最小容量:</label>
<select id="mrb-filter-capacity">
<option value="0">不限</option>
<option value="5">5人以上</option>
<option value="10">10人以上</option>
<option value="20">20人以上</option>
<option value="30">30人以上</option>
<option value="50">50人以上</option>
</select>
</div>
<div class="mrb-filter-section">
<label for="mrb-filter-equipment">设备要求:</label>
<div class="mrb-equipment-checkboxes">
<label><input type="checkbox" value="projector"> 投影仪</label>
<label><input type="checkbox" value="whiteboard"> 白板</label>
<label><input type="checkbox" value="videoconf"> 视频会议</label>
<label><input type="checkbox" value="phone"> 电话会议</label>
</div>
</div>
<button id="mrb-apply-filter" class="button">应用筛选</button>
<button id="mrb-reset-filter" class="button button-secondary">重置筛选</button>
</div>
<div class="mrb-room-list">
<h3>可用会议室</h3>
<div id="mrb-rooms-container">
<!-- 通过AJAX加载会议室列表 -->
<div class="mrb-loading">加载中...</div>
</div>
</div>
<div class="mrb-resource-list">
<h3>可预订资源</h3>
<div id="mrb-resources-container">
<!-- 通过AJAX加载资源列表 -->
<div class="mrb-loading">加载中...</div>
</div>
</div>
</div>
<div class="mrb-calendar-section">
<div class="mrb-calendar-controls">
<button id="mrb-prev-week" class="button">< 上周</button>
<button id="mrb-today" class="button">今天</button>
<button id="mrb-next-week" class="button">下周 ></button>
<span id="mrb-current-week" class="mrb-week-display"></span>
<div class="mrb-view-toggle">
<button class="button active" data-view="week">周视图</button>
<button class="button" data-view="day">日视图</button>
<button class="button" data-view="month">月视图</button>
</div>
</div>
<div id="mrb-booking-calendar"></div>
<div class="mrb-legend">
<div class="mrb-legend-item">
<span class="mrb-legend-color available"></span>
<span>可预订</span>
</div>
<div class="mrb-legend-item">
<span class="mrb-legend-color booked"></span>
<span>已预订</span>
</div>
<div class="mrb-legend-item">
<span class="mrb-legend-color your-booking"></span>
<span>您的预订</span>
</div>
<div class="mrb-legend-item">
<span class="mrb-legend-color unavailable"></span>
<span>不可用</span>
</div>
</div>
</div>
</div>
</div>
<!-- 预订模态框 -->
<div id="mrb-booking-modal" class="mrb-modal" style="display:none;">
<div class="mrb-modal-content">
<div class="mrb-modal-header">
<h3>新建预订</h3>
<span class="mrb-modal-close">×</span>
</div>
<div class="mrb-modal-body">
<form id="mrb-booking-form">
<?php wp_nonce_field('mrb_create_booking', 'mrb_booking_nonce'); ?>
<input type="hidden" id="booking_room_id" name="room_id">
<input type="hidden" id="booking_start" name="start_time">
<input type="hidden" id="booking_end" name="end_time">
<div class="mrb-form-group">
<label for="booking_title">会议主题 *</label>
<input type="text" id="booking_title" name="title" required>
</div>
<div class="mrb-form-group">
<label for="booking_description">会议描述</label>
<textarea id="booking_description" name="description" rows="3"></textarea>
</div>
<div class="mrb-form-row">
<div class="mrb-form-group">
<label for="booking_date">日期</label>
<input type="text" id="booking_date" name="date" readonly>
</div>
<div class="mrb-form-group">
<label for="booking_start_time">开始时间</label>
<select id="booking_start_time" name="start_time_select">
<!-- 通过JS动态生成时间选项 -->
</select>
</div>
<div class="mrb-form-group">
<label for="booking_end_time">结束时间</label>
<select id="booking_end_time" name="end_time_select">
<!-- 通过JS动态生成时间选项 -->
</select>
</div>
</div>
<div class="mrb-form-group">
<label for="booking_attendees">参会人数 *</label>
<input type="number" id="booking_attendees" name="attendees" min="1" value="1" required>
<span id="booking_capacity_info" class="mrb-hint"></span>
</div>
<div class="mrb-form-group">
<label>预订资源</label>
<div id="mrb-booking-resources">
<!-- 通过JS动态生成资源选项 -->
</div>
</div>
<div class="mrb-form-group">
<label for="booking_recurring">重复预订</label>
<select id="booking_recurring" name="recurring">
<option value="none">不重复</option>
<option value="daily">每天</option>
<option value="weekly">每周</option>
<option value="monthly">每月</option>
</select>
<div id="mrb-recurring-options" style="display:none;">
<label for="booking_recurring_count">重复次数:</label>
<input type="number" id="booking_recurring_count" name="recurring_count" min="1" max="12" value="1">
</div>
</div>
<div class="mrb-form-actions">
<button type="submit" class="button button-primary">确认预订</button>
<button type="button" class="button mrb-modal-cancel">取消</button>
</div>
</form>
</div>
</div>
</div>
## 第五部分:AJAX处理与数据交互
### 5.1 AJAX处理类
在`class-ajax.php`中,处理所有前端和后端的AJAX请求:
class MRB_Ajax {
public function __construct() {
// 前端AJAX动作
add_action('wp_ajax_mrb_get_rooms', array($this, 'get_rooms'));
add_action('wp_ajax_nopriv_mrb_get_rooms', array($this, 'get_rooms'));
add_action('wp_ajax_mrb_get_resources', array($this, 'get_resources'));
add_action('wp_ajax_nopriv_mrb_get_resources', array($this, 'get_resources'));
add_action('wp_ajax_mrb_get_bookings', array($this, 'get_bookings'));
add_action('wp_ajax_nopriv_mrb_get_bookings', array($this, 'get_bookings'));
add_action('wp_ajax_mrb_create_booking', array($this, 'create_booking'));
add_action('wp_ajax_mrb_cancel_booking', array($this, 'cancel_booking'));
// 后台AJAX动作
add_action('wp_ajax_mrb_admin_save_room', array($this, 'admin_save_room'));
add_action('wp_ajax_mrb_admin_delete_room', array($this, 'admin_delete_room'));
add_action('wp_ajax_mrb_admin_get_rooms', array($this, 'admin_get_rooms'));
}
/**
* 获取会议室列表
*/
public function get_rooms() {
// 验证nonce
if (!check_ajax_referer('mrb_frontend_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
global $wpdb;
$table_name = $wpdb->prefix . 'meeting_rooms';
// 获取筛选参数
$capacity = isset($_POST['capacity']) ? intval($_POST['capacity']) : 0;
$equipment = isset($_POST['equipment']) ? $_POST['equipment'] : array();
// 构建查询
$where = array('status = 1');
$params = array();
if ($capacity > 0) {
$where[] = 'capacity >= %d';
$params[] = $capacity;
}
$where_sql = !empty($where) ? 'WHERE ' . implode(' AND ', $where) : '';
// 如果有设备筛选,需要进一步处理
if (!empty($equipment) && is_array($equipment)) {
$query = "SELECT * FROM $table_name $where_sql";
if (!empty($params)) {
$query = $wpdb->prepare($query, $params);
}
$rooms = $wpdb->get_results($query);
// 过滤设备
$filtered_rooms = array();
foreach ($rooms as $room) {
$amenities = explode(',', $room->amenities);
$amenities = array_map('trim', $amenities);
$match = true;
foreach ($equipment as $eq) {
if (!in_array($eq, $amenities)) {
$match = false;
break;
}
}
if ($match) {
$filtered_rooms[] = $room;
}
}
$rooms = $filtered_rooms;
} else {
$query = "SELECT * FROM $table_name $where_sql ORDER BY name ASC";
if (!empty($params)) {
$query = $wpdb->prepare($query, $params);
}
$rooms = $wpdb->get_results($query);
}
// 格式化返回数据
$formatted_rooms = array();
foreach ($rooms as $room) {
$formatted_rooms[] = array(
'id' => $room->id,
'name' => $room->name,
'capacity' => $room->capacity,
'location' => $room->location,
'amenities' => $room->amenities,
'description' => $room->description
);
}
wp_send_json_success($formatted_rooms);
}
/**
* 获取资源列表
*/
public function get_resources() {
if (!check_ajax_referer('mrb_frontend_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
global $wpdb;
$table_name = $wpdb->prefix . 'resources';
$resources = $wpdb->get_results(
"SELECT * FROM $table_name WHERE status = 1 ORDER BY type, name ASC"
);
$formatted_resources = array();
foreach ($resources as $resource) {
$formatted_resources[] = array(
'id' => $resource->id,
'name' => $resource->name,
'type' => $resource->type,
'description' => $resource->description,
'quantity' => $resource->quantity,
'available' => $resource->quantity // 简化处理,实际需要计算已预订数量
);
}
wp_send_json_success($formatted_resources);
}
/**
* 获取预订数据
*/
public function get_bookings() {
if (!check_ajax_referer('mrb_frontend_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
global $wpdb;
$start = isset($_POST['start']) ? sanitize_text_field($_POST['start']) : date('Y-m-d');
$end = isset($_POST['end']) ? sanitize_text_field($_POST['end']) : date('Y-m-d', strtotime('+1 month'));
$room_id = isset($_POST['room_id']) ? intval($_POST['room_id']) : 0;
$bookings_table = $wpdb->prefix . 'bookings';
$rooms_table = $wpdb->prefix . 'meeting_rooms';
// 构建查询
$where = array("b.start_time >= %s", "b.end_time <= %s", "b.status != 'cancelled'");
$params = array($start, $end);
if ($room_id > 0) {
$where[] = "b.room_id = %d";
$params[] = $room_id;
}
$where_sql = implode(' AND ', $where);
$query = $wpdb->prepare(
"SELECT b.*, r.name as room_name, r.color as room_color
FROM $bookings_table b
LEFT JOIN $rooms_table r ON b.room_id = r.id
WHERE $where_sql
ORDER BY b.start_time ASC",
$params
);
$bookings = $wpdb->get_results($query);
// 格式化FullCalendar事件数据
$events = array();
$current_user_id = get_current_user_id();
foreach ($bookings as $booking) {
$color = '#3788d8'; // 默认蓝色
if ($booking->user_id == $current_user_id) {
$color = '#28a745'; // 用户自己的预订,绿色
} elseif ($booking->status == 'pending') {
$color = '#ffc107'; // 待审核,黄色
}
$events[] = array(
'id' => $booking->id,
'title' => $booking->title . ' (' . $booking->room_name . ')',
'start' => $booking->start_time,
'end' => $booking->end_time,
'color' => $color,
'extendedProps' => array(
'room_id' => $booking->room_id,
'room_name' => $booking->room_name,
'description' => $booking->description,
'attendees' => $booking->attendees,
'status' => $booking->status,
'user_id' => $booking->user_id
)
);
}
wp_send_json_success($events);
}
/**
* 创建新预订
*/
public function create_booking() {
if (!check_ajax_referer('mrb_frontend_nonce', 'nonce', false)) {
wp_die('安全验证失败', 403);
}
// 验证用户登录
if (!is_user_logged_in()) {
wp_send_json_error('请先登录');
}
// 验证输入数据
$required_fields = array('room_id', 'title', 'start_time', 'end_time', 'attendees');
foreach ($required_fields as $field) {
if (empty($_POST[$field])) {
wp_send_json_error('缺少必要字段: ' . $field);
}
}
global $wpdb;
$room_id = intval($_POST['room_id']);
$user_id = get_current_user_id();
$title = sanitize_text_field($_POST['title']);
$description = isset($_POST['description']) ? sanitize_textarea_field($_POST['description']) : '';
$start_time = sanitize_text_field($_POST['start_time']);
$end_time = sanitize_text_field($_POST['end_time']);
$attendees = intval($_POST['attendees']);
$resources = isset($_POST['resources']) ? $_POST['resources'] : array();
// 检查会议室是否存在且可用
$room = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}meeting_rooms WHERE id = %d AND status = 1",
$room_id
));
if (!$room) {
wp_send_json_error('会议室不存在或不可用');
}
// 检查容量
if ($attendees > $room->capacity) {
wp_send_json_error('参会人数超过会议室容量');
}
// 检查时间冲突
$conflict = $wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM {$wpdb->prefix}bookings
WHERE room_id = %d
AND status != 'cancelled'
AND (
(start_time < %s AND end_time > %s) OR
(start_time >= %s AND start_time < %s) OR
(end_time > %s AND end_time <= %s)
)",
$room_id,
$end_time, $start_time,
$start_time, $end_time,
$start_time, $end_time
));
if ($conflict > 0) {
wp_send_json_error('该时间段已被预订');
}
// 检查资源可用性
$resource_errors = array();
if (is_array($resources) && !empty($resources)) {
foreach ($resources as $resource_id => $quantity) {
$resource_id = intval($resource_id);
$quantity = intval($quantity);
if ($quantity > 0) {
// 检查资源是否存在
$resource = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM {$wpdb->prefix}resources WHERE id = %d AND status = 1",
$resource_id
));
if (!$resource) {
$resource_errors[] = "资源ID {$resource_id} 不存在";
continue
