業務流程
- 驗證用戶積分是否足夠(條件更新防並發)。
- 依兌換積分檢查對應獎品庫存(話費/實物)。
- 庫存足夠時扣減庫存、寫入獲獎記錄、回傳成功;否則回滾積分、回傳失敗。
- 記錄積分消耗明細。
核心程式碼
<?php
// $user_id = $_SESSION['zaszh_user_id']; // 實際上線啟用
$user_id = 1; // 測試用
$exchange_points = intval($_GET['exchange_points']);
require('connect_database.php');
// 扣減積分(條件更新:積分足夠才扣減)
$mysqli->query("UPDATE zaszh_user SET answer_points = answer_points - {$exchange_points} WHERE id = '{$user_id}' AND answer_points >= {$exchange_points}");
if ($mysqli->affected_rows) {
switch ($exchange_points) {
case 200: // 5 元話費
$mysqli->query("UPDATE zaszh_telephone_charge_surplus SET charge_surplus = charge_surplus - 5 WHERE date = substring(now(),1,10) AND charge_surplus >= 5");
if ($mysqli->affected_rows) {
$mysqli->query("INSERT INTO zaszh_award (user_id, prize, create_date) VALUES ('{$user_id}', '5元話費', unix_timestamp(now()))");
echo json_encode(array('status' => 'success', 'msg' => '5元話費'));
} else {
$mysqli->query("UPDATE zaszh_user SET answer_points = answer_points + {$exchange_points} WHERE id = '{$user_id}'");
}
break;
case 400: // 10 元話費
$mysqli->query("UPDATE zaszh_telephone_charge_surplus SET charge_surplus = charge_surplus - 10 WHERE date = substring(now(),1,10) AND charge_surplus >= 10");
if ($mysqli->affected_rows) {
$mysqli->query("INSERT INTO zaszh_award (user_id, prize, create_date) VALUES ('{$user_id}', '10元話費', unix_timestamp(now()))");
echo json_encode(array('status' => 'success', 'msg' => '10元話費'));
} else {
$mysqli->query("UPDATE zaszh_user SET answer_points = answer_points + {$exchange_points} WHERE id = '{$user_id}'");
}
break;
}
// 記錄積分消耗
$mysqli->query("INSERT INTO zaszh_answer_points_consume (user_id, points_consume, consume_for, create_date) VALUES ('{$user_id}', '{$exchange_points}', 'exchange', unix_timestamp(now()))");
} else {
echo json_encode(array('status' => 'error', 'msg' => '您的積分不足。'));
}
$mysqli->close();
?>
關鍵設計點
- 條件更新防並發:
UPDATE ... WHERE answer_points >= {$exchange_points} 確保原子性扣減。
- 庫存同步扣減:話費按日期分表(
zaszh_telephone_charge_surplus),實物獎品可用 zaszh_prize_surplus。
- 失敗回滾:庫存不足時回滾積分,避免用戶損失。
- 完整審計追蹤:
zaszh_award(獲獎記錄)、zaszh_answer_points_consume(消耗明細)雙重記錄。
擴展建議
- 引入事務(Transaction)包裹扣減積分與扣減庫存,確保強一致性。
- 獎品類型抽象化,避免 switch-case 硬編碼,採用資料驅動配置。
- 介面改為 POST + CSRF Token,防止 CSRF 攻擊。