WordPress文创产品柔性限时预售插件开发实战教程
一、项目概述与需求分析
在文创产品销售领域,柔性预售模式越来越受欢迎。这种模式允许商家根据预售情况调整生产数量,降低库存风险。本教程将带领大家开发一个完整的WordPress文创产品柔性限时预售插件。
核心功能需求:
- 为产品添加预售状态和预售时间设置
- 显示预售倒计时和预售进度
- 支持预售数量限制和柔性调整
- 预售结束后自动转为正常销售或下架
- 后台管理界面和销售数据统计
二、插件基础结构搭建
首先创建插件的基本目录结构和主文件:
<?php
/**
* Plugin Name: 文创产品柔性限时预售插件
* Plugin URI: https://yourwebsite.com/
* Description: 为WordPress文创产品添加柔性限时预售功能
* Version: 1.0.0
* Author: 你的名字
* License: GPL v2 or later
* Text Domain: flexible-presale
*/
// 防止直接访问
if (!defined('ABSPATH')) {
exit;
}
// 定义插件常量
define('FLEXIBLE_PRESALE_VERSION', '1.0.0');
define('FLEXIBLE_PRESALE_PATH', plugin_dir_path(__FILE__));
define('FLEXIBLE_PRESALE_URL', plugin_dir_url(__FILE__));
// 初始化插件
class Flexible_Presale_Plugin {
private static $instance = null;
public static function get_instance() {
if (null === self::$instance) {
self::$instance = new self();
}
return self::$instance;
}
private function __construct() {
$this->init_hooks();
}
private function init_hooks() {
// 激活/停用钩子
register_activation_hook(__FILE__, array($this, 'activate'));
register_deactivation_hook(__FILE__, array($this, 'deactivate'));
// 初始化
add_action('plugins_loaded', array($this, 'init'));
}
public function activate() {
// 创建必要的数据库表
$this->create_tables();
// 设置默认选项
update_option('flexible_presale_version', FLEXIBLE_PRESALE_VERSION);
}
public function deactivate() {
// 清理临时数据
wp_clear_scheduled_hook('flexible_presale_daily_check');
}
public function init() {
// 加载文本域
load_plugin_textdomain('flexible-presale', false, dirname(plugin_basename(__FILE__)) . '/languages');
// 包含必要文件
$this->includes();
// 初始化组件
$this->init_components();
}
private function includes() {
// 包含后台管理类
require_once FLEXIBLE_PRESALE_PATH . 'includes/class-admin.php';
// 包含产品管理类
require_once FLEXIBLE_PRESALE_PATH . 'includes/class-product.php';
// 包含前端显示类
require_once FLEXIBLE_PRESALE_PATH . 'includes/class-frontend.php';
// 包含AJAX处理类
require_once FLEXIBLE_PRESALE_PATH . 'includes/class-ajax.php';
}
private function init_components() {
// 初始化各个组件
Flexible_Presale_Admin::init();
Flexible_Presale_Product::init();
Flexible_Presale_Frontend::init();
Flexible_Presale_Ajax::init();
}
private function create_tables() {
global $wpdb;
$charset_collate = $wpdb->get_charset_collate();
$table_name = $wpdb->prefix . 'flexible_presale_data';
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
product_id bigint(20) NOT NULL,
presale_start datetime NOT NULL,
presale_end datetime NOT NULL,
target_quantity int(11) NOT NULL DEFAULT 0,
current_quantity int(11) NOT NULL DEFAULT 0,
is_flexible tinyint(1) NOT NULL DEFAULT 1,
status varchar(20) NOT NULL DEFAULT 'active',
created_at datetime DEFAULT CURRENT_TIMESTAMP,
updated_at datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY product_id (product_id),
KEY presale_end (presale_end)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
}
// 启动插件
Flexible_Presale_Plugin::get_instance();
?>
三、产品预售数据管理模块
接下来创建产品管理类,用于处理产品的预售设置:
<?php
/**
* 产品预售管理类
*/
class Flexible_Presale_Product {
public static function init() {
// 添加产品预售设置字段
add_action('woocommerce_product_options_general_product_data', array(__CLASS__, 'add_presale_fields'));
// 保存产品预售设置
add_action('woocommerce_process_product_meta', array(__CLASS__, 'save_presale_fields'));
// 添加预售状态到产品列表
add_filter('manage_product_posts_columns', array(__CLASS__, 'add_presale_column'));
add_action('manage_product_posts_custom_column', array(__CLASS__, 'show_presale_column'), 10, 2);
}
/**
* 在产品编辑页面添加预售设置字段
*/
public static function add_presale_fields() {
global $post;
echo '<div class="options_group presale-options">';
// 预售开关
woocommerce_wp_checkbox(array(
'id' => '_is_presale',
'label' => __('启用预售', 'flexible-presale'),
'description' => __('启用此产品的预售功能', 'flexible-presale')
));
// 预售开始时间
woocommerce_wp_text_input(array(
'id' => '_presale_start_date',
'label' => __('预售开始时间', 'flexible-presale'),
'type' => 'datetime-local',
'desc_tip' => true,
'description' => __('预售开始的具体日期和时间', 'flexible-presale')
));
// 预售结束时间
woocommerce_wp_text_input(array(
'id' => '_presale_end_date',
'label' => __('预售结束时间', 'flexible-presale'),
'type' => 'datetime-local',
'desc_tip' => true,
'description' => __('预售结束的具体日期和时间', 'flexible-presale')
));
// 目标预售数量
woocommerce_wp_text_input(array(
'id' => '_presale_target_qty',
'label' => __('目标预售数量', 'flexible-presale'),
'type' => 'number',
'custom_attributes' => array(
'min' => '0',
'step' => '1'
),
'desc_tip' => true,
'description' => __('期望达到的预售数量', 'flexible-presale')
));
// 是否启用柔性调整
woocommerce_wp_checkbox(array(
'id' => '_is_flexible_presale',
'label' => __('柔性预售', 'flexible-presale'),
'description' => __('根据预售情况自动调整生产数量', 'flexible-presale')
));
// 最低生产数量
woocommerce_wp_text_input(array(
'id' => '_min_production_qty',
'label' => __('最低生产数量', 'flexible-presale'),
'type' => 'number',
'custom_attributes' => array(
'min' => '0',
'step' => '1'
),
'desc_tip' => true,
'description' => __('无论预售情况如何都会生产的最低数量', 'flexible-presale')
));
echo '</div>';
// 添加JavaScript处理日期时间选择器
self::add_admin_scripts();
}
/**
* 保存产品预售设置
*/
public static function save_presale_fields($post_id) {
// 验证nonce
if (!isset($_POST['woocommerce_meta_nonce']) ||
!wp_verify_nonce($_POST['woocommerce_meta_nonce'], 'woocommerce_save_data')) {
return;
}
// 保存预售设置
$is_presale = isset($_POST['_is_presale']) ? 'yes' : 'no';
update_post_meta($post_id, '_is_presale', $is_presale);
if ($is_presale === 'yes') {
// 保存预售开始时间
if (!empty($_POST['_presale_start_date'])) {
$start_date = sanitize_text_field($_POST['_presale_start_date']);
update_post_meta($post_id, '_presale_start_date', $start_date);
}
// 保存预售结束时间
if (!empty($_POST['_presale_end_date'])) {
$end_date = sanitize_text_field($_POST['_presale_end_date']);
update_post_meta($post_id, '_presale_end_date', $end_date);
}
// 保存目标数量
if (!empty($_POST['_presale_target_qty'])) {
$target_qty = intval($_POST['_presale_target_qty']);
update_post_meta($post_id, '_presale_target_qty', $target_qty);
}
// 保存柔性预售设置
$is_flexible = isset($_POST['_is_flexible_presale']) ? 'yes' : 'no';
update_post_meta($post_id, '_is_flexible_presale', $is_flexible);
// 保存最低生产数量
if (!empty($_POST['_min_production_qty'])) {
$min_qty = intval($_POST['_min_production_qty']);
update_post_meta($post_id, '_min_production_qty', $min_qty);
}
// 更新预售数据到自定义表
self::update_presale_data($post_id);
}
}
/**
* 更新预售数据到数据库
*/
private static function update_presale_data($product_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
$presale_data = array(
'product_id' => $product_id,
'presale_start' => get_post_meta($product_id, '_presale_start_date', true),
'presale_end' => get_post_meta($product_id, '_presale_end_date', true),
'target_quantity' => get_post_meta($product_id, '_presale_target_qty', true),
'is_flexible' => get_post_meta($product_id, '_is_flexible_presale', true) === 'yes' ? 1 : 0,
'status' => 'active'
);
// 检查是否已存在记录
$existing = $wpdb->get_var($wpdb->prepare(
"SELECT id FROM $table_name WHERE product_id = %d",
$product_id
));
if ($existing) {
$wpdb->update($table_name, $presale_data, array('product_id' => $product_id));
} else {
$wpdb->insert($table_name, $presale_data);
}
}
/**
* 在产品列表添加预售状态列
*/
public static function add_presale_column($columns) {
$columns['presale_status'] = __('预售状态', 'flexible-presale');
return $columns;
}
/**
* 显示预售状态列内容
*/
public static function show_presale_column($column, $post_id) {
if ($column === 'presale_status') {
$is_presale = get_post_meta($post_id, '_is_presale', true);
if ($is_presale === 'yes') {
$end_date = get_post_meta($post_id, '_presale_end_date', true);
$now = current_time('mysql');
if ($end_date > $now) {
echo '<span class="presale-active">' . __('预售中', 'flexible-presale') . '</span>';
} else {
echo '<span class="presale-ended">' . __('预售结束', 'flexible-presale') . '</span>';
}
} else {
echo '<span class="presale-inactive">' . __('未预售', 'flexible-presale') . '</span>';
}
}
}
/**
* 添加管理后台脚本
*/
private static function add_admin_scripts() {
?>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 动态显示/隐藏预售字段
$('#_is_presale').change(function() {
if ($(this).is(':checked')) {
$('.presale-options').show();
} else {
$('.presale-options').hide();
}
}).trigger('change');
// 设置日期时间选择器的最小值
var now = new Date();
var today = now.toISOString().slice(0, 16);
$('#_presale_start_date').attr('min', today);
$('#_presale_end_date').attr('min', today);
});
</script>
<?php
}
}
?>
四、前端预售展示与倒计时功能
创建前端显示类,处理产品页面的预售信息展示:
<?php
/**
* 前端预售展示类
*/
class Flexible_Presale_Frontend {
public static function init() {
// 在产品页面显示预售信息
add_action('woocommerce_before_add_to_cart_form', array(__CLASS__, 'show_presale_info'), 10);
// 在商品列表显示预售标签
add_action('woocommerce_before_shop_loop_item_title', array(__CLASS__, 'add_presale_badge'), 5);
// 修改预售产品的价格显示
add_filter('woocommerce_get_price_html', array(__CLASS__, 'modify_price_display'), 10, 2);
// 添加预售倒计时脚本
add_action('wp_enqueue_scripts', array(__CLASS__, 'enqueue_frontend_scripts'));
}
/**
* 在产品页面显示预售信息
*/
public static function show_presale_info() {
global $product;
if (!self::is_presale_product($product->get_id())) {
return;
}
$product_id = $product->get_id();
$presale_data = self::get_presale_data($product_id);
if (!$presale_data) {
return;
}
?>
<div class="flexible-presale-info">
<div class="presale-header">
<h3><?php _e('🎉 限时预售中', 'flexible-presale'); ?></h3>
<p class="presale-description">
<?php _e('参与预售,享受优先发货和特别优惠!', 'flexible-presale'); ?>
</p>
</div>
<div class="presale-countdown">
<h4><?php _e('预售结束倒计时:', 'flexible-presale'); ?></h4>
<div id="presale-countdown-<?php echo $product_id; ?>" class="countdown-timer"></div>
</div>
<div class="presale-progress">
<h4><?php _e('预售进度:', 'flexible-presale'); ?></h4>
<?php
$current_qty = $presale_data->current_quantity;
$target_qty = $presale_data->target_quantity;
$percentage = $target_qty > 0 ? min(100, ($current_qty / $target_qty) * 100) : 0;
?>
<div class="progress-bar">
<div class="progress-fill" style="width: <?php echo $percentage; ?>%"></div>
</div>
<p class="progress-text">
<?php printf(__('已预售 %d / 目标 %d (%.1f%%)', 'flexible-presale'),
$current_qty, $target_qty, $percentage); ?>
</p>
</div>
<div class="presale-notice">
<p>
<?php if ($presale_data->is_flexible) : ?>
<?php _e('✅ 此为柔性预售:最终生产数量将根据预售情况调整', 'flexible-presale'); ?>
<?php else : ?>
<?php _e('📦 此为定量预售:达到目标数量即开始生产', 'flexible-presale'); ?>
<?php endif; ?>
</p>
<p>
<?php _e('⏰ 预售结束后将根据订单安排生产,预计发货时间将另行通知', 'flexible-presale'); ?>
</p>
</div>
</div>
<script type="text/javascript">
jQuery(document).ready(function($) {
// 初始化倒计时
initializeCountdown(<?php echo $product_id; ?>, '<?php echo $presale_data->presale_end; ?>');
});
</script>
<?php
}
/**
* 在商品列表显示预售标签
*/
public static function add_presale_badge() {
global $product;
if (self::is_presale_product($product->get_id())) {
echo '<span class="presale-badge">' . __('预售', 'flexible-presale') . '</span>';
}
}
/**
* 修改预售产品的价格显示
*/
public static function modify_price_display($price, $product) {
)) {
$presale_data = self::get_presale_data($product->get_id());
if ($presale_data) {
$original_price = $product->get_regular_price();
$presale_price = $product->get_sale_price() ?: $original_price;
$price_html = '<div class="presale-price">';
$price_html .= '<span class="original-price">' . wc_price($original_price) . '</span>';
$price_html .= '<span class="presale-discount">' . wc_price($presale_price) . '</span>';
$price_html .= '<span class="presale-label">' . __('预售特价', 'flexible-presale') . '</span>';
$price_html .= '</div>';
return $price_html;
}
}
return $price;
}
/**
* 加载前端脚本和样式
*/
public static function enqueue_frontend_scripts() {
if (is_product() || is_shop() || is_product_category()) {
wp_enqueue_style(
'flexible-presale-frontend',
FLEXIBLE_PRESALE_URL . 'assets/css/frontend.css',
array(),
FLEXIBLE_PRESALE_VERSION
);
wp_enqueue_script(
'flexible-presale-countdown',
FLEXIBLE_PRESALE_URL . 'assets/js/countdown.js',
array('jquery'),
FLEXIBLE_PRESALE_VERSION,
true
);
wp_localize_script('flexible-presale-countdown', 'presale_vars', array(
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('flexible_presale_nonce')
));
}
}
/**
* 检查产品是否为预售产品
*/
private static function is_presale_product($product_id) {
$is_presale = get_post_meta($product_id, '_is_presale', true);
$end_date = get_post_meta($product_id, '_presale_end_date', true);
$now = current_time('mysql');
return ($is_presale === 'yes' && $end_date > $now);
}
/**
* 获取预售数据
*/
private static function get_presale_data($product_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
return $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE product_id = %d AND status = 'active'",
$product_id
));
}
}
?>
## 五、倒计时与预售进度JavaScript实现
创建倒计时JavaScript文件:
/**
- 预售倒计时功能
*/
(function($) {
'use strict';
// 倒计时计时器集合
var countdownTimers = {};
/**
* 初始化倒计时
*/
window.initializeCountdown = function(productId, endDateStr) {
var endDate = new Date(endDateStr);
var countdownElement = $('#presale-countdown-' + productId);
if (!countdownElement.length || isNaN(endDate.getTime())) {
return;
}
// 更新倒计时显示
function updateCountdown() {
var now = new Date();
var timeLeft = endDate - now;
if (timeLeft <= 0) {
clearInterval(countdownTimers[productId]);
countdownElement.html('<div class="countdown-ended">预售已结束</div>');
updatePresaleStatus(productId);
return;
}
var days = Math.floor(timeLeft / (1000 * 60 * 60 * 24));
var hours = Math.floor((timeLeft % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
var minutes = Math.floor((timeLeft % (1000 * 60 * 60)) / (1000 * 60));
var seconds = Math.floor((timeLeft % (1000 * 60)) / 1000);
var countdownHtml = '<div class="countdown-digits">';
countdownHtml += '<div class="countdown-unit"><span class="digit">' + days + '</span><span class="label">天</span></div>';
countdownHtml += '<div class="countdown-unit"><span class="digit">' + hours + '</span><span class="label">时</span></div>';
countdownHtml += '<div class="countdown-unit"><span class="digit">' + minutes + '</span><span class="label">分</span></div>';
countdownHtml += '<div class="countdown-unit"><span class="digit">' + seconds + '</span><span class="label">秒</span></div>';
countdownHtml += '</div>';
countdownElement.html(countdownHtml);
}
// 立即更新一次
updateCountdown();
// 每秒更新一次
countdownTimers[productId] = setInterval(updateCountdown, 1000);
};
/**
* 更新预售状态
*/
function updatePresaleStatus(productId) {
$.ajax({
url: presale_vars.ajax_url,
type: 'POST',
data: {
action: 'update_presale_status',
product_id: productId,
nonce: presale_vars.nonce
},
success: function(response) {
if (response.success) {
// 刷新页面或更新UI
location.reload();
}
}
});
}
/**
* 更新预售进度
*/
window.updatePresaleProgress = function(productId, quantity) {
$.ajax({
url: presale_vars.ajax_url,
type: 'POST',
data: {
action: 'update_presale_progress',
product_id: productId,
quantity: quantity,
nonce: presale_vars.nonce
},
success: function(response) {
if (response.success) {
// 更新进度条显示
var percentage = response.data.percentage;
$('.progress-fill').css('width', percentage + '%');
$('.progress-text').text('已预售 ' + response.data.current + ' / 目标 ' + response.data.target + ' (' + percentage.toFixed(1) + '%)');
}
}
});
};
/**
* 页面加载完成后初始化所有倒计时
*/
$(document).ready(function() {
$('[id^="presale-countdown-"]').each(function() {
var id = $(this).attr('id');
var productId = id.replace('presale-countdown-', '');
var endDate = $(this).data('end-date');
if (productId && endDate) {
initializeCountdown(productId, endDate);
}
});
// 监听加入购物车事件
$(document).on('added_to_cart', function(e, fragments, cart_hash, $button) {
var productId = $button.data('product_id');
var quantity = $button.data('quantity') || 1;
if (productId) {
updatePresaleProgress(productId, quantity);
}
});
});
})(jQuery);
## 六、AJAX处理与后台管理
创建AJAX处理类:
<?php
/**
- AJAX处理类
*/
class Flexible_Presale_Ajax {
public static function init() {
// 更新预售状态
add_action('wp_ajax_update_presale_status', array(__CLASS__, 'update_presale_status'));
add_action('wp_ajax_nopriv_update_presale_status', array(__CLASS__, 'update_presale_status'));
// 更新预售进度
add_action('wp_ajax_update_presale_progress', array(__CLASS__, 'update_presale_progress'));
add_action('wp_ajax_nopriv_update_presale_progress', array(__CLASS__, 'update_presale_progress'));
// 获取预售统计数据
add_action('wp_ajax_get_presale_stats', array(__CLASS__, 'get_presale_stats'));
// 每日检查预售状态
add_action('flexible_presale_daily_check', array(__CLASS__, 'daily_status_check'));
}
/**
* 更新预售状态
*/
public static function update_presale_status() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'flexible_presale_nonce')) {
wp_die('权限验证失败');
}
$product_id = intval($_POST['product_id']);
if ($product_id) {
// 更新产品预售状态
update_post_meta($product_id, '_is_presale', 'no');
// 更新数据库状态
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
$wpdb->update(
$table_name,
array('status' => 'ended'),
array('product_id' => $product_id)
);
// 发送邮件通知
self::send_presale_end_notification($product_id);
wp_send_json_success(array(
'message' => '预售状态已更新'
));
}
wp_send_json_error(array(
'message' => '更新失败'
));
}
/**
* 更新预售进度
*/
public static function update_presale_progress() {
// 验证nonce
if (!wp_verify_nonce($_POST['nonce'], 'flexible_presale_nonce')) {
wp_die('权限验证失败');
}
$product_id = intval($_POST['product_id']);
$quantity = intval($_POST['quantity']);
if ($product_id && $quantity > 0) {
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
// 获取当前数据
$current_data = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE product_id = %d AND status = 'active'",
$product_id
));
if ($current_data) {
// 更新预售数量
$new_quantity = $current_data->current_quantity + $quantity;
$wpdb->update(
$table_name,
array('current_quantity' => $new_quantity),
array('id' => $current_data->id)
);
// 计算百分比
$percentage = $current_data->target_quantity > 0
? min(100, ($new_quantity / $current_data->target_quantity) * 100)
: 0;
// 检查是否达到目标
if ($new_quantity >= $current_data->target_quantity && $current_data->target_quantity > 0) {
self::handle_target_reached($product_id, $current_data);
}
wp_send_json_success(array(
'current' => $new_quantity,
'target' => $current_data->target_quantity,
'percentage' => $percentage
));
}
}
wp_send_json_error(array(
'message' => '更新失败'
));
}
/**
* 处理达到预售目标
*/
private static function handle_target_reached($product_id, $presale_data) {
// 发送达到目标通知
$admin_email = get_option('admin_email');
$subject = sprintf(__('产品 #%d 已达到预售目标', 'flexible-presale'), $product_id);
$message = sprintf(__(
"产品 #%d 已达到预售目标数量 %d。n" .
"当前预售数量:%dn" .
"预售结束时间:%sn" .
"请及时安排生产。",
'flexible-presale'
), $product_id, $presale_data->target_quantity,
$presale_data->current_quantity, $presale_data->presale_end);
wp_mail($admin_email, $subject, $message);
// 如果是柔性预售,计算建议生产数量
if ($presale_data->is_flexible) {
$min_qty = get_post_meta($product_id, '_min_production_qty', true) ?: 0;
$suggested_qty = max($min_qty, $presale_data->current_quantity);
// 保存建议生产数量
update_post_meta($product_id, '_suggested_production_qty', $suggested_qty);
// 发送生产建议
$subject = sprintf(__('产品 #%d 生产数量建议', 'flexible-presale'), $product_id);
$message = sprintf(__(
"根据柔性预售结果,建议生产数量:%dn" .
"最低生产数量:%dn" .
"实际预售数量:%d",
'flexible-presale'
), $suggested_qty, $min_qty, $presale_data->current_quantity);
wp_mail($admin_email, $subject, $message);
}
}
/**
* 发送预售结束通知
*/
private static function send_presale_end_notification($product_id) {
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
$presale_data = $wpdb->get_row($wpdb->prepare(
"SELECT * FROM $table_name WHERE product_id = %d",
$product_id
));
if ($presale_data) {
$admin_email = get_option('admin_email');
$product = wc_get_product($product_id);
$product_name = $product ? $product->get_name() : '产品 #' . $product_id;
$subject = sprintf(__('产品"%s"预售已结束', 'flexible-presale'), $product_name);
$message = sprintf(__(
"产品:%sn" .
"预售开始时间:%sn" .
"预售结束时间:%sn" .
"目标预售数量:%dn" .
"实际预售数量:%dn" .
"完成度:%.1f%%nn" .
"请登录后台查看详细数据并安排后续工作。",
'flexible-presale'
), $product_name, $presale_data->presale_start,
$presale_data->presale_end, $presale_data->target_quantity,
$presale_data->current_quantity,
($presale_data->target_quantity > 0 ?
($presale_data->current_quantity / $presale_data->target_quantity * 100) : 0));
wp_mail($admin_email, $subject, $message);
}
}
/**
* 每日检查预售状态
*/
public static function daily_status_check() {
global $wpdb;
$table_name = $wpdb->prefix . 'flexible_presale_data';
$now = current_time('mysql');
// 查找已结束但状态未更新的预售
$ended_presales = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM $table_name WHERE presale_end < %s AND status = 'active'",
$now
));
foreach ($ended_presales as $presale) {
// 更新状态
$wpdb->update(
$table_name,
array('status' => 'ended'),
array('id' => $presale->id)
);
// 更新产品元数据
update_post_meta($presale->product_id, '_is_presale', 'no');
// 发送通知
self::send_presale_end_notification($presale->product_id);
}
// 记录检查日志
error_log('柔性预售插件每日检查完成,处理了 ' . count($ended_presales) . ' 个已结束的预售');
}
}
?>
## 七、CSS样式设计与优化
创建前端样式文件:
/ 预售插件前端样式 /
.flexible-presale-info {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 10px;
padding: 25px;
margin-bottom: 30px;
color: white;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
.presale-header h3 {
margin: 0 0 10px 0;
font-size: 24px;
font-weight: bold;
}
.presale-description {
margin: 0 0 20px 0;
opacity: 0.9;
font-size: 14px;
}
/ 倒计时样式 /
.presale-countdown {
margin: 25px 0;
padding: 20px;
background: rgba(255, 255, 255, 0.1);
border-radius: 8px;
}
.presale-countdown h4 {
margin: 0 0 15px 0;
font-size: 16px;
}
.countdown-timer {
display: flex;
justify-content: center;
gap: 15px;
}
.countdown-unit {
text-align: center;
min-width: 70px;
}
.countdown-unit .digit {
display: block;
font-size: 32px;
font-weight: bold;
background: rgba(255, 255, 255, 0.2);
padding: 10px;
border-radius: 5px;
margin-bottom: 5px;
}
.countdown-unit .label {
font-size: 12px;
opacity: 0.8;
text-transform: uppercase;
}
.countdown-ended {
text-align: center;
font-size: 18px;
font-weight: bold;
padding: 15px;
background: rgba(255, 255, 255, 0.2);
border-radius: 5px;
}
/* 进度条样式
