You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于WooCommerce订单自定义时间元键发送邮件?排查代码错误

问题排查与代码修正

问题描述

我需要根据自定义的订单过期日期(meta_key: _order_expiration_date)触发邮件通知,目前只能按订单状态触发,核心需求是按日期触发。自己写的代码有问题,还发现wp_post表中订单的post_type变成了shop_order_placehold而非shop_order,以下是原代码:

function add_order_expiration_date_field($order) {
    $expiration_date = get_post_meta($order->get_id(), '_order_expiration_date', true);
    ?>
    <div class="">
        <h4><?php _e('Expiration date', 'woocommerce'); ?></h4>
        <p class="form-field form-field-wide">
            <label for="order_expiration_date"><?php _e('Expiration date', 'woocommerce'); ?></label>
            <input type="datetime-local" class="input-text" name="order_expiration_date" id="order_expiration_date" value="<?php echo esc_attr($expiration_date); ?>" />
        </p>
    </div>
    <?php
}
add_action('woocommerce_admin_order_data_after_order_details', 'add_order_expiration_date_field');


//Save due date when updating order
function save_order_expiration_date($order_id) {
    $expiration_date = isset($_POST['order_expiration_date']) ? sanitize_text_field($_POST['order_expiration_date']) : '';
    update_post_meta($order_id, '_order_expiration_date', $expiration_date);
}
add_action('woocommerce_process_shop_order_meta', 'save_order_expiration_date');

// Function to handle custom query parameter'_order_expiration_date'
function handle_custom_order_expiration_date_query( $query, $query_vars ) {
    if ( ! empty( $query_vars['_order_expiration_date'] ) ) {
        $query['meta_query'][] = array(
            'key' => '_order_expiration_date',
            'value' => esc_attr( $query_vars['_order_expiration_date']) ,
            'compare' => '=',
        );
    }
    return $query;
}
add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', 'handle_custom_order_expiration_date_query', 10, 2 );

// Test function to send an email
function test_cron_mail() {
   // Define the custom query parameter '_order_expiration_date'
    $current_date_time = current_time( 'mysql' );
    $args = array(
        'status' => 'wc-completed',
        'meta_query' => array(
            array(
                'key' => '_order_expiration_date',
                'value' => $current_date_time,
                'compare' => '<',
                'type' => 'DATETIME',
            ),  
        ),
        'posts_per_page' => -1,
    );

    // Apply filter to handle custom query parameter
    add_filter( 'woocommerce_order_data_store_cpt_get_orders_query', 'handle_custom_order_expiration_date_query', 10, 2 );

    // Perform order query
    $query = new WC_Order_Query( $args );
    echo 'args:  ';var_dump ($args) ;
    $orders = $query->get_orders(array();

    echo 'orders:  ';
    var_dump ($orders) ;

    // Delete the filter after performing the query
    remove_filter( 'woocommerce_order_data_store_cpt_get_orders_query', 'handle_custom_order_expiration_date_query', 10, 2 );

    // Scroll through expired orders and take necessary actions
    foreach ( $orders as $order ) {
        $order_id = $order->get_id();
        $billing_name = $order->get_billing_first_name();
        $billing_email = $order->get_billing_email();

        $subject = 'Hosting expired for ' . $billing_name;
        $message = 'Your hosting is expired. Please, contact us for further information.';

        // Make sure the email address isválida antes de enviar el correo
        if ( is_email( $billing_email ) ) {
            wp_mail( $billing_email, $subject, $message );
        }
    }
}

// Add cron task for test function
add_action( 'init', 'schedule_test_cron_job' );

function schedule_test_cron_job() {
    // Set a 5 minute delay for the test cron task to run
    $timestamp = time() + 1 * 60;

    // Schedule the test cron task
    wp_schedule_single_event($timestamp, 'test_cron_job');
}

add_action('test_cron_job', 'test_cron_mail');

代码错误分析与修正

1. 语法错误:get_orders()调用参数不完整

原代码中$orders = $query->get_orders(array();缺少闭合括号,且get_orders()无需传空数组,直接调用即可。

2. 日期格式不匹配导致查询失效

前端datetime-local保存的格式是YYYY-MM-DDTHH:MM,而current_time('mysql')返回YYYY-MM-DD HH:MM:SS,两者格式不一致,需要统一:

  • 保存时将datetime-local格式转换为MySQL DATETIME格式;
  • 或查询时将当前时间转换为datetime-local的格式进行比较。

3. 不必要的过滤器调用

handle_custom_order_expiration_date_query过滤器是用于处理自定义查询参数的,当前查询已经直接定义了meta_query,无需额外添加该过滤器,反而会干扰逻辑。

4. Cron任务重复调度问题

schedule_test_cron_job挂在init钩子上,每次页面加载都会创建重复任务,需要先检查任务是否已存在再调度。

5. 避免查询到临时占位订单

添加type => 'shop_order'参数,排除shop_order_placehold类型的临时订单。

6. 注释语法错误

原代码中西班牙语注释存在拼写错误,修正为规范中文注释。


修正后的完整代码

// 添加订单过期日期字段到后台订单详情页
function add_order_expiration_date_field($order) {
    $expiration_date = get_post_meta($order->get_id(), '_order_expiration_date', true);
    // 转换MySQL格式为datetime-local格式用于回显
    if (!empty($expiration_date)) {
        $expiration_date = date('Y-m-d\TH:i', strtotime($expiration_date));
    }
    ?>
    <div class="">
        <h4><?php _e('Expiration date', 'woocommerce'); ?></h4>
        <p class="form-field form-field-wide">
            <label for="order_expiration_date"><?php _e('Expiration date', 'woocommerce'); ?></label>
            <input type="datetime-local" class="input-text" name="order_expiration_date" id="order_expiration_date" value="<?php echo esc_attr($expiration_date); ?>" />
        </p>
    </div>
    <?php
}
add_action('woocommerce_admin_order_data_after_order_details', 'add_order_expiration_date_field');


// 保存订单过期日期,转换为MySQL DATETIME格式
function save_order_expiration_date($order_id) {
    if (isset($_POST['order_expiration_date'])) {
        // 将datetime-local格式转换为MySQL DATETIME格式
        $expiration_date = date('Y-m-d H:i:s', strtotime(sanitize_text_field($_POST['order_expiration_date'])));
        update_post_meta($order_id, '_order_expiration_date', $expiration_date);
    }
}
add_action('woocommerce_process_shop_order_meta', 'save_order_expiration_date');

// 发送过期提醒邮件的核心函数
function send_expired_order_emails() {
    $current_date_time = current_time('mysql');
    $args = array(
        'status'        => 'wc-completed',
        'type'          => 'shop_order', // 仅查询正式订单,排除占位符
        'meta_query'    => array(
            array(
                'key'       => '_order_expiration_date',
                'value'     => $current_date_time,
                'compare'   => '<',
                'type'      => 'DATETIME',
            ),  
        ),
        'posts_per_page'=> -1,
    );

    $query = new WC_Order_Query($args);
    $orders = $query->get_orders();

    // 遍历过期订单发送邮件
    foreach ($orders as $order) {
        $billing_name = $order->get_billing_first_name();
        $billing_email = $order->get_billing_email();

        if (!is_email($billing_email)) {
            continue;
        }

        $subject = 'Hosting expired for ' . $billing_name;
        $message = 'Your hosting is expired. Please contact us for further information.';
        wp_mail($billing_email, $subject, $message);
    }
}

// 调度Cron任务:仅在任务不存在时创建
function schedule_expiration_cron_job() {
    if (!wp_next_scheduled('send_expired_order_emails_hook')) {
        // 每分钟执行一次(测试用,正式环境可改为每天)
        wp_schedule_event(time(), 'minute', 'send_expired_order_emails_hook');
    }
}
add_action('init', 'schedule_expiration_cron_job');

// 绑定Cron钩子到发送邮件函数
add_action('send_expired_order_emails_hook', 'send_expired_order_emails');

额外说明

  • 如果不需要通过自定义查询参数_order_expiration_date查询订单,可以直接删除原代码中的handle_custom_order_expiration_date_query函数及其过滤器绑定;
  • 正式环境建议将Cron调度间隔改为daily(每天),避免频繁执行;
  • 可以添加日志记录功能,方便排查邮件发送情况。

内容的提问来源于stack exchange,提问作者fakasmile1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:02:03