有獎活動送出去的是成本,不是流量。點數可以兌換,名次可以領現金,抽獎券可以換實物。一旦多發、重發,或被少數人大量領走,出問題的是帳,不是畫面。磨人開發團隊在活動平台的建置與維運上,把每一筆獎勵當成帳來處理。
同一類活動的其餘做法,各有專文:
獎勵能換成成本,錯誤就是帳務問題
活動平台常見的損失有三種,都發生在帳上。第一種是同一筆獎勵發了兩次,多半來自網路重送或排程重跑。第二種是同一位會員在同一瞬間送出兩次請求,餘額被扣成負數,或同一張券被兌換兩次。第三種是少數人用大量帳號領走名次獎,真正的參與者什麼也拿不到。

這三種損失有共同點:測試時不會出現,上線後流量一大才出現,事後也很難說清楚是誰多拿了什麼。所以對策不在前端多加提示,而在後端:獎勵怎麼進出要有規則,每一筆都能對帳。以下做法來自開發團隊在有真實成本的活動平台上的實作。
三條帳務規則:唯一編號、先鎖會員、固定加鎖順序
有現金成本的獎勵,開發團隊定了三條規則,寫在共用的帳務程式裡。活動功能一律呼叫這段程式入帳,不能繞過去直接寫點數表。
每一筆都有唯一編號,重送不會重發
每一筆入帳都帶一個唯一編號。同一個編號第二次進來,帳務程式回覆已處理,不再發放。編號要由事件本身組成,例如哪個活動、哪一期、哪位會員,不能每次隨機產生。否則排程重跑時,同一筆獎勵會拿到新編號,被當成新的一筆。這個要求有學理依據:應用層要做到冪等,接收端必須記住已處理過的訊息,因為訊息多採至少一次送達,重送與重複處理是常見情況(Helland,2007)。該文作者註明這是個人立場的論述。
支付服務的做法相同。Stripe 的 API 文件說明,客戶端帶冪等鍵重試,不會因連線錯誤而重複建立物件或重複更新,伺服器也會比對參數,與第一次不一致時回報錯誤(Stripe 文件,Idempotent requests)。文件另註明 API v2 的重試行為不同,這裡只引用冪等鍵的基本概念。IETF HTTPAPI 工作小組另有一份 Idempotency-Key 標頭的草案,讓 POST 這類請求能安全重送(Jena 等,2026)。這份文件仍是工作中的草案,不是 RFC。草案本身並未以金流為例,用同一把鍵避免獎勵重複入帳,是本文的應用。
寫入前先鎖住會員,同時送出的請求排隊
同一位會員在同一瞬間送出兩次請求,是連點與多裝置的日常。如果兩個請求都先讀餘額、再各自扣款,兩次都會通過檢查,餘額就變負。做法是在同一個交易內、寫入帳務之前,先鎖住該會員的資料列。後到的請求排隊,等前一個完成後,才讀到最新的餘額。兌換類的功能都以這條路為標準寫法。
牽涉兩人的交易,固定照同一個順序加鎖
邀請、轉贈這類交易同時動到兩位會員。甲轉給乙、乙同時轉給甲,各自先鎖自己再等對方,兩邊就會互相等待而死鎖。規則是不論誰發起,一律依會員編號由小到大加鎖。對 InnoDB 死鎖的建議是同一個方向:涉及多組列時,每次以相同順序執行操作,讓交易有序排隊,並把這些操作寫在應用程式函式或預存程序中(MySQL 8.0 手冊,2026 年版次)。
鎖負責排隊,唯一鍵負責兜底。例如一筆「已讀」紀錄若用 INSERT … SELECT … NOT EXISTS 一次寫入,可能與另一條會寫同一列資料的流程互等間隙鎖而死鎖。較穩的寫法是逐筆 INSERT,撞到主鍵就略過,遇到死鎖就再試一次。唯一鍵在這裡是最後一道防線,不是第一道。

規則只寫在文件裡不夠。開發團隊用靜態掃描的檢查腳本,找出繞過共用帳務程式的寫法。對照現有程式的檢查,常見的缺口有三類:兌換流程沒有加鎖,同一位會員連點可能把餘額扣成負數;排行榜獎勵沒有防重複編號,排程同一天跑兩次可能重發;對帳補發沒有檢查是否已領過。券兌換可被重複兌換這類問題,修法是整段包進交易、鎖住會員列,並在帳務流水加上防重複編號。
獎勵服務
- 兌換與抽獎呼叫帳務程式,不碰點數表
- 名次結算與補發排程執行,每筆帶由事件組成的編號
共用帳務程式
- 唯一編號檢查重複的編號回覆已處理,不再發放
- 會員鎖與加鎖順序先鎖會員,兩人交易依編號由小到大
- 統一的調整函式所有入帳與扣款都經過這一處
資料庫
- 會員資料列在交易內被鎖住
- 帳務流水每筆流水帶防重複編號
不跨過邊界點數表的寫入權限只留在共用帳務程式內,獎勵服務不能直接寫入。檢查腳本會掃描繞過的寫法。
範圍:有現金成本的點數、券與名次獎
防刷不該叫新用戶等時間
一人開多個帳號,是有獎活動最老的問題。Douceur 針對沒有中央權威的分散式系統論證,單一實體可以偽造大量身分,進而控制系統的大部分(Douceur,2002)。活動平台有中央的帳號系統,情境不同,但問題的形狀一樣:帳號的數量不等於人的數量,獎勵若按帳號發,少數人就能領走大部分。
最容易想到的擋法是「帳號註冊滿幾天才算數」。這個門檻簡單,代價是擋到所有真正的新用戶,而且擋在新用戶最需要被留住的第一天。開發團隊不用時間門檻當主要手段,時間門檻最多只留在新用戶本來就碰不到的功能上,天數也不超過約三天。改用四個不傷新用戶的做法:
- 每一對好友的轉移設上限。成對帳號之間能互相轉移的獎勵有固定額度,用分身互送也送不出規模。
- 同一裝置、同一網路的帳號合併計算。登入後由前端產生匿名的裝置代碼,存在瀏覽器本機,不做指紋辨識,隨每個請求送出。後端記下每個帳號最近的網路位址與裝置代碼,保留約三個月,共用任一個的帳號併成一群。
- 高價獎勵延遲入帳。名次獎先記成待入帳,預設約一天後才正式發放,期間後台可以追回。
- 異常偵測加人工複核。同一群帳號只發名次最高的那一個,其餘轉為待審核,由營運人員判斷。
名次獎另有人數門檻:榜上人數低於門檻時,前幾名只發沒有成本的外觀獎勵,門檻預設為獎項數的三倍。人數太少的榜,很容易被少數帳號包下。所有閾值都是後台參數,包括轉移上限、延遲時間與人數門檻,各有上下限護欄,每次調整都留下稽核軌跡。誰設的門檻、什麼時候改、改成多少,都查得到。
| 做法 | 擋的是什麼 | 對新用戶的影響 |
|---|---|---|
| 註冊滿一定天數才算數 | 剛註冊的分身 | 新用戶在第一天什麼都領不到 |
| 每一對好友的轉移上限 | 成對帳號互相轉移獎勵 | 影響很小,只限制成對的大量轉移 |
| 同裝置、同網路合併計算 | 一人多帳號 | 新用戶照常使用,只在發獎時併成一群計算 |
| 高價獎勵延遲入帳 | 領走後才被發現的異常 | 新用戶照常使用,高價獎勵晚約一天到帳 |
| 異常帳號人工複核 | 漏網的成群帳號 | 正常帳號不受影響,被標記的帳號晚一步領獎 |
有條件的獎勵,條件要讓參與者看得到。待入帳、人工複核與轉移上限,都應寫進活動辦法。公平交易委員會的網路廣告處理原則要求,促銷或抽獎類的網路廣告須正確描述活動規則,並充分揭示附帶的條件、義務或限制,不得以版面編排讓消費者不易察覺(公平交易委員會,2023)。這是行政機關的處理原則,不是法律條文。活動辦法的寫法應由主辦方的法務確認。
- 檢查唯一編號編號已處理過,回覆重複,不再發放
- 鎖住會員同一位會員的請求排隊
- 套用規則轉移上限、同裝置合併、預算與餘額
- 規則通過,且獎勵不屬高價直接入帳寫入帳務流水
- 獎勵屬高價,或帳號落在異常群組先記為待確認延遲後入帳,或交營運人員複核;期間後台可追回
- 超過上限、編號重複,或餘額不足拒絕回覆原因,不寫入帳
預算寫成驗證器,超過就存不進去
有現金成本的獎項,營運人員會在後台調整數值。如果預算只靠營運人員記得,填錯一個數字,超支要到結算才被發現。開發團隊把預算寫成程式裡的檢查:獎項單價乘上預估人數,超過預算,後台就不讓存。結算前再驗一次。
獎項表另有兩條驗證。名次區間必須連續,不能漏掉任何名次。不同獎項要折成同一個計價單位,才能比較與加總;現金、點數與實物沒有共同單位,總成本就沒有辦法檢查。
價值錨點也要寫成規則。同一張抽獎券,以兌換價換算與以大獎預算換算,價值可能差到數倍。開發團隊的規定是:門檻一律用大獎預算除以預計投入的張數重算,不沿用舊值。取捨是預算由輸入端擋住,超出預算的設定一律存不進去,要加獎項就得先調預算。這個摩擦是刻意保留的。
活動的狀態是產品的一部分,每個狀態都要有畫面與開關
活動型平台的狀態分為預熱、開放、結束、封存。遊戲化活動有明確的起訖,結束後還要保留成果頁,開發團隊的遊戲化活動實作案例就屬於這一類,該活動從規劃、設計到開發完成不到 2 個月。只設計開放狀態,等於把結束後的畫面留給意外。
開發團隊從第一天就把每個狀態做成畫面與開關。預熱期間整站是倒數頁:首頁回應正常內容,分享預覽卡與搜尋引擎拿到的是完整頁面,其餘頁面回 503。倒數頁不載入 3D 的程式碼,3D 的效能做法見網頁 3D 效能工程。結束後,排行榜與成果仍可查看,抽獎等功能自動關閉。封存則是關閉活動與排行榜計算的開關。前端建置時會呼叫後端的設定介面,所以上線順序固定為後端先、前端後。
狀態:預熱、開放、結束、封存
數字要可信:瀏覽量靠行為,同時在線靠心跳
瀏覽量只在真的換頁時才算
活動期間,主辦方看的是人潮與成效,數字怎麼算比數字本身重要。常見的瀏覽量去重,是同一人 2 秒內只算一次,這是用時間在猜。開發團隊的做法是讓前端記住目前頁面的身分,只在身分改變時送出一筆。換到另一個頁面算一次。同一頁換分頁、關掉彈窗回到原頁,都不送。直接用網址進站,進站那一筆就是那一頁,不重送。沒有自己網址的彈窗送另一種事件,算畫面打開,不算瀏覽量。瀏覽量的條件只寫一份,所有查詢共用,所以各頁加總剛好等於總瀏覽量。
追蹤上線前的時段,用各頁的打開事件估計,以第一筆精確事件為分界。圖上估計的格子畫斜線,說明欄寫明分界時間,不與精確資料混為一談。
同時在線用心跳計算
瀏覽器在前景時約每 30 秒回報一次,初始化與切回前景那一刻各補一次,背景不送。所以切到背景、鎖螢幕、關分頁,短時間內就不再計入。登入的會員按人算,同一人兩台裝置算一位;沒登入的訪客按裝置算。同時在線最高的那一格,會標出是哪一分鐘。心跳上線前的時段,用一段時間內有操作的人數估計,結果偏低,同樣畫斜線。
和 GA4 差在哪要寫清楚
兩套數字對不齊是常態,要先分清口徑。在這類專案的設定裡,GA4 只計入同意統計的訪客,數字偏低,也接不到會員帳號;自建追蹤涵蓋全部訪客,登入後可以歸戶到會員。GA4 的工作階段數本身也是估計值:Google 的說明頁註明,報表的工作階段數是依不重複工作階段 ID 估計的近似值,與 BigQuery 精確計算會有小幅差異(Google Analytics 說明,About Analytics sessions)。所以每個指標旁都附完整算法,並寫明和 GA4 差在哪。
前後端各有一份的名單,用腳本守住
追蹤事件的白名單,前端和後端各有一份,靠記得同步一定會漏。開發團隊寫一支腳本逐字比對,兩邊不一致、或找不到對方時直接失敗,不會悄悄略過。檢查本身也會過期:事件欄位增加之後,沒有跟著改的斷言會讓檢查一直失敗,所以腳本要定期對照改動檢查。
不適合這樣做的情況
- 只辦一次性的小型抽獎。現成的抽獎工具就夠,不需要自建帳務。
- 獎項沒有真實成本,例如只發虛擬徽章。重發與多領的代價很低,三條帳務規則可以簡化。
- 不願意保留任何延遲入帳,只接受即時發放。拿掉延遲入帳與人工複核之後,防刷只剩門檻和事後追查:前者會傷到新用戶,後者追不回已經發出的獎勵。
各項做法的實際效果取決於活動規模與對手,閾值是否合適,要在上線後靠儀表板觀察與調整。
下一步:先備齊四項資料
- 獎項清單與每一項的預算
- 預估參與人數
- 活動期間
- 是否需要會員登入
備齊之後,可以聯絡開發團隊,說明活動內容與預計規模。評估開發廠商本身,另見挑選軟體開發廠商的方法。




