fix(history): 重构特码冷热功能 — 改为弹窗列表展示每期相对于前N期的冷热状态

改为批量查询模式:每期特码相对于它前面N期的出现频率判定冷热
弹窗内以表格形式展示所有期号、特码球、冷热标签、次数、排名
支持调整向前期数(10-100),打开弹窗自动查询
This commit is contained in:
2026-04-24 20:06:37 +08:00
parent efdef3798e
commit 9881f75e59
5 changed files with 187 additions and 199 deletions
+108 -85
View File
@@ -469,103 +469,126 @@ class History extends Model
}
/**
* 查询指定期号特码在向前y期范围内的冷热状态
* @param string|int $expect 指定期号
* 批量查询所有期号特码相对于前N期的冷热状态
* @param int $lookback 向前推算期数
* @return array|false 冷热状态数据,未找到返回false
* @param int $limit 查询总期数(从最新往前取)
* @return array [{expect, specialNum, count, avgCount, status, rank, totalPeriods}, ...]
*/
public function getSpecialHotColdByExpect($expect, $lookback = 30)
public function getSpecialHotColdList($lookback = 30, $limit = 100)
{
// 查询指定期号的数据
$target = $this->where('expect', $expect)->field('expect,num7,openTime')->find();
if (!$target) {
return false;
}
$specialNum = (int)$target['num7'];
// 查询该期往前lookback期的数据(按openTime排序,取目标期之前的lookback条)
// 查询最近 $limit 期数据,按时间倒序(最新在前)
$history = $this
->field('expect,num7,openTime')
->where('openTime', '<', $target['openTime'])
->order('openTime', 'desc')
->limit($lookback)
->limit($limit)
->select();
$totalPeriods = count($history);
if ($totalPeriods === 0) {
return [
'expect' => (string)$expect,
if (empty($history)) {
return [];
}
// 查询更多历史数据用于统计
$allHistory = $this
->field('expect,num7,openTime')
->order('openTime', 'desc')
->limit($limit + 200)
->select();
// 按openTime排序(确保顺序)
$historySorted = [];
foreach ($history as $row) {
$historySorted[] = $row;
}
$allHistorySorted = [];
foreach ($allHistory as $row) {
$allHistorySorted[] = $row;
}
$result = [];
// 对每一期,计算它前面lookback期的冷热状态
for ($i = 0; $i < count($historySorted); $i++) {
$row = $historySorted[$i];
$specialNum = (int)$row['num7'];
// 找到该期在全量数据中的位置
$targetTime = $row['openTime'];
$count = array_fill(1, 49, 0);
$periodCount = 0;
// 往前统计lookback期(跳过该期本身)
for ($j = 0; $j < count($allHistorySorted); $j++) {
$checkRow = $allHistorySorted[$j];
$checkTime = $checkRow['openTime'];
// 只统计比该期更早的数据
if ($checkTime >= $targetTime) {
continue;
}
if ($periodCount >= $lookback) {
break;
}
$num = (int)$checkRow['num7'];
if ($num >= 1 && $num <= 49) {
$count[$num]++;
}
$periodCount++;
}
if ($periodCount === 0) {
$result[] = [
'expect' => (string)$row['expect'],
'specialNum' => $specialNum,
'count' => 0,
'avgCount' => 0,
'status' => 'unknown',
'rank' => 0
];
continue;
}
$targetCount = $count[$specialNum];
$avgCount = $periodCount / 49;
$status = 'normal';
if ($avgCount > 0) {
if ($targetCount > $avgCount * 1.5) {
$status = 'hot';
} elseif ($targetCount < $avgCount * 0.5) {
$status = 'cold';
}
}
// 计算排名
$sorted = [];
for ($num = 1; $num <= 49; $num++) {
$sorted[] = ['num' => $num, 'count' => $count[$num]];
}
usort($sorted, function ($a, $b) {
return $b['count'] - $a['count'];
});
$rank = 0;
foreach ($sorted as $idx => $item) {
if ($item['num'] === $specialNum) {
$rank = $idx + 1;
break;
}
}
$result[] = [
'expect' => (string)$row['expect'],
'specialNum' => $specialNum,
'lookback' => $lookback,
'count' => 0,
'avgCount' => 0,
'status' => 'cold',
'rank' => 0,
'totalPeriods' => 0,
'allStats' => []
'count' => $targetCount,
'avgCount' => round($avgCount, 2),
'status' => $status,
'rank' => $rank
];
}
// 统计lookback范围内每个特码的出现次数
$count = array_fill(1, 49, 0);
foreach ($history as $row) {
$num = (int)$row['num7'];
if ($num >= 1 && $num <= 49) {
$count[$num]++;
}
}
// 计算目标特码的出现次数
$targetCount = $count[$specialNum];
// 计算平均出现次数(49个号码,totalPeriods期)
$avgCount = $totalPeriods / 49;
// 判定冷热
$status = 'normal';
if ($avgCount > 0) {
if ($targetCount > $avgCount * 1.5) {
$status = 'hot';
} elseif ($targetCount < $avgCount * 0.5) {
$status = 'cold';
}
}
// 计算排名(按出现次数降序)
$sorted = [];
for ($num = 1; $num <= 49; $num++) {
$sorted[] = ['num' => $num, 'count' => $count[$num]];
}
usort($sorted, function ($a, $b) {
return $b['count'] - $a['count'];
});
$rank = 0;
foreach ($sorted as $idx => $item) {
if ($item['num'] === $specialNum) {
$rank = $idx + 1;
break;
}
}
// 构建所有号码的统计(只返回top和bottom用于展示)
$hotNums = array_slice($sorted, 0, 5);
$coldNums = array_slice($sorted, -5);
$coldNums = array_reverse($coldNums);
return [
'expect' => (string)$expect,
'specialNum' => $specialNum,
'lookback' => $lookback,
'count' => $targetCount,
'avgCount' => round($avgCount, 2),
'status' => $status,
'rank' => $rank,
'totalPeriods' => $totalPeriods,
'hotNums' => $hotNums,
'coldNums' => $coldNums
];
return $result;
}
/**