PHP项目中"协防补位成功次数"的实现方案
在游戏类PHP项目中,"协防补位成功次数"通常是一个玩家统计数据,由于你没有提供具体代码,我按常见实现方式给你完整方案。

数据库设计
玩家统计主表(推荐)
CREATE TABLE `player_stats` ( `player_id` INT UNSIGNED NOT NULL COMMENT '玩家ID', `assist_defense_success` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '协防补位成功次数', `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`player_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='玩家统计';
明细流水表(用于防刷、追溯、每日/赛季统计)
CREATE TABLE `assist_defense_log` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `player_id` INT UNSIGNED NOT NULL COMMENT '助攻玩家', `target_id` INT UNSIGNED NOT NULL COMMENT '被协防玩家', `battle_id` BIGINT UNSIGNED NOT NULL COMMENT '战斗ID', `result` TINYINT NOT NULL COMMENT '1成功 0失败', `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_battle_player` (`battle_id`,`player_id`) COMMENT '同一场战斗去重', KEY `idx_player_time` (`player_id`,`created_at`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
uk_battle_player唯一索引是关键,防止同一场战斗重复 +1(常见刷分漏洞)。
核心服务类
<?php
class AssistDefenseService
{
private PDO $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
/**
* 记录一次协防补位结果
* @param int $playerId 协防玩家
* @param int $targetId 被协防玩家
* @param int $battleId 战斗ID
* @param bool $success 是否成功
* @return bool 是否计为一次有效成功
*/
public function record(int $playerId, int $targetId, int $battleId, bool $success): bool
{
if ($playerId === $targetId) {
return false; // 不能给自己协防
}
$this->db->beginTransaction();
try {
// 1. 写流水(唯一索引防重)
$stmt = $this->db->prepare(
"INSERT INTO assist_defense_log (player_id, target_id, battle_id, result)
VALUES (:p, :t, :b, :r)"
);
$stmt->execute([
':p' => $playerId,
':t' => $targetId,
':b' => $battleId,
':r' => $success ? 1 : 0,
]);
// 2. 仅成功时累加统计
if ($success) {
$upd = $this->db->prepare(
"INSERT INTO player_stats (player_id, assist_defense_success)
VALUES (:p, 1)
ON DUPLICATE KEY UPDATE assist_defense_success = assist_defense_success + 1"
);
$upd->execute([':p' => $playerId]);
}
$this->db->commit();
return $success;
} catch (PDOException $e) {
$this->db->rollBack();
// 1062 = 唯一键冲突,说明这场战斗已记录过,直接忽略
if ($e->errorInfo[1] === 1062) {
return false;
}
throw $e;
}
}
/** 查询玩家协防补位成功次数 */
public function getCount(int $playerId): int
{
$stmt = $this->db->prepare(
"SELECT assist_defense_success FROM player_stats WHERE player_id = :p"
);
$stmt->execute([':p' => $playerId]);
return (int)($stmt->fetchColumn() ?: 0);
}
}
调用示例
$service = new AssistDefenseService($pdo);
// 战斗结算时调用
$service->record(
playerId: 1001,
targetId: 1002,
battleId: 20240520001,
success: true
);
// 展示用
echo "协防补位成功次数:" . $service->getCount(1001);
进阶:排行榜 / 每日任务
排行榜(Redis + DB 双写)
// 成功后同步到 Redis ZSET,实时排行
$redis->zIncrBy('rank:assist_defense:day:' . date('Ymd'), 1, $playerId);
每日统计(按需取舍)
SELECT COUNT(*) FROM assist_defense_log WHERE player_id = ? AND result = 1 AND created_at >= ?
或新增 daily_stats(player_id, date, count) 表,避免大表 count。
常见坑点提醒
| 坑 | 建议 |
|---|---|
| 同一场战斗重复上报 | 用 UNIQUE(battle_id, player_id) 兜底 |
| 并发加一丢失 | 用 UPDATE ... SET x = x + 1 或 Redis 原子累加 |
| 自己给自己协防刷分 | 校验 player_id != target_id |
| 统计与流水不一致 | 统计字段以流水为准,定期对账修复 |
| 大表 count 慢 | 用统计表 + Redis,避免频繁 count |
如果你能贴出你项目里现有的相关代码(比如战斗结算逻辑、统计表结构),我可以直接帮你定位"协防补位成功次数"该怎么改或在哪查看。