PHP项目中统计"协防补位成功次数"的实现方案
由于你没有提供具体的项目代码,我给出一个通用的设计思路和实现方案,你可以根据实际项目结构进行调整。

先明确业务概念
协防补位成功通常指:
- 玩家 A 的防守位置被攻击时,玩家 B 前往支援
- B 到达时该位置仍在战斗中
- 最终防守成功(未被攻破)
不同游戏定义不同,需要先确认清楚。
数据库设计
方案1:日志表 + 聚合统计(推荐)
-- 协防行为记录表 CREATE TABLE `assist_defense_log` ( `id` BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY, `helper_id` INT UNSIGNED NOT NULL COMMENT '协防者UID', `target_id` INT UNSIGNED NOT NULL COMMENT '被协防者UID', `battle_id` BIGINT UNSIGNED NOT NULL COMMENT '战斗ID', `position_id` INT NOT NULL COMMENT '补位位置', `status` TINYINT NOT NULL DEFAULT 0 COMMENT '0进行中 1成功 2失败', `created_at` INT UNSIGNED NOT NULL, `finished_at` INT UNSIGNED DEFAULT 0, KEY `idx_helper_status` (`helper_id`, `status`), KEY `idx_battle` (`battle_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
方案2:计数器字段(查询快)
ALTER TABLE `user_stat` ADD COLUMN `assist_defense_success` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '协防补位成功次数', ADD COLUMN `assist_defense_total` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '协防补位总次数';
PHP 代码实现
记录协防行为(战斗开始时)
class AssistDefenseService
{
private PDO $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
/**
* 记录协防补位(战斗开始时调用)
*/
public function recordAssist(int $helperId, int $targetId, int $battleId, int $positionId): int
{
$sql = "INSERT INTO assist_defense_log
(helper_id, target_id, battle_id, position_id, status, created_at)
VALUES (:helper_id, :target_id, :battle_id, :position_id, 0, :now)";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':helper_id' => $helperId,
':target_id' => $targetId,
':battle_id' => $battleId,
':position_id' => $positionId,
':now' => time(),
]);
return (int)$this->db->lastInsertId();
}
/**
* 战斗结束时更新结果(协防成功才累计)
*/
public function finishAssist(int $battleId, bool $defenseSuccess): void
{
$this->db->beginTransaction();
try {
// 找出这场战斗的所有协防记录
$stmt = $this->db->prepare(
"SELECT id, helper_id FROM assist_defense_log
WHERE battle_id = :bid AND status = 0 FOR UPDATE"
);
$stmt->execute([':bid' => $battleId]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
if (empty($rows)) {
$this->db->commit();
return;
}
$newStatus = $defenseSuccess ? 1 : 2;
// 更新日志状态
$updateStmt = $this->db->prepare(
"UPDATE assist_defense_log
SET status = :status, finished_at = :now
WHERE id = :id"
);
// 成功则累加用户统计
$incStmt = $this->db->prepare(
"UPDATE user_stat
SET assist_defense_success = assist_defense_success + 1,
assist_defense_total = assist_defense_total + 1
WHERE uid = :uid"
);
$incTotalStmt = $this->db->prepare(
"UPDATE user_stat
SET assist_defense_total = assist_defense_total + 1
WHERE uid = :uid"
);
foreach ($rows as $row) {
$updateStmt->execute([
':status' => $newStatus,
':now' => time(),
':id' => $row['id'],
]);
if ($defenseSuccess) {
$incStmt->execute([':uid' => $row['helper_id']]);
} else {
$incTotalStmt->execute([':uid' => $row['helper_id']]);
}
}
$this->db->commit();
} catch (Throwable $e) {
$this->db->rollBack();
throw $e;
}
}
}
查询某玩家的协防补位成功次数
/**
* 从计数器读(推荐,需保证写入时同步)
*/
public function getAssistSuccessCount(int $uid): int
{
$stmt = $this->db->prepare(
"SELECT assist_defense_success FROM user_stat WHERE uid = :uid"
);
$stmt->execute([':uid' => $uid]);
return (int)($stmt->fetchColumn() ?: 0);
}
/**
* 从日志表实时聚合(数据量大时慢,适合对账)
*/
public function getAssistSuccessCountByLog(int $uid): int
{
$stmt = $this->db->prepare(
"SELECT COUNT(*) FROM assist_defense_log
WHERE helper_id = :uid AND status = 1"
);
$stmt->execute([':uid' => $uid]);
return (int)$stmt->fetchColumn();
}
进阶:Redis 缓存加速
如果是高并发游戏,用 Redis 做实时计数:
class AssistDefenseCounter
{
private Redis $redis;
private PDO $db;
public function __construct(Redis $redis, PDO $db)
{
$this->redis = $redis;
$this->db = $db;
}
public function incr(int $uid, int $delta = 1): void
{
$key = "assist:success:{$uid}";
$this->redis->incrBy($key, $delta);
// 加入待持久化队列
$this->redis->sAdd('assist:sync:pending', $uid);
}
public function get(int $uid): int
{
$key = "assist:success:{$uid}";
if ($this->redis->exists($key)) {
// 缓存没命中就回源
}
$val = $this->redis->get($key);
if ($val !== false) {
return (int)$val;
}
return $this->getAssistSuccessCount($uid);
}
/** 定时任务批量刷回 DB */
public function flushToDb(): void
{
$uids = $this->redis->sMembers('assist:sync:pending');
foreach ($uids as $uid) {
$val = (int)$this->redis->get("assist:success:{$uid}");
// 更新 MySQL...
$this->redis->sRem('assist:sync:pending', $uid);
}
}
}
需要你补充的信息
要给出贴合你项目的代码,请告诉我:
- 框架:Laravel / ThinkPHP / 原生 PHP / Hyperf / Swoole?
- 业务定义:怎么才算"协防补位成功"?(战斗胜利?补位到位?)
- 触发点:哪个接口/事件里触发(战斗结算、定时任务?)
- 表结构:现有的用户表、战斗表、日志表字段?
- 统计需求:是实时查、定时统计、还是活动期内累计?
把关键代码/表结构贴出来,我可以帮你直接改写成可落地的代码。