「資料庫不是拿來養蚊子的!」資深工程師教你用 50 行代碼自動偵測並喚醒 90 天未消費的『沈睡客群』

2026/01/14 | CRM 應用, WC 開發, WP 開發技巧, 數位行銷策略

告別資料庫蚊子館:50 行代碼自動催生現金流

您的資料庫裡是否躺著數萬名「沈睡客群」?資深工程師 Eric 揭露,手動篩選名單進行再行銷的效率極差。本文教您利用 WordPress 原生 Cron Job 與一段精簡 PHP 代碼,全自動偵測超過 90 天未消費的用戶,並即時發送專屬優惠券。告別浪費時間的重複性勞動,現在就實施這套自動化喚醒引擎,將那 95% 的沉寂會員,轉變為持續且穩定的營收來源!

需要專業協助?

聯絡浪花專案團隊 →

「資料庫不是拿來養蚊子的!」資深工程師教你用 50 行代碼自動偵測並喚醒 90 天未消費的『沈睡客群』

嗨,我是 Eric,浪花科技的資深工程師。今天不談什麼高大上的 AI 預測模型,我們來聊聊一個更實際、甚至有點「銅臭味」的話題:把躺在資料庫裡的死魚,變成活跳跳的現金。

我看過太多客戶的 WooCommerce 後台,會員數動輒好幾萬,看起來很風光,但仔細一撈 SQL,真正活躍的可能連 5% 都不到。剩下的 95% 呢?就像你家過年買的糖果盒一樣,擺在角落長灰塵。這些「沈睡客戶」其實比新客戶更有價值——他們認識你、買過你的東西,只是忘了你的存在。

行銷部門通常會說:「那我們每個月手動發電子報!」別鬧了,手動篩選名單、匯出 Excel、匯入 MailChimp,這流程跑兩次就沒人想做了。身為工程師,我們的職責就是「消滅重複性勞動」

今天這篇文章,我要教你如何用 WordPress 原生的 Cron Job 和一段精簡的 PHP 代碼,自動偵測「超過 90 天未消費」的用戶,並自動生成一張專屬優惠券寄給他。全自動、零手動。

為什麼是 90 天?技術與人性的黃金交叉點

在寫 Code 之前,先講邏輯。為什麼設定 90 天?

  • 記憶曲線:三個月是大部分消費者對品牌記憶模糊的臨界點。
  • 資料庫效能:如果每天都掃描「昨天未消費」的用戶,你的伺服器 CPU 會哭出來。90 天是一個既能篩出足夠樣本,又不至於讓 Query 爆炸的時間區間。
  • 避免騷擾:太頻繁的「想念你」郵件,只會讓人覺得你是恐怖情人。

技術難點:如何在 WordPress 中高效撈取「最後消費日」?

這是一個坑。WooCommerce 預設的 wp_users 表裡面,並沒有直接欄位紀錄「最後一次下單時間」。

如果你每次 Cron Job 執行時,都要用 wc_get_orders() 去遍歷所有訂單來反查用戶,這就是標準的「N+1 查詢災難」。當你的訂單數破萬時,網站會直接卡死。

Step 1:埋點——在用戶 Meta 紀錄最後消費時間

最優雅的做法,是在用戶下單完成時,順手更新一個 _last_purchase_dateusermeta 表。這樣我們之後撈資料時,只需要簡單的 Meta Query,速度快上 100 倍。

把這段加到你的 functions.php 或自製外掛中:


// 當訂單狀態變更為「完成」或「處理中」時,更新用戶的最後購買時間
add_action('woocommerce_order_status_completed', 'roamer_update_last_purchase_date', 10, 1);
add_action('woocommerce_order_status_processing', 'roamer_update_last_purchase_date', 10, 1);

function roamer_update_last_purchase_date($order_id) {
    $order = wc_get_order($order_id);
    $user_id = $order->get_user_id();

    if ($user_id) {
        // 儲存 Unix Timestamp,方便後續比對
        update_user_meta($user_id, '_roamer_last_buy', current_time('timestamp'));
        // 順便清除「已喚醒」標記(如果有的話),因為他買了!
        delete_user_meta($user_id, '_roamer_dormant_alert_sent');
    }
}

Step 2:打造自動喚醒引擎 (Cron Job)

有了數據,接下來就是自動化引擎。我們需要註冊一個 WordPress 排程,每天跑一次,找出符合以下條件的人:

  1. _roamer_last_buy 小於 90 天前。
  2. _roamer_dormant_alert_sent 不存在(避免重複發送)。

核心程式碼邏輯


// 1. 註冊排程時間間隔(這裡示範每天一次,測試時可改為每小時)
add_filter('cron_schedules', function ($schedules) {
    $schedules['daily_check'] = array(
        'interval' => 86400,
        'display'  => __('Every Day')
    );
    return $schedules;
});

// 2. 註冊 Action Hook
add_action('roamer_daily_dormant_check', 'roamer_process_dormant_users');

// 3. 確保排程已啟用(通常放在外掛啟用時,這裡簡化處理)
if (!wp_next_scheduled('roamer_daily_dormant_check')) {
    wp_schedule_event(time(), 'daily_check', 'roamer_daily_dormant_check');
}

// 4. 主邏輯:撈人、發信
function roamer_process_dormant_users() {
    // 設定 90 天前的時間戳
    $cutoff_date = strtotime('-90 days');

    // 查詢條件
    $args = array(
        'role'    => 'customer',
        'number'  => 50, // 為了效能,每次只處理 50 人
        'meta_query' => array(
            'relation' => 'AND',
            array(
                'key'     => '_roamer_last_buy',
                'value'   => $cutoff_date,
                'compare' => '<', // 早於 90 天前
                'type'    => 'NUMERIC'
            ),
            array(
                'key'     => '_roamer_dormant_alert_sent',
                'compare' => 'NOT EXISTS' // 沒發過通知的
            )
        )
    );

    $user_query = new WP_User_Query($args);
    $users = $user_query->get_results();

    if (!empty($users)) {
        foreach ($users as $user) {
            // 產生專屬優惠碼 (例如:COMEBACK-123)
            $coupon_code = roamer_create_dynamic_coupon($user->ID);

            // 發送郵件
            $subject = '好久不見!這是您的專屬回歸禮物';
            $message = "嗨 {$user->display_name},

我們發現您有一陣子沒來逛逛了。為了表示想念,我們為您準備了 9 折優惠券:{$coupon_code}

期待您的光臨!"; $headers = array('Content-Type: text/html; charset=UTF-8'); wp_mail($user->user_email, $subject, $message, $headers); // 標記已發送,避免明天重複發 update_user_meta($user->ID, '_roamer_dormant_alert_sent', current_time('timestamp')); } } }

Step 3:動態生成專屬優惠券 (Bonus)

別再發那種全網通用的 WELCOME2025 了,那種代碼很容易流出被濫用。我們要生成的是「限制該用戶Email使用」且「限時」的專屬優惠券。


function roamer_create_dynamic_coupon($user_id) {
    $coupon_code = 'COMEBACK-' . $user_id . '-' . wp_rand(100, 999);
    
    $coupon = new WC_Coupon();
    $coupon->set_code($coupon_code);
    $coupon->set_discount_type('percent'); // 百分比折扣
    $coupon->set_amount(10); // 10% OFF
    $coupon->set_individual_use(true); // 單獨使用
    $coupon->set_usage_limit(1); // 只能用一次
    $coupon->set_email_restrictions(array(get_userdata($user_id)->user_email)); // 限制該用戶 Email
    $coupon->set_date_expires(strtotime('+7 days')); // 7天後過期,製造急迫感
    $coupon->save();

    return $coupon_code;
}

工程師的碎碎念:關於效能與優化

這套系統上線後,我有幾個建議要給你,這也是很多新手開發者容易忽略的坑:

1. 批次處理 (Batch Processing)

如果你有 10 萬個沈睡會員,千萬不要嘗試一次全部發完。我在代碼中設定了 'number' => 50,這意味著每次 Cron 執行只處理 50 人。如果你的量很大,可以將 Cron 頻率調高(例如每小時一次),這樣既能消化名單,又不會讓伺服器因為 PHP Timeout 而掛掉。

2. 郵件發送服務 (SMTP)

千萬不要依賴 WordPress 預設的 wp_mail() 直接用主機發信。當你短時間發送大量郵件時,主機 IP 很容易被 Gmail 或 Outlook 判定為垃圾郵件。請務必搭配 SendGridMailgunAmazon SES 等專業的 Transactional Email 服務,並設定好 SMTP 外掛。

3. 資料庫索引 (Indexing)

wp_usermeta 表變得非常巨大時,Meta Query 的效能會下降。雖然這個功能是在背景執行,不會直接影響前台使用者體驗,但如果可以,定期清理孤兒 Meta Data (Orphan Meta) 是保持網站健康的好習慣。

總結

透過這段不到 100 行的代碼,你建立了一個 24 小時不休息的業務員。它會自動觀察誰快要忘記你們品牌,然後在他遺忘的前一刻,遞上一張有誠意的優惠券。這比投 Facebook 廣告找新客便宜太多了。

技術是為了服務商業目標而存在的。別讓你的資料庫只是一堆冷冰冰的 0 與 1,讓它們動起來,為你創造價值。

常見問題 (FAQ)

Q1: 如果用戶沒登入就購買,這樣偵測得到嗎?

這套邏輯是基於 user_id 的。如果是「訪客結帳 (Guest Checkout)」,WooCommerce 預設不會建立使用者帳號,因此無法記錄到 User Meta。建議開啟 WooCommerce 的「結帳時自動建立帳戶」功能,才能最大化數據價值。

Q2: 這會不會跟我的電子報系統 (如 MailChimp) 衝突?

不會衝突,但要小心「訊息轟炸」。建議在你的 CRM 策略中區隔開來。這個功能屬於「系統級通知」,針對性極強。如果 MailChimp 也有類似的 Automation,請擇一使用,避免客戶同一天收到兩封喚醒信。

Q3: 代碼放哪裡?一定要寫成外掛嗎?

對於初學者,放在子主題 (Child Theme) 的 functions.php 是最快的方法。但為了管理方便,我強烈建議將這些功能封裝成一個獨立的「網站專屬功能外掛 (Site Specific Plugin)」,這樣就算你換了佈景主題,功能依然存在。

延伸閱讀

如果你覺得要在 PHP 裡面寫這些邏輯太頭痛,或者你的資料量已經大到需要企業級的 CRM 架構規劃,歡迎隨時找我們聊聊。我們擅長處理這種「稍微有點複雜」的技術難題。

不想手動撈名單?需要客製化您的 WooCommerce 自動化行銷引擎?

立即聯繫浪花科技,啟動您的數據變現計畫

 
立即諮詢,索取免費1年網站保固