文章目录
- 在数字化浪潮席卷各行各业的今天,一个高效、便捷的在线咨询预约系统已成为许多服务型企业和专业人士的刚需。无论是医疗诊所、法律事务所、教育机构还是个人咨询师,都需要一套能够自动化管理预约流程的系统。本文将详细介绍如何通过WordPress二次开发,构建一个功能完善、稳定可靠的在线咨询预约系统。
- 在开始开发之前,我们需要明确系统的核心需求: 用户端功能:客户可以查看可预约时间段、选择服务类型、填写基本信息、提交预约请求 管理端功能:管理员可以设置服务项目、管理预约时间、查看预约详情、处理预约状态 通知系统:自动向客户和管理员发送预约确认、提醒和变更通知 日历集成:直观展示可预约时间段,避免时间冲突 支付集成(可选):支持预约定金或全款支付 基于这些需求,我们将系统分为以下几个模块:用户预约模块、管理后台模块、日历显示模块和通知模块。
-
- 首先确保你的WordPress网站已经安装并运行正常。建议使用最新版本的WordPress,并选择一款轻量级、响应式的主题作为基础。
- 虽然我们的目标是自定义开发,但一些基础插件可以大大提高开发效率: Advanced Custom Fields (ACF):用于创建自定义字段 缓存插件:如WP Rocket,提升网站性能 安全插件:如Wordfence,增强系统安全性
- 前端:主要使用HTML5、CSS3、JavaScript(配合jQuery) 后端:PHP 7.4+,利用WordPress钩子函数和自定义文章类型 数据库:MySQL,使用WordPress数据库API进行操作 日历组件:FullCalendar.js(开源JavaScript日历库) 通知服务:使用WP Mail SMTP插件配置邮件发送,或集成第三方短信API
-
- 在主题的functions.php文件中添加以下代码,创建“预约”和“服务”两种自定义文章类型: // 创建服务自定义文章类型 function create_service_post_type() { register_post_type('service', array( 'labels' => array( 'name' => __('服务项目'), 'singular_name' => __('服务项目') ), 'public' => true, 'has_archive' => true, 'menu_icon' => 'dashicons-calendar-alt', 'supports' => array('title', 'editor', 'thumbnail') ) ); } add_action('init', 'create_service_post_type'); // 创建预约自定义文章类型 function create_appointment_post_type() { register_post_type('appointment', array( 'labels' => array( 'name' => __('预约'), 'singular_name' => __('预约') ), 'public' => false, 'show_ui' => true, 'has_archive' => false, 'menu_icon' => 'dashicons-clock', 'supports' => array('title') ) ); } add_action('init', 'create_appointment_post_type');
- 通过ACF插件为“预约”文章类型添加以下字段组: 客户姓名(文本) 客户邮箱(邮箱) 客户电话(文本) 预约服务(关联到服务文章类型) 预约日期(日期选择器) 预约时间(选择框) 预约状态(选择框:待确认、已确认、已完成、已取消) 备注(文本区域)
-
- 在functions.php中添加短代码函数,方便在任何页面插入预约表单: function appointment_booking_shortcode() { ob_start(); ?> <div id="appointment-booking-system"> <!-- 这里将放置预约界面 --> </div> <?php return ob_get_clean(); } add_shortcode('appointment_booking', 'appointment_booking_shortcode');
- 使用HTML、CSS和JavaScript创建一个用户友好的多步骤表单: <div class="appointment-steps"> <div class="step active" data-step="1"> <h3>选择服务</h3> <div class="service-list"> <!-- 动态加载服务选项 --> </div> </div> <div class="step" data-step="2"> <h3>选择时间</h3> <div id="booking-calendar"></div> </div> <div class="step" data-step="3"> <h3>填写信息</h3> <form id="appointment-form"> <input type="text" name="client_name" placeholder="您的姓名" required> <input type="email" name="client_email" placeholder="电子邮箱" required> <input type="tel" name="client_phone" placeholder="联系电话" required> <textarea name="notes" placeholder="备注信息"></textarea> <button type="submit">提交预约</button> </form> </div> <div class="step" data-step="4"> <h3>预约成功</h3> <div class="confirmation-message"> <p>您的预约已提交成功!</p> <p>我们已向您的邮箱发送确认信息。</p> </div> </div> </div>
- 在页面中引入FullCalendar库,并配置可预约时间: document.addEventListener('DOMContentLoaded', function() { var calendarEl = document.getElementById('booking-calendar'); var calendar = new FullCalendar.Calendar(calendarEl, { initialView: 'timeGridWeek', slotMinTime: '09:00:00', slotMaxTime: '18:00:00', events: '/wp-json/appointment/v1/available-slots', dateClick: function(info) { // 处理日期选择 selectAppointmentTime(info.dateStr); } }); calendar.render(); });
-
- 为前端提供可预约时间段数据: // 注册REST API路由 add_action('rest_api_init', function() { register_rest_route('appointment/v1', '/available-slots', array( 'methods' => 'GET', 'callback' => 'get_available_slots', 'permission_callback' => '__return_true' )); register_rest_route('appointment/v1', '/book-appointment', array( 'methods' => 'POST', 'callback' => 'book_appointment', 'permission_callback' => '__return_true' )); }); // 获取可预约时间段 function get_available_slots($request) { $date = $request->get_param('date'); // 查询数据库,排除已被预约的时间 // 返回可用时间段数组 } // 处理预约提交 function book_appointment($request) { $data = $request->get_params(); // 验证数据 if(empty($data['client_name']) || empty($data['client_email'])) { return new WP_Error('validation_error', '请填写完整信息', array('status' => 400)); } // 创建预约记录 $appointment_id = wp_insert_post(array( 'post_title' => $data['client_name'] . '的预约', 'post_type' => 'appointment', 'post_status' => 'publish' )); // 保存自定义字段 update_field('client_name', $data['client_name'], $appointment_id); update_field('client_email', $data['client_email'], $appointment_id); update_field('appointment_date', $data['appointment_date'], $appointment_id); update_field('appointment_status', 'pending', $appointment_id); // 发送确认邮件 send_confirmation_email($data); return array( 'success' => true, 'appointment_id' => $appointment_id ); }
- 确保同一时间段不会被重复预约: function is_time_slot_available($date, $time) { $args = array( 'post_type' => 'appointment', 'posts_per_page' => -1, 'meta_query' => array( 'relation' => 'AND', array( 'key' => 'appointment_date', 'value' => $date, 'compare' => '=' ), array( 'key' => 'appointment_time', 'value' => $time, 'compare' => '=' ), array( 'key' => 'appointment_status', 'value' => array('pending', 'confirmed'), 'compare' => 'IN' ) ) ); $existing_appointments = new WP_Query($args); return $existing_appointments->post_count == 0; }
-
- 在WordPress后台添加自定义管理页面,集中显示所有预约: add_action('admin_menu', 'add_appointment_admin_page'); function add_appointment_admin_page() { add_menu_page( '预约管理', '预约管理', 'manage_options', 'appointment-manager', 'render_appointment_admin_page', 'dashicons-calendar', 30 ); } function render_appointment_admin_page() { // 显示预约列表,包括筛选和搜索功能 // 添加批量操作:确认、取消、删除 }
- 在管理后台集成日历视图,直观展示每日预约情况: // 在管理页面添加FullCalendar function enqueue_admin_calendar() { $screen = get_current_screen(); if($screen->id == 'toplevel_page_appointment-manager') { wp_enqueue_script('fullcalendar-admin', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js'); wp_enqueue_style('fullcalendar-admin-css', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css'); } } add_action('admin_enqueue_scripts', 'enqueue_admin_calendar');
-
- 使用WordPress邮件函数发送通知: function send_confirmation_email($appointment_data) { $to = $appointment_data['client_email']; $subject = '您的预约已确认'; $message = "尊敬的" . $appointment_data['client_name'] . ":nn"; $message .= "您的预约已成功提交,详情如下:n"; $message .= "预约时间:" . $appointment_data['appointment_date'] . " " . $appointment_data['appointment_time'] . "n"; $message .= "如有变动,请及时联系我们。nn"; $message .= "感谢您的预约!"; wp_mail($to, $subject, $message); // 同时发送通知给管理员 $admin_email = get_option('admin_email'); wp_mail($admin_email, '新预约通知', '您有一个新的预约请求,请及时处理。'); }
- 设置定时任务,在预约前一天发送提醒: // 创建定时任务 if (!wp_next_scheduled('send_appointment_reminders')) { wp_schedule_event(time(), 'daily', 'send_appointment_reminders'); } add_action('send_appointment_reminders', 'send_daily_reminders'); function send_daily_reminders() { // 查询明天所有的预约 $tomorrow = date('Y-m-d', strtotime('+1 day')); $args = array( 'post_type' => 'appointment', 'posts_per_page' => -1, 'meta_query' => array( array( 'key' => 'appointment_date', 'value' => $tomorrow, 'compare' => '=' ), array( 'key' => 'appointment_status', 'value' => 'confirmed', 'compare' => '=' ) ) ); $appointments = new WP_Query($args); while($appointments->have_posts()) { $appointments->the_post(); $appointment_id = get_the_ID(); $client_email = get_field('client_email', $appointment_id); $client_name = get_field('client_name', $appointment_id); $appointment_time = get_field('appointment_time', $appointment_id); // 发送提醒邮件 $subject = '预约提醒:您明天的预约'; $message = "尊敬的" . $client_name . ":nn"; $message .= "提醒您明天" . $appointment_time . "有预约。n"; $message .= "请准时参加,如有变动请及时联系我们。"; wp_mail($client_email, $subject, $message); } }
- 当基础系统运行稳定后,可以考虑添加以下扩展功能: 在线支付集成:支持微信支付、支付宝等支付方式 客户账户系统:允许客户注册账户,查看预约历史 评价系统:预约完成后邀请客户评价服务 多服务商支持:适用于有多位咨询师或医生的机构 移动端应用:开发配套的移动应用,方便随时管理预约 数据分析仪表盘:统计预约数据,分析业务趋势
- 通过WordPress二次开发构建在线咨询预约系统,既可以利用WordPress成熟的内容管理框架,又能根据具体业务需求进行深度定制。本文介绍的方法和代码示例提供了一个完整的开发路径,从需求分析到功能实现,涵盖了系统开发的主要环节。 值得注意的是,每个业务场景都有其特殊性,在实际开发过程中需要根据具体需求进行调整和优化。建议在开发初期就充分考虑系统的扩展性,以便未来能够轻松添加新功能。 在线预约系统的价值不仅在于自动化流程、减少人工错误,更重要的是提升客户体验,展现专业形象。一个稳定、易用的预约系统将成为您业务发展的重要助力。 最后,记得在系统上线后持续收集用户反馈,不断迭代优化,使系统更好地服务于您的业务需求。祝您开发顺利!
-
- 确保预约系统在各种设备上都能完美显示: /* 移动端优先的响应式设计 */ .appointment-container { max-width: 1200px; margin: 0 auto; padding: 20px; } @media (max-width: 768px) { .appointment-steps { flex-direction: column; } .step { width: 100% !important; margin-bottom: 20px; } #booking-calendar .fc-toolbar { flex-direction: column; } .fc-toolbar-chunk { margin-bottom: 10px; } } /* 触摸友好的交互元素 */ .booking-button, .time-slot { min-height: 44px; /* 苹果推荐的最小触摸尺寸 */ padding: 12px 24px; font-size: 16px; /* 防止iOS缩放 */ } .time-slot:hover, .time-slot:active { transform: scale(1.05); transition: transform 0.2s ease; }
- 在用户填写表单时提供即时验证: // 实时表单验证 function setupRealTimeValidation() { const form = document.getElementById('appointment-form'); const inputs = form.querySelectorAll('input[required], select[required]'); inputs.forEach(input => { input.addEventListener('blur', function() { validateField(this); }); input.addEventListener('input', function() { clearFieldError(this); }); }); } function validateField(field) { const value = field.value.trim(); const errorElement = document.getElementById(`${field.name}-error`); if (!value) { showFieldError(field, '此字段为必填项'); return false; } if (field.type === 'email' && !isValidEmail(value)) { showFieldError(field, '请输入有效的邮箱地址'); return false; } if (field.name === 'client_phone' && !isValidPhone(value)) { showFieldError(field, '请输入有效的电话号码'); return false; } return true; } function showFieldError(field, message) { field.classList.add('error'); let errorElement = document.getElementById(`${field.name}-error`); if (!errorElement) { errorElement = document.createElement('div'); errorElement.id = `${field.name}-error`; errorElement.className = 'field-error'; field.parentNode.appendChild(errorElement); } errorElement.textContent = message; }
-
- 为不同类型的服务设置不同的预约规则: class AppointmentRules { private $service_id; public function __construct($service_id) { $this->service_id = $service_id; } // 获取服务的最短预约提前时间 public function get_min_advance_time() { $min_advance = get_field('min_advance_booking', $this->service_id); return $min_advance ?: 2; // 默认2小时 } // 获取服务的最长预约提前时间 public function get_max_advance_time() { $max_advance = get_field('max_advance_booking', $this->service_id); return $max_advance ?: 30; // 默认30天 } // 检查是否允许预约 public function is_booking_allowed($date, $time) { $datetime = strtotime("$date $time"); $current_time = current_time('timestamp'); // 检查是否在允许的时间范围内 $min_advance_hours = $this->get_min_advance_time(); $max_advance_days = $this->get_max_advance_time(); $min_time = $current_time + ($min_advance_hours * HOUR_IN_SECONDS); $max_time = $current_time + ($max_advance_days * DAY_IN_SECONDS); if ($datetime < $min_time) { return array( 'allowed' => false, 'message' => "需要至少提前{$min_advance_hours}小时预约" ); } if ($datetime > $max_time) { return array( 'allowed' => false, 'message' => "最多只能提前{$max_advance_days}天预约" ); } // 检查节假日 if ($this->is_holiday($date)) { return array( 'allowed' => false, 'message' => "该日期为节假日,不可预约" ); } return array('allowed' => true); } // 节假日检查 private function is_holiday($date) { $holidays = get_field('holidays', 'option'); if (!$holidays) return false; foreach ($holidays as $holiday) { if ($holiday['date'] == $date) { return true; } } return false; } }
- class TimeSlotManager { // 获取可用的时间槽 public function get_available_slots($service_id, $date) { $service_duration = get_field('service_duration', $service_id); $working_hours = $this->get_working_hours($date); $existing_appointments = $this->get_existing_appointments($date); $available_slots = array(); foreach ($working_hours as $time_range) { $slots = $this->generate_time_slots( $time_range['start'], $time_range['end'], $service_duration ); foreach ($slots as $slot) { if (!$this->is_slot_conflicted($slot, $existing_appointments, $service_duration)) { $available_slots[] = $slot; } } } return $available_slots; } // 生成时间槽 private function generate_time_slots($start_time, $end_time, $duration) { $slots = array(); $current = strtotime($start_time); $end = strtotime($end_time); while ($current + ($duration * 60) <= $end) { $slot_end = $current + ($duration * 60); $slots[] = array( 'start' => date('H:i', $current), 'end' => date('H:i', $slot_end) ); $current = $slot_end; } return $slots; } // 检查时间冲突 private function is_slot_conflicted($slot, $existing_appointments, $duration) { $slot_start = strtotime($slot['start']); $slot_end = strtotime($slot['end']); foreach ($existing_appointments as $appointment) { $app_start = strtotime($appointment['time']); $app_end = $app_start + ($appointment['duration'] * 60); // 检查时间重叠 if ($slot_start < $app_end && $slot_end > $app_start) { return true; } } return false; } }
-
- class AppointmentAnalytics { // 获取月度统计数据 public function get_monthly_stats($month, $year) { global $wpdb; $start_date = "$year-$month-01"; $end_date = date('Y-m-t', strtotime($start_date)); $stats = array( 'total_appointments' => 0, 'confirmed_appointments' => 0, 'cancelled_appointments' => 0, 'revenue' => 0, 'popular_services' => array(), 'peak_hours' => array() ); // 获取总预约数 $query = $wpdb->prepare(" SELECT COUNT(*) as count, appointment_status.meta_value as status FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} appointment_date ON p.ID = appointment_date.post_id AND appointment_date.meta_key = 'appointment_date' LEFT JOIN {$wpdb->postmeta} appointment_status ON p.ID = appointment_status.post_id AND appointment_status.meta_key = 'appointment_status' WHERE p.post_type = 'appointment' AND p.post_status = 'publish' AND appointment_date.meta_value BETWEEN %s AND %s GROUP BY appointment_status.meta_value ", $start_date, $end_date); $results = $wpdb->get_results($query); foreach ($results as $row) { $stats['total_appointments'] += $row->count; if ($row->status == 'confirmed') { $stats['confirmed_appointments'] = $row->count; } elseif ($row->status == 'cancelled') { $stats['cancelled_appointments'] = $row->count; } } return $stats; } // 生成预约趋势图表数据 public function get_trend_data($period = 'monthly') { $data = array(); $current_date = new DateTime(); for ($i = 11; $i >= 0; $i--) { if ($period == 'monthly') { $date = clone $current_date; $date->modify("-$i months"); $label = $date->format('Y-m'); $stats = $this->get_monthly_stats( $date->format('m'), $date->format('Y') ); } else { // 周度数据 $date = clone $current_date; $date->modify("-$i weeks"); $label = $date->format('Y-W'); $stats = $this->get_weekly_stats($date); } $data[] = array( 'label' => $label, 'total' => $stats['total_appointments'], 'confirmed' => $stats['confirmed_appointments'], 'cancelled' => $stats['cancelled_appointments'] ); } return $data; } }
- class EfficiencyAnalyzer { // 计算预约到店率 public function calculate_show_up_rate($start_date, $end_date) { $total_confirmed = $this->count_appointments_by_status( 'confirmed', $start_date, $end_date ); $total_completed = $this->count_appointments_by_status( 'completed', $start_date, $end_date ); if ($total_confirmed > 0) { return round(($total_completed / $total_confirmed) * 100, 2); } return 0; } // 计算平均预约提前时间 public function calculate_average_lead_time($start_date, $end_date) { global $wpdb; $query = $wpdb->prepare(" SELECT AVG(TIMESTAMPDIFF(HOUR, STR_TO_DATE(CONCAT(appointment_date.meta_value, ' ', appointment_time.meta_value), '%%Y-%%m-%%d %%H:%%i'), created.post_date )) as avg_lead_time FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} appointment_date ON p.ID = appointment_date.post_id AND appointment_date.meta_key = 'appointment_date' LEFT JOIN {$wpdb->postmeta} appointment_time ON p.ID = appointment_time.post_id AND appointment_time.meta_key = 'appointment_time' LEFT JOIN {$wpdb->posts} created ON p.ID = created.ID WHERE p.post_type = 'appointment' AND p.post_status = 'publish' AND appointment_date.meta_value BETWEEN %s AND %s AND appointment_time.meta_value IS NOT NULL ", $start_date, $end_date); $result = $wpdb->get_var($query); return round($result, 1); } }
-
- class CalendarSync { private $service; public function __construct($service = 'google') { switch ($service) { case 'google': $this->service = new GoogleCalendarSync(); break; case 'outlook': $this->service = new OutlookCalendarSync(); break; default: throw new Exception('不支持的日历服务'); } } // 同步预约到外部日历 public function sync_appointment($appointment_id, $action = 'create') { $appointment_data = $this->get_appointment_data($appointment_id); switch ($action) { case 'create': return $this->service->create_event($appointment_data); case 'update': return $this->service->update_event($appointment_data); case 'delete': return $this->service->delete_event($appointment_data['external_id']); } } // 从外部日历同步可用时间 public function sync_availability($date) { $busy_slots = $this->service->get_busy_slots($date); return $this->convert_to_unavailable_slots($busy_slots); } } class GoogleCalendarSync { private $client; private $service; public function __construct() { $this->client = new Google_Client(); $this->client->setAuthConfig(get_stylesheet_directory() . '/config/google-calendar.json'); $this->client->addScope(Google_Service_Calendar::CALENDAR); $this->service = new Google_Service_Calendar($this->client); } public function create_event($appointment_data) { $event = new Google_Service_Calendar_Event(array( 'summary' => '咨询预约 - ' . $appointment_data['client_name'], 'description' => $appointment_data['notes'], 'start' => array( 'dateTime' => $appointment_data['start_datetime'], 'timeZone' => 'Asia/Shanghai', ), 'end' => array( 'dateTime' => $appointment_data['end_datetime'], 'timeZone' => 'Asia/Shanghai', ), 'attendees' => array( array('email' => $appointment_data['client_email']), array('email' => get_option('admin_email')) ), 'reminders' => array( 'useDefault' => false, 'overrides' => array( array('method' => 'email', 'minutes' => 24 * 60), array('method' => 'popup', 'minutes' => 30), ), ), )); $calendarId = 'primary'; $event = $this->service->events->insert($calendarId, $event); return $event->getId(); } }
- class WechatMiniProgram { private $app_id; private $app_secret; public function __construct() { $this->app_id = get_option('wechat_app_id'); $this->app_secret = get_option('wechat_app_secret'); } // 获取微信用户openid public function get_user_openid($code) { $url = "https://api.weixin.qq.com/sns/jscode2session"; $params = array( 'appid' => $this->app_id, 'secret' => $this->app_secret, 'js_code' => $code, 'grant_type' => 'authorization_code' ); $response = wp_remote_get($url . '?' . http_build_query($params)); if (is_wp_error($response)) { return false; } $body = json_decode(wp_remote_retrieve_body($response), true); return isset($body['openid']) ? $body['openid'] : false; } // 发送预约提醒模板消息 public function send_appointment_reminder($openid, $appointment_data) { $access_token = $this->get_access_token(); $url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={$access_token}"; $data = array( 'touser' => $openid, 'template_id' => get_option('wechat_reminder_template_id'), 'page' => 'pages/appointment/detail?id=' . $appointment_data['id'], 'data' => array( 'thing1' => array('value' => $appointment_data['service_name']), 'time2' => array('value' => $appointment_data['appointment_time']), 'thing3' => array('value' => '请准时到达') ) ); $response = wp_remote_post($url, array( 'headers' => array('Content-Type' => 'application/json'), 'body' => json_encode($data), 'timeout' => 10 )); return !is_wp_error($response); } }
在数字化浪潮席卷各行各业的今天,一个高效、便捷的在线咨询预约系统已成为许多服务型企业和专业人士的刚需。无论是医疗诊所、法律事务所、教育机构还是个人咨询师,都需要一套能够自动化管理预约流程的系统。本文将详细介绍如何通过WordPress二次开发,构建一个功能完善、稳定可靠的在线咨询预约系统。
在开始开发之前,我们需要明确系统的核心需求:
- 用户端功能:客户可以查看可预约时间段、选择服务类型、填写基本信息、提交预约请求
- 管理端功能:管理员可以设置服务项目、管理预约时间、查看预约详情、处理预约状态
- 通知系统:自动向客户和管理员发送预约确认、提醒和变更通知
- 日历集成:直观展示可预约时间段,避免时间冲突
- 支付集成(可选):支持预约定金或全款支付
基于这些需求,我们将系统分为以下几个模块:用户预约模块、管理后台模块、日历显示模块和通知模块。
首先确保你的WordPress网站已经安装并运行正常。建议使用最新版本的WordPress,并选择一款轻量级、响应式的主题作为基础。
虽然我们的目标是自定义开发,但一些基础插件可以大大提高开发效率:
- Advanced Custom Fields (ACF):用于创建自定义字段
- 缓存插件:如WP Rocket,提升网站性能
- 安全插件:如Wordfence,增强系统安全性
- 前端:主要使用HTML5、CSS3、JavaScript(配合jQuery)
- 后端:PHP 7.4+,利用WordPress钩子函数和自定义文章类型
- 数据库:MySQL,使用WordPress数据库API进行操作
- 日历组件:FullCalendar.js(开源JavaScript日历库)
- 通知服务:使用WP Mail SMTP插件配置邮件发送,或集成第三方短信API
在主题的functions.php文件中添加以下代码,创建“预约”和“服务”两种自定义文章类型:
// 创建服务自定义文章类型
function create_service_post_type() {
register_post_type('service',
array(
'labels' => array(
'name' => __('服务项目'),
'singular_name' => __('服务项目')
),
'public' => true,
'has_archive' => true,
'menu_icon' => 'dashicons-calendar-alt',
'supports' => array('title', 'editor', 'thumbnail')
)
);
}
add_action('init', 'create_service_post_type');
// 创建预约自定义文章类型
function create_appointment_post_type() {
register_post_type('appointment',
array(
'labels' => array(
'name' => __('预约'),
'singular_name' => __('预约')
),
'public' => false,
'show_ui' => true,
'has_archive' => false,
'menu_icon' => 'dashicons-clock',
'supports' => array('title')
)
);
}
add_action('init', 'create_appointment_post_type');
通过ACF插件为“预约”文章类型添加以下字段组:
- 客户姓名(文本)
- 客户邮箱(邮箱)
- 客户电话(文本)
- 预约服务(关联到服务文章类型)
- 预约日期(日期选择器)
- 预约时间(选择框)
- 预约状态(选择框:待确认、已确认、已完成、已取消)
- 备注(文本区域)
在functions.php中添加短代码函数,方便在任何页面插入预约表单:
function appointment_booking_shortcode() {
ob_start();
?>
<div id="appointment-booking-system">
<!-- 这里将放置预约界面 -->
</div>
<?php
return ob_get_clean();
}
add_shortcode('appointment_booking', 'appointment_booking_shortcode');
使用HTML、CSS和JavaScript创建一个用户友好的多步骤表单:
<div class="appointment-steps">
<div class="step active" data-step="1">
<h3>选择服务</h3>
<div class="service-list">
<!-- 动态加载服务选项 -->
</div>
</div>
<div class="step" data-step="2">
<h3>选择时间</h3>
<div id="booking-calendar"></div>
</div>
<div class="step" data-step="3">
<h3>填写信息</h3>
<form id="appointment-form">
<input type="text" name="client_name" placeholder="您的姓名" required>
<input type="email" name="client_email" placeholder="电子邮箱" required>
<input type="tel" name="client_phone" placeholder="联系电话" required>
<textarea name="notes" placeholder="备注信息"></textarea>
<button type="submit">提交预约</button>
</form>
</div>
<div class="step" data-step="4">
<h3>预约成功</h3>
<div class="confirmation-message">
<p>您的预约已提交成功!</p>
<p>我们已向您的邮箱发送确认信息。</p>
</div>
</div>
</div>
在页面中引入FullCalendar库,并配置可预约时间:
document.addEventListener('DOMContentLoaded', function() {
var calendarEl = document.getElementById('booking-calendar');
var calendar = new FullCalendar.Calendar(calendarEl, {
initialView: 'timeGridWeek',
slotMinTime: '09:00:00',
slotMaxTime: '18:00:00',
events: '/wp-json/appointment/v1/available-slots',
dateClick: function(info) {
// 处理日期选择
selectAppointmentTime(info.dateStr);
}
});
calendar.render();
});
为前端提供可预约时间段数据:
// 注册REST API路由
add_action('rest_api_init', function() {
register_rest_route('appointment/v1', '/available-slots', array(
'methods' => 'GET',
'callback' => 'get_available_slots',
'permission_callback' => '__return_true'
));
register_rest_route('appointment/v1', '/book-appointment', array(
'methods' => 'POST',
'callback' => 'book_appointment',
'permission_callback' => '__return_true'
));
});
// 获取可预约时间段
function get_available_slots($request) {
$date = $request->get_param('date');
// 查询数据库,排除已被预约的时间
// 返回可用时间段数组
}
// 处理预约提交
function book_appointment($request) {
$data = $request->get_params();
// 验证数据
if(empty($data['client_name']) || empty($data['client_email'])) {
return new WP_Error('validation_error', '请填写完整信息', array('status' => 400));
}
// 创建预约记录
$appointment_id = wp_insert_post(array(
'post_title' => $data['client_name'] . '的预约',
'post_type' => 'appointment',
'post_status' => 'publish'
));
// 保存自定义字段
update_field('client_name', $data['client_name'], $appointment_id);
update_field('client_email', $data['client_email'], $appointment_id);
update_field('appointment_date', $data['appointment_date'], $appointment_id);
update_field('appointment_status', 'pending', $appointment_id);
// 发送确认邮件
send_confirmation_email($data);
return array(
'success' => true,
'appointment_id' => $appointment_id
);
}
确保同一时间段不会被重复预约:
function is_time_slot_available($date, $time) {
$args = array(
'post_type' => 'appointment',
'posts_per_page' => -1,
'meta_query' => array(
'relation' => 'AND',
array(
'key' => 'appointment_date',
'value' => $date,
'compare' => '='
),
array(
'key' => 'appointment_time',
'value' => $time,
'compare' => '='
),
array(
'key' => 'appointment_status',
'value' => array('pending', 'confirmed'),
'compare' => 'IN'
)
)
);
$existing_appointments = new WP_Query($args);
return $existing_appointments->post_count == 0;
}
在WordPress后台添加自定义管理页面,集中显示所有预约:
add_action('admin_menu', 'add_appointment_admin_page');
function add_appointment_admin_page() {
add_menu_page(
'预约管理',
'预约管理',
'manage_options',
'appointment-manager',
'render_appointment_admin_page',
'dashicons-calendar',
30
);
}
function render_appointment_admin_page() {
// 显示预约列表,包括筛选和搜索功能
// 添加批量操作:确认、取消、删除
}
在管理后台集成日历视图,直观展示每日预约情况:
// 在管理页面添加FullCalendar
function enqueue_admin_calendar() {
$screen = get_current_screen();
if($screen->id == 'toplevel_page_appointment-manager') {
wp_enqueue_script('fullcalendar-admin', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.js');
wp_enqueue_style('fullcalendar-admin-css', 'https://cdn.jsdelivr.net/npm/fullcalendar@5.10.1/main.min.css');
}
}
add_action('admin_enqueue_scripts', 'enqueue_admin_calendar');
使用WordPress邮件函数发送通知:
function send_confirmation_email($appointment_data) {
$to = $appointment_data['client_email'];
$subject = '您的预约已确认';
$message = "尊敬的" . $appointment_data['client_name'] . ":nn";
$message .= "您的预约已成功提交,详情如下:n";
$message .= "预约时间:" . $appointment_data['appointment_date'] . " " . $appointment_data['appointment_time'] . "n";
$message .= "如有变动,请及时联系我们。nn";
$message .= "感谢您的预约!";
wp_mail($to, $subject, $message);
// 同时发送通知给管理员
$admin_email = get_option('admin_email');
wp_mail($admin_email, '新预约通知', '您有一个新的预约请求,请及时处理。');
}
设置定时任务,在预约前一天发送提醒:
// 创建定时任务
if (!wp_next_scheduled('send_appointment_reminders')) {
wp_schedule_event(time(), 'daily', 'send_appointment_reminders');
}
add_action('send_appointment_reminders', 'send_daily_reminders');
function send_daily_reminders() {
// 查询明天所有的预约
$tomorrow = date('Y-m-d', strtotime('+1 day'));
$args = array(
'post_type' => 'appointment',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'appointment_date',
'value' => $tomorrow,
'compare' => '='
),
array(
'key' => 'appointment_status',
'value' => 'confirmed',
'compare' => '='
)
)
);
$appointments = new WP_Query($args);
while($appointments->have_posts()) {
$appointments->the_post();
$appointment_id = get_the_ID();
$client_email = get_field('client_email', $appointment_id);
$client_name = get_field('client_name', $appointment_id);
$appointment_time = get_field('appointment_time', $appointment_id);
// 发送提醒邮件
$subject = '预约提醒:您明天的预约';
$message = "尊敬的" . $client_name . ":nn";
$message .= "提醒您明天" . $appointment_time . "有预约。n";
$message .= "请准时参加,如有变动请及时联系我们。";
wp_mail($client_email, $subject, $message);
}
}
- 测试预约流程的完整性
- 验证时间冲突检测的准确性
- 检查邮件通知的送达情况
- 测试管理后台的各项操作
- 对数据库查询进行缓存优化
- 使用AJAX加载日历数据,提高响应速度
- 压缩前端资源文件
- 对高并发场景进行压力测试
- 对所有用户输入进行验证和清理
- 使用nonce防止CSRF攻击
- 限制API访问频率
- 定期备份预约数据
当基础系统运行稳定后,可以考虑添加以下扩展功能:
- 在线支付集成:支持微信支付、支付宝等支付方式
- 客户账户系统:允许客户注册账户,查看预约历史
- 评价系统:预约完成后邀请客户评价服务
- 多服务商支持:适用于有多位咨询师或医生的机构
- 移动端应用:开发配套的移动应用,方便随时管理预约
- 数据分析仪表盘:统计预约数据,分析业务趋势
通过WordPress二次开发构建在线咨询预约系统,既可以利用WordPress成熟的内容管理框架,又能根据具体业务需求进行深度定制。本文介绍的方法和代码示例提供了一个完整的开发路径,从需求分析到功能实现,涵盖了系统开发的主要环节。
值得注意的是,每个业务场景都有其特殊性,在实际开发过程中需要根据具体需求进行调整和优化。建议在开发初期就充分考虑系统的扩展性,以便未来能够轻松添加新功能。
在线预约系统的价值不仅在于自动化流程、减少人工错误,更重要的是提升客户体验,展现专业形象。一个稳定、易用的预约系统将成为您业务发展的重要助力。
最后,记得在系统上线后持续收集用户反馈,不断迭代优化,使系统更好地服务于您的业务需求。祝您开发顺利!
确保预约系统在各种设备上都能完美显示:
/* 移动端优先的响应式设计 */
.appointment-container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
@media (max-width: 768px) {
.appointment-steps {
flex-direction: column;
}
.step {
width: 100% !important;
margin-bottom: 20px;
}
#booking-calendar .fc-toolbar {
flex-direction: column;
}
.fc-toolbar-chunk {
margin-bottom: 10px;
}
}
/* 触摸友好的交互元素 */
.booking-button, .time-slot {
min-height: 44px; /* 苹果推荐的最小触摸尺寸 */
padding: 12px 24px;
font-size: 16px; /* 防止iOS缩放 */
}
.time-slot:hover, .time-slot:active {
transform: scale(1.05);
transition: transform 0.2s ease;
}
在用户填写表单时提供即时验证:
// 实时表单验证
function setupRealTimeValidation() {
const form = document.getElementById('appointment-form');
const inputs = form.querySelectorAll('input[required], select[required]');
inputs.forEach(input => {
input.addEventListener('blur', function() {
validateField(this);
});
input.addEventListener('input', function() {
clearFieldError(this);
});
});
}
function validateField(field) {
const value = field.value.trim();
const errorElement = document.getElementById(`${field.name}-error`);
if (!value) {
showFieldError(field, '此字段为必填项');
return false;
}
if (field.type === 'email' && !isValidEmail(value)) {
showFieldError(field, '请输入有效的邮箱地址');
return false;
}
if (field.name === 'client_phone' && !isValidPhone(value)) {
showFieldError(field, '请输入有效的电话号码');
return false;
}
return true;
}
function showFieldError(field, message) {
field.classList.add('error');
let errorElement = document.getElementById(`${field.name}-error`);
if (!errorElement) {
errorElement = document.createElement('div');
errorElement.id = `${field.name}-error`;
errorElement.className = 'field-error';
field.parentNode.appendChild(errorElement);
}
errorElement.textContent = message;
}
为不同类型的服务设置不同的预约规则:
class AppointmentRules {
private $service_id;
public function __construct($service_id) {
$this->service_id = $service_id;
}
// 获取服务的最短预约提前时间
public function get_min_advance_time() {
$min_advance = get_field('min_advance_booking', $this->service_id);
return $min_advance ?: 2; // 默认2小时
}
// 获取服务的最长预约提前时间
public function get_max_advance_time() {
$max_advance = get_field('max_advance_booking', $this->service_id);
return $max_advance ?: 30; // 默认30天
}
// 检查是否允许预约
public function is_booking_allowed($date, $time) {
$datetime = strtotime("$date $time");
$current_time = current_time('timestamp');
// 检查是否在允许的时间范围内
$min_advance_hours = $this->get_min_advance_time();
$max_advance_days = $this->get_max_advance_time();
$min_time = $current_time + ($min_advance_hours * HOUR_IN_SECONDS);
$max_time = $current_time + ($max_advance_days * DAY_IN_SECONDS);
if ($datetime < $min_time) {
return array(
'allowed' => false,
'message' => "需要至少提前{$min_advance_hours}小时预约"
);
}
if ($datetime > $max_time) {
return array(
'allowed' => false,
'message' => "最多只能提前{$max_advance_days}天预约"
);
}
// 检查节假日
if ($this->is_holiday($date)) {
return array(
'allowed' => false,
'message' => "该日期为节假日,不可预约"
);
}
return array('allowed' => true);
}
// 节假日检查
private function is_holiday($date) {
$holidays = get_field('holidays', 'option');
if (!$holidays) return false;
foreach ($holidays as $holiday) {
if ($holiday['date'] == $date) {
return true;
}
}
return false;
}
}
class TimeSlotManager {
// 获取可用的时间槽
public function get_available_slots($service_id, $date) {
$service_duration = get_field('service_duration', $service_id);
$working_hours = $this->get_working_hours($date);
$existing_appointments = $this->get_existing_appointments($date);
$available_slots = array();
foreach ($working_hours as $time_range) {
$slots = $this->generate_time_slots(
$time_range['start'],
$time_range['end'],
$service_duration
);
foreach ($slots as $slot) {
if (!$this->is_slot_conflicted($slot, $existing_appointments, $service_duration)) {
$available_slots[] = $slot;
}
}
}
return $available_slots;
}
// 生成时间槽
private function generate_time_slots($start_time, $end_time, $duration) {
$slots = array();
$current = strtotime($start_time);
$end = strtotime($end_time);
while ($current + ($duration * 60) <= $end) {
$slot_end = $current + ($duration * 60);
$slots[] = array(
'start' => date('H:i', $current),
'end' => date('H:i', $slot_end)
);
$current = $slot_end;
}
return $slots;
}
// 检查时间冲突
private function is_slot_conflicted($slot, $existing_appointments, $duration) {
$slot_start = strtotime($slot['start']);
$slot_end = strtotime($slot['end']);
foreach ($existing_appointments as $appointment) {
$app_start = strtotime($appointment['time']);
$app_end = $app_start + ($appointment['duration'] * 60);
// 检查时间重叠
if ($slot_start < $app_end && $slot_end > $app_start) {
return true;
}
}
return false;
}
}
class TimeSlotManager {
// 获取可用的时间槽
public function get_available_slots($service_id, $date) {
$service_duration = get_field('service_duration', $service_id);
$working_hours = $this->get_working_hours($date);
$existing_appointments = $this->get_existing_appointments($date);
$available_slots = array();
foreach ($working_hours as $time_range) {
$slots = $this->generate_time_slots(
$time_range['start'],
$time_range['end'],
$service_duration
);
foreach ($slots as $slot) {
if (!$this->is_slot_conflicted($slot, $existing_appointments, $service_duration)) {
$available_slots[] = $slot;
}
}
}
return $available_slots;
}
// 生成时间槽
private function generate_time_slots($start_time, $end_time, $duration) {
$slots = array();
$current = strtotime($start_time);
$end = strtotime($end_time);
while ($current + ($duration * 60) <= $end) {
$slot_end = $current + ($duration * 60);
$slots[] = array(
'start' => date('H:i', $current),
'end' => date('H:i', $slot_end)
);
$current = $slot_end;
}
return $slots;
}
// 检查时间冲突
private function is_slot_conflicted($slot, $existing_appointments, $duration) {
$slot_start = strtotime($slot['start']);
$slot_end = strtotime($slot['end']);
foreach ($existing_appointments as $appointment) {
$app_start = strtotime($appointment['time']);
$app_end = $app_start + ($appointment['duration'] * 60);
// 检查时间重叠
if ($slot_start < $app_end && $slot_end > $app_start) {
return true;
}
}
return false;
}
}
class AppointmentAnalytics {
// 获取月度统计数据
public function get_monthly_stats($month, $year) {
global $wpdb;
$start_date = "$year-$month-01";
$end_date = date('Y-m-t', strtotime($start_date));
$stats = array(
'total_appointments' => 0,
'confirmed_appointments' => 0,
'cancelled_appointments' => 0,
'revenue' => 0,
'popular_services' => array(),
'peak_hours' => array()
);
// 获取总预约数
$query = $wpdb->prepare("
SELECT COUNT(*) as count,
appointment_status.meta_value as status
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} appointment_date
ON p.ID = appointment_date.post_id
AND appointment_date.meta_key = 'appointment_date'
LEFT JOIN {$wpdb->postmeta} appointment_status
ON p.ID = appointment_status.post_id
AND appointment_status.meta_key = 'appointment_status'
WHERE p.post_type = 'appointment'
AND p.post_status = 'publish'
AND appointment_date.meta_value BETWEEN %s AND %s
GROUP BY appointment_status.meta_value
", $start_date, $end_date);
$results = $wpdb->get_results($query);
foreach ($results as $row) {
$stats['total_appointments'] += $row->count;
if ($row->status == 'confirmed') {
$stats['confirmed_appointments'] = $row->count;
} elseif ($row->status == 'cancelled') {
$stats['cancelled_appointments'] = $row->count;
}
}
return $stats;
}
// 生成预约趋势图表数据
public function get_trend_data($period = 'monthly') {
$data = array();
$current_date = new DateTime();
for ($i = 11; $i >= 0; $i--) {
if ($period == 'monthly') {
$date = clone $current_date;
$date->modify("-$i months");
$label = $date->format('Y-m');
$stats = $this->get_monthly_stats(
$date->format('m'),
$date->format('Y')
);
} else {
// 周度数据
$date = clone $current_date;
$date->modify("-$i weeks");
$label = $date->format('Y-W');
$stats = $this->get_weekly_stats($date);
}
$data[] = array(
'label' => $label,
'total' => $stats['total_appointments'],
'confirmed' => $stats['confirmed_appointments'],
'cancelled' => $stats['cancelled_appointments']
);
}
return $data;
}
}
class AppointmentAnalytics {
// 获取月度统计数据
public function get_monthly_stats($month, $year) {
global $wpdb;
$start_date = "$year-$month-01";
$end_date = date('Y-m-t', strtotime($start_date));
$stats = array(
'total_appointments' => 0,
'confirmed_appointments' => 0,
'cancelled_appointments' => 0,
'revenue' => 0,
'popular_services' => array(),
'peak_hours' => array()
);
// 获取总预约数
$query = $wpdb->prepare("
SELECT COUNT(*) as count,
appointment_status.meta_value as status
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} appointment_date
ON p.ID = appointment_date.post_id
AND appointment_date.meta_key = 'appointment_date'
LEFT JOIN {$wpdb->postmeta} appointment_status
ON p.ID = appointment_status.post_id
AND appointment_status.meta_key = 'appointment_status'
WHERE p.post_type = 'appointment'
AND p.post_status = 'publish'
AND appointment_date.meta_value BETWEEN %s AND %s
GROUP BY appointment_status.meta_value
", $start_date, $end_date);
$results = $wpdb->get_results($query);
foreach ($results as $row) {
$stats['total_appointments'] += $row->count;
if ($row->status == 'confirmed') {
$stats['confirmed_appointments'] = $row->count;
} elseif ($row->status == 'cancelled') {
$stats['cancelled_appointments'] = $row->count;
}
}
return $stats;
}
// 生成预约趋势图表数据
public function get_trend_data($period = 'monthly') {
$data = array();
$current_date = new DateTime();
for ($i = 11; $i >= 0; $i--) {
if ($period == 'monthly') {
$date = clone $current_date;
$date->modify("-$i months");
$label = $date->format('Y-m');
$stats = $this->get_monthly_stats(
$date->format('m'),
$date->format('Y')
);
} else {
// 周度数据
$date = clone $current_date;
$date->modify("-$i weeks");
$label = $date->format('Y-W');
$stats = $this->get_weekly_stats($date);
}
$data[] = array(
'label' => $label,
'total' => $stats['total_appointments'],
'confirmed' => $stats['confirmed_appointments'],
'cancelled' => $stats['cancelled_appointments']
);
}
return $data;
}
}
class EfficiencyAnalyzer {
// 计算预约到店率
public function calculate_show_up_rate($start_date, $end_date) {
$total_confirmed = $this->count_appointments_by_status(
'confirmed',
$start_date,
$end_date
);
$total_completed = $this->count_appointments_by_status(
'completed',
$start_date,
$end_date
);
if ($total_confirmed > 0) {
return round(($total_completed / $total_confirmed) * 100, 2);
}
return 0;
}
// 计算平均预约提前时间
public function calculate_average_lead_time($start_date, $end_date) {
global $wpdb;
$query = $wpdb->prepare("
SELECT AVG(TIMESTAMPDIFF(HOUR,
STR_TO_DATE(CONCAT(appointment_date.meta_value, ' ', appointment_time.meta_value), '%%Y-%%m-%%d %%H:%%i'),
created.post_date
)) as avg_lead_time
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} appointment_date
ON p.ID = appointment_date.post_id
AND appointment_date.meta_key = 'appointment_date'
LEFT JOIN {$wpdb->postmeta} appointment_time
ON p.ID = appointment_time.post_id
AND appointment_time.meta_key = 'appointment_time'
LEFT JOIN {$wpdb->posts} created
ON p.ID = created.ID
WHERE p.post_type = 'appointment'
AND p.post_status = 'publish'
AND appointment_date.meta_value BETWEEN %s AND %s
AND appointment_time.meta_value IS NOT NULL
", $start_date, $end_date);
$result = $wpdb->get_var($query);
return round($result, 1);
}
}
class EfficiencyAnalyzer {
// 计算预约到店率
public function calculate_show_up_rate($start_date, $end_date) {
$total_confirmed = $this->count_appointments_by_status(
'confirmed',
$start_date,
$end_date
);
$total_completed = $this->count_appointments_by_status(
'completed',
$start_date,
$end_date
);
if ($total_confirmed > 0) {
return round(($total_completed / $total_confirmed) * 100, 2);
}
return 0;
}
// 计算平均预约提前时间
public function calculate_average_lead_time($start_date, $end_date) {
global $wpdb;
$query = $wpdb->prepare("
SELECT AVG(TIMESTAMPDIFF(HOUR,
STR_TO_DATE(CONCAT(appointment_date.meta_value, ' ', appointment_time.meta_value), '%%Y-%%m-%%d %%H:%%i'),
created.post_date
)) as avg_lead_time
FROM {$wpdb->posts} p
LEFT JOIN {$wpdb->postmeta} appointment_date
ON p.ID = appointment_date.post_id
AND appointment_date.meta_key = 'appointment_date'
LEFT JOIN {$wpdb->postmeta} appointment_time
ON p.ID = appointment_time.post_id
AND appointment_time.meta_key = 'appointment_time'
LEFT JOIN {$wpdb->posts} created
ON p.ID = created.ID
WHERE p.post_type = 'appointment'
AND p.post_status = 'publish'
AND appointment_date.meta_value BETWEEN %s AND %s
AND appointment_time.meta_value IS NOT NULL
", $start_date, $end_date);
$result = $wpdb->get_var($query);
return round($result, 1);
}
}
class CalendarSync {
private $service;
public function __construct($service = 'google') {
switch ($service) {
case 'google':
$this->service = new GoogleCalendarSync();
break;
case 'outlook':
$this->service = new OutlookCalendarSync();
break;
default:
throw new Exception('不支持的日历服务');
}
}
// 同步预约到外部日历
public function sync_appointment($appointment_id, $action = 'create') {
$appointment_data = $this->get_appointment_data($appointment_id);
switch ($action) {
case 'create':
return $this->service->create_event($appointment_data);
case 'update':
return $this->service->update_event($appointment_data);
case 'delete':
return $this->service->delete_event($appointment_data['external_id']);
}
}
// 从外部日历同步可用时间
public function sync_availability($date) {
$busy_slots = $this->service->get_busy_slots($date);
return $this->convert_to_unavailable_slots($busy_slots);
}
}
class GoogleCalendarSync {
private $client;
private $service;
public function __construct() {
$this->client = new Google_Client();
$this->client->setAuthConfig(get_stylesheet_directory() . '/config/google-calendar.json');
$this->client->addScope(Google_Service_Calendar::CALENDAR);
$this->service = new Google_Service_Calendar($this->client);
}
public function create_event($appointment_data) {
$event = new Google_Service_Calendar_Event(array(
'summary' => '咨询预约 - ' . $appointment_data['client_name'],
'description' => $appointment_data['notes'],
'start' => array(
'dateTime' => $appointment_data['start_datetime'],
'timeZone' => 'Asia/Shanghai',
),
'end' => array(
'dateTime' => $appointment_data['end_datetime'],
'timeZone' => 'Asia/Shanghai',
),
'attendees' => array(
array('email' => $appointment_data['client_email']),
array('email' => get_option('admin_email'))
),
'reminders' => array(
'useDefault' => false,
'overrides' => array(
array('method' => 'email', 'minutes' => 24 * 60),
array('method' => 'popup', 'minutes' => 30),
),
),
));
$calendarId = 'primary';
$event = $this->service->events->insert($calendarId, $event);
return $event->getId();
}
}
class CalendarSync {
private $service;
public function __construct($service = 'google') {
switch ($service) {
case 'google':
$this->service = new GoogleCalendarSync();
break;
case 'outlook':
$this->service = new OutlookCalendarSync();
break;
default:
throw new Exception('不支持的日历服务');
}
}
// 同步预约到外部日历
public function sync_appointment($appointment_id, $action = 'create') {
$appointment_data = $this->get_appointment_data($appointment_id);
switch ($action) {
case 'create':
return $this->service->create_event($appointment_data);
case 'update':
return $this->service->update_event($appointment_data);
case 'delete':
return $this->service->delete_event($appointment_data['external_id']);
}
}
// 从外部日历同步可用时间
public function sync_availability($date) {
$busy_slots = $this->service->get_busy_slots($date);
return $this->convert_to_unavailable_slots($busy_slots);
}
}
class GoogleCalendarSync {
private $client;
private $service;
public function __construct() {
$this->client = new Google_Client();
$this->client->setAuthConfig(get_stylesheet_directory() . '/config/google-calendar.json');
$this->client->addScope(Google_Service_Calendar::CALENDAR);
$this->service = new Google_Service_Calendar($this->client);
}
public function create_event($appointment_data) {
$event = new Google_Service_Calendar_Event(array(
'summary' => '咨询预约 - ' . $appointment_data['client_name'],
'description' => $appointment_data['notes'],
'start' => array(
'dateTime' => $appointment_data['start_datetime'],
'timeZone' => 'Asia/Shanghai',
),
'end' => array(
'dateTime' => $appointment_data['end_datetime'],
'timeZone' => 'Asia/Shanghai',
),
'attendees' => array(
array('email' => $appointment_data['client_email']),
array('email' => get_option('admin_email'))
),
'reminders' => array(
'useDefault' => false,
'overrides' => array(
array('method' => 'email', 'minutes' => 24 * 60),
array('method' => 'popup', 'minutes' => 30),
),
),
));
$calendarId = 'primary';
$event = $this->service->events->insert($calendarId, $event);
return $event->getId();
}
}
class WechatMiniProgram {
private $app_id;
private $app_secret;
public function __construct() {
$this->app_id = get_option('wechat_app_id');
$this->app_secret = get_option('wechat_app_secret');
}
// 获取微信用户openid
public function get_user_openid($code) {
$url = "https://api.weixin.qq.com/sns/jscode2session";
$params = array(
'appid' => $this->app_id,
'secret' => $this->app_secret,
'js_code' => $code,
'grant_type' => 'authorization_code'
);
$response = wp_remote_get($url . '?' . http_build_query($params));
if (is_wp_error($response)) {
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
return isset($body['openid']) ? $body['openid'] : false;
}
// 发送预约提醒模板消息
public function send_appointment_reminder($openid, $appointment_data) {
$access_token = $this->get_access_token();
$url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={$access_token}";
$data = array(
'touser' => $openid,
'template_id' => get_option('wechat_reminder_template_id'),
'page' => 'pages/appointment/detail?id=' . $appointment_data['id'],
'data' => array(
'thing1' => array('value' => $appointment_data['service_name']),
'time2' => array('value' => $appointment_data['appointment_time']),
'thing3' => array('value' => '请准时到达')
)
);
$response = wp_remote_post($url, array(
'headers' => array('Content-Type' => 'application/json'),
'body' => json_encode($data),
'timeout' => 10
));
return !is_wp_error($response);
}
}
class WechatMiniProgram {
private $app_id;
private $app_secret;
public function __construct() {
$this->app_id = get_option('wechat_app_id');
$this->app_secret = get_option('wechat_app_secret');
}
// 获取微信用户openid
public function get_user_openid($code) {
$url = "https://api.weixin.qq.com/sns/jscode2session";
$params = array(
'appid' => $this->app_id,
'secret' => $this->app_secret,
'js_code' => $code,
'grant_type' => 'authorization_code'
);
$response = wp_remote_get($url . '?' . http_build_query($params));
if (is_wp_error($response)) {
return false;
}
$body = json_decode(wp_remote_retrieve_body($response), true);
return isset($body['openid']) ? $body['openid'] : false;
}
// 发送预约提醒模板消息
public function send_appointment_reminder($openid, $appointment_data) {
$access_token = $this->get_access_token();
$url = "https://api.weixin.qq.com/cgi-bin/message/subscribe/send?access_token={$access_token}";
$data = array(
'touser' => $openid,
'template_id' => get_option('wechat_reminder_template_id'),
'page' => 'pages/appointment/detail?id=' . $appointment_data['id'],
'data' => array(
'thing1' => array('value' => $appointment_data['service_name']),
'time2' => array('value' => $appointment_data['appointment_time']),
'thing3' => array('value' => '请准时到达')
)
);
$response = wp_remote_post($url, array(
'headers' => array('Content-Type' => 'application/json'),
'body' => json_encode($data),
'timeout' => 10
));
return !is_wp_error($response);
}
}
class AppointmentBackup {
// 创建预约数据备份
public function create_backup() {
$backup_data = array(
'timestamp' => current_time('timestamp'),
'appointments' => $this->export_appointments(),
'services' => $this->export_services(),
'settings' => $this->export_settings()
);
class AppointmentBackup {
// 创建预约数据备份
public function create_backup() {
$backup_data = array(
'timestamp' => current_time('timestamp'),
'appointments' => $this->export_appointments(),
'services' => $this->export_services(),
'settings' => $this->export_settings()
);


