PHP项目:半全场玩法开发入手思路
半全场(HT/FT)是竞彩足球里比较特殊的一个玩法,涉及"半场结果 × 全场结果"的组合,下面按业务理解 → 数据结构 → 赔率计算 → 结算逻辑 → 工程实现的顺序梳理入手路径。

先搞清楚业务规则(最容易被忽略)
入手前必须明确以下几点,否则后面代码全要返工:
- 9种组合:胜胜、胜平、胜负、平胜、平平、平负、负胜、负平、负负(主队视角)。
- 半场结果的判定:通常按 45 分钟(含伤停补时)比分,不含加时;但不同彩种/联赛规则可能不同(有的按"上半场结束官方比分")。必须确认数据源给出的半场比分口径。
- 全场结果同样按 90 分钟(含补时)常规时间,不含加时和点球。
- 取消/延期/腰斩的退票规则,各平台不同。
- 让球半全场是否存在(部分玩法有"让球半全场"),如果有,让球数作用于半场和全场分别计算还是统一让球。
建议第一步:把规则写成一份
rules.md,和结算、赔率、前端对齐后再动代码。
数据结构设计
赛事与比分表
-- 比赛表(关键字段) match ( id, league_id, home_team_id, away_team_id, match_time, ht_home_score, ht_away_score, -- 半场比分 ft_home_score, ft_away_score, -- 全场比分 status, -- 未开赛/进行中/已结束/取消/延期 ... )
要点:半场比分必须单独落库,不能只存全场,结算时只读快照,不要实时依赖第三方接口。
投注表(半全场注单)
bet_htft ( id, order_id, match_id, user_id, selection, -- 枚举:HH/HD/HA/DH/DD/DA/AH/AD/AA odds, -- 投注时锁定的赔率 amount, status, settle_time )
selection 用 2 字符编码(H=主胜/主队领先, D=平, A=客胜/客队领先),第一位半场、第二位全场,便于程序判断和索引。
核心:结果映射与结算
比分 → 结果枚举
function resultCode(int $homeScore, int $awayScore): string
{
if ($homeScore > $awayScore) return 'H';
if ($homeScore === $awayScore) return 'D';
return 'A';
}
组合键
$ht = resultCode($match['ht_home_score'], $match['ht_away_score']); $ft = resultCode($match['ft_home_score'], $match['ft_away_score']); $winSelection = $ht . $ft; // "HD" = 半场主胜、全场平
结算
foreach ($bets as $bet) {
if ($bet['selection'] === $winSelection) {
// 中奖:派彩 = amount * odds,注意是否含本金
} else {
// 未中
}
}
注意点:
- 结算必须幂等(同一比赛重复触发结算不能重复派彩),用
settle_status+ 事务 + 唯一约束。 - 半场比分缺失(腰斩)要按平台规则处理退票。
- 赔率用投注时锁定的值,不要用结算时的值。
赔率来源与计算
半全场赔率一般由数据供应商提供(如某些体育数据 API),不建议自己算,原因:
- 半全场是联合概率事件,需要半场和全场的联合分布,自算误差大。
- 主流做法是接入供应商盘口 + 自己加 margin(抽水)。
如果确实要自算(研究/内部产品),思路:
P(半场=X, 全场=Y) 需要半场比分概率矩阵
→ 用 Poisson / Dixon-Coles 模型估计各时段进球
→ 结合半场和全场的相关性(半场领先的队全场更容易胜)
→ 归一化后加 margin 转成欧赔
工程上把赔率计算做成独立服务,输入比赛特征,输出 9 个赔率,方便回测。
PHP 工程落地建议
- 枚举用常量类,不要裸字符串散落各处:
final class HtftSelection {
public const HH = 'HH'; // ... 9个
public static function all(): array { /* ... */ }
}
-
结算做成命令行任务(Laravel Command / Symfony Console + 队列),按
match_id加锁,避免并发重复结算。 -
赔率变更走快照:用户下注时把
odds写死进注单,历史赔率另存一张表用于审计。 -
写单元测试覆盖 9 种组合 + 边界(0:0、大比分、半场平全场非平、腰斩等),这是最容易出 bug 的地方。
// 测试样例 assert(resolveHtft(1,0, 2,1) === 'HH'); assert(resolveHtft(0,0, 1,1) === 'DD'); assert(resolveHtft(1,0, 1,1) === 'HD'); assert(resolveHtft(0,1, 2,1) === 'AH');
-
退票/异常流程单独处理,状态机:
未结算 → 已结算 / 已退票,不允许跳过。 -
对账:结算后跑一遍"注单总额 vs 派彩总额",与财务侧核对。
推荐的入手顺序
- 读透平台/彩种的半全场规则,写
rules.md。 - 建比分表 + 注单表,确定
selection编码。 - 写
resultCode+ 组合键 + 结算单测(用假数据先把 9 种情况跑通)。 - 接赔率数据源,做赔率快照。
- 做结算任务 + 幂等 + 对账。
- 最后做前端展示和投注交互(9 个选项的排列、赔率刷新、组合提示)。
一句话总结:半全场的技术难点不在"9 种组合的判断"(很简单),而在半场比分口径、赔率快照、结算幂等和异常退票这四件事上,把这四点用规则文档和单测固定下来,PHP 实现就很顺。
如果你告诉我具体用的是哪个框架(Laravel/ThinkPHP 等)和是否接第三方盘口,我可以给更贴合的目录结构和代码骨架。