From 6a782c2d6553277442db877c53b1f35d418b6ae8 Mon Sep 17 00:00:00 2001 From: leon <916117771@qq.com> Date: Thu, 30 Apr 2026 22:45:03 +0800 Subject: [PATCH] =?UTF-8?q?feat(history):=20=E6=96=B0=E5=A2=9E=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E5=BC=80=E5=A5=96=E6=95=B0=E6=8D=AE=E5=88=86=E6=9E=90?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 添加History控制器和模型实现开奖数据统计分析 - 实现遗漏号码、走势图、冷热号码等基础分析功能 - 添加波色分析、生肖分析、奇偶分析等专项统计 - 集成尾号转移矩阵和首号转移矩阵概率分析 - 在后台仪表板展示综合统计数据表格 --- application/admin/controller/History.php | 32 +++++- application/admin/model/History.php | 125 +++++++++++++++++++++- application/timetask/controller/Index.php | 14 ++- public/assets/js/backend/dashboard.js | 46 ++++++++ 4 files changed, 212 insertions(+), 5 deletions(-) diff --git a/application/admin/controller/History.php b/application/admin/controller/History.php index bf393b6..bca15bf 100644 --- a/application/admin/controller/History.php +++ b/application/admin/controller/History.php @@ -22,7 +22,7 @@ class History extends Backend * 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录) * @var array */ - protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard', 'specialHeatmap', 'specialHotColdAction', 'zoneTransition', 'colorWaveTransition', 'zoneToColorTransition', 'zodiacTransition']; + protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard', 'specialHeatmap', 'specialHotColdAction', 'zoneTransition', 'colorWaveTransition', 'zoneToColorTransition', 'zodiacTransition', 'tailNumberTransition', 'headNumberTransition']; public function _initialize() { @@ -358,5 +358,35 @@ class History extends Backend } } + /** + * 尾号转移概率统计 + */ + public function tailNumberTransition() + { + if ($this->request->isAjax()) { + $periods = $this->request->get('periods', 100, 'intval'); + if ($periods < 10 || $periods > 500) { + $this->error('期数范围必须在 10-500 之间'); + } + $result = $this->model->getTailNumberTransition($periods); + $this->success('查询成功', null, $result); + } + } + + /** + * 首号转移概率统计 + */ + public function headNumberTransition() + { + if ($this->request->isAjax()) { + $periods = $this->request->get('periods', 100, 'intval'); + if ($periods < 10 || $periods > 500) { + $this->error('期数范围必须在 10-500 之间'); + } + $result = $this->model->getHeadNumberTransition($periods); + $this->success('查询成功', null, $result); + } + } + } diff --git a/application/admin/model/History.php b/application/admin/model/History.php index ceb538e..9925ae4 100644 --- a/application/admin/model/History.php +++ b/application/admin/model/History.php @@ -468,7 +468,9 @@ class History extends Model 'zonetransition' => $this->getZoneTransition($periods), 'colorwavetransition' => $this->getColorWaveTransition($periods), 'zonetocolortransition' => $this->getZoneToZoneColor($periods), - 'zodiactransition' => $this->getZodiacTransition($periods) + 'zodiactransition' => $this->getZodiacTransition($periods), + 'tailnumbertransition' => $this->getTailNumberTransition($periods), + 'headnumbertransition' => $this->getHeadNumberTransition($periods) ]; } @@ -861,6 +863,127 @@ class History extends Model ]; } + /** + * 尾号转移概率统计 + * 统计特码从一个尾号转移到另一个尾号的概率(尾号0-9) + * @param int $periods 查询最近多少期 + * @return array {tails: [], matrix: [], probabilities: [], row_totals: [], total_transitions: int} + */ + public function getTailNumberTransition($periods = 100) + { + $history = $this + ->field('expect,num7,openTime') + ->order('openTime', 'desc') + ->limit($periods) + ->select(); + + if (empty($history) || count($history) < 2) { + return ['tails' => ['尾0','尾1','尾2','尾3','尾4','尾5','尾6','尾7','尾8','尾9'], 'matrix' => [], 'probabilities' => [], 'row_totals' => [], 'total_transitions' => 0]; + } + + $history = array_reverse($history); + + $tailLabels = ['尾0', '尾1', '尾2', '尾3', '尾4', '尾5', '尾6', '尾7', '尾8', '尾9']; + $numTails = 10; + $matrix = array_fill(0, $numTails, array_fill(0, $numTails, 0)); + $rowTotals = array_fill(0, $numTails, 0); + + $getTail = function ($num) { + return $num % 10; + }; + + $totalTransitions = 0; + for ($i = 0; $i < count($history) - 1; $i++) { + $currentNum = (int)$history[$i]['num7']; + $nextNum = (int)$history[$i + 1]['num7']; + if ($currentNum < 1 || $currentNum > 49 || $nextNum < 1 || $nextNum > 49) continue; + + $from = $getTail($currentNum); + $to = $getTail($nextNum); + $matrix[$from][$to]++; + $rowTotals[$from]++; + $totalTransitions++; + } + + $probabilities = array_fill(0, $numTails, array_fill(0, $numTails, 0)); + for ($i = 0; $i < $numTails; $i++) { + if ($rowTotals[$i] > 0) { + for ($j = 0; $j < $numTails; $j++) { + $probabilities[$i][$j] = round($matrix[$i][$j] / $rowTotals[$i] * 100, 1); + } + } + } + + return [ + 'tails' => $tailLabels, + 'matrix' => $matrix, + 'probabilities' => $probabilities, + 'row_totals' => $rowTotals, + 'total_transitions' => $totalTransitions + ]; + } + + /** + * 首号转移概率统计 + * 统计特码从一个首号转移到另一个首号的概率(首号0-4) + * 首号定义:1-9首号=0,10-19首号=1,20-29首号=2,30-39首号=3,40-49首号=4 + * @param int $periods 查询最近多少期 + * @return array {heads: [], matrix: [], probabilities: [], row_totals: [], total_transitions: int} + */ + public function getHeadNumberTransition($periods = 100) + { + $history = $this + ->field('expect,num7,openTime') + ->order('openTime', 'desc') + ->limit($periods) + ->select(); + + if (empty($history) || count($history) < 2) { + return ['heads' => ['首号0','首号1','首号2','首号3','首号4'], 'matrix' => [], 'probabilities' => [], 'row_totals' => [], 'total_transitions' => 0]; + } + + $history = array_reverse($history); + + $headLabels = ['首号0', '首号1', '首号2', '首号3', '首号4']; + $numHeads = 5; + $matrix = array_fill(0, $numHeads, array_fill(0, $numHeads, 0)); + $rowTotals = array_fill(0, $numHeads, 0); + + $getHead = function ($num) { + return intval(floor($num / 10)); + }; + + $totalTransitions = 0; + for ($i = 0; $i < count($history) - 1; $i++) { + $currentNum = (int)$history[$i]['num7']; + $nextNum = (int)$history[$i + 1]['num7']; + if ($currentNum < 1 || $currentNum > 49 || $nextNum < 1 || $nextNum > 49) continue; + + $from = $getHead($currentNum); + $to = $getHead($nextNum); + $matrix[$from][$to]++; + $rowTotals[$from]++; + $totalTransitions++; + } + + $probabilities = array_fill(0, $numHeads, array_fill(0, $numHeads, 0)); + for ($i = 0; $i < $numHeads; $i++) { + if ($rowTotals[$i] > 0) { + for ($j = 0; $j < $numHeads; $j++) { + $probabilities[$i][$j] = round($matrix[$i][$j] / $rowTotals[$i] * 100, 1); + } + } + } + + return [ + 'heads' => $headLabels, + 'matrix' => $matrix, + 'probabilities' => $probabilities, + 'row_totals' => $rowTotals, + 'total_transitions' => $totalTransitions + ]; + } + /** * 区域→区域转移矩阵,单元格内显示波色概率 * 统计上一期特码所在区域后,下一期特码在各区域的分布,以及每个区域内的波色占比 diff --git a/application/timetask/controller/Index.php b/application/timetask/controller/Index.php index 9391b16..cac53b2 100644 --- a/application/timetask/controller/Index.php +++ b/application/timetask/controller/Index.php @@ -4,6 +4,7 @@ namespace app\timetask\controller; use app\common\controller\Api; use think\Db; +use think\exception\HttpResponseException; class Index extends Api { @@ -38,12 +39,19 @@ class Index extends Api } } $this->success('获取成功'); - } catch (\Exception $e) { - $this->error($e->getMessage()); + } catch (HttpResponseException $e) { + // 这是框架正常的响应异常,直接抛出让它继续执行 + throw $e; + } catch (\Throwable $e) { + // 捕获其他异常和错误 + $errorMsg = $e->getMessage() ?: get_class($e); + $file = $e->getFile(); + $line = $e->getLine(); + \think\Log::write("get_history 异常: {$errorMsg} at {$file}:{$line}", 'error'); + $this->error('异常:' . $errorMsg . ' [' . $file . ':' . $line . ']'); } - } else { $this->error('获取失败'); } diff --git a/public/assets/js/backend/dashboard.js b/public/assets/js/backend/dashboard.js index c2c3212..1fada99 100644 --- a/public/assets/js/backend/dashboard.js +++ b/public/assets/js/backend/dashboard.js @@ -176,6 +176,52 @@ define(['jquery'], function ($) { } html += ''; } + + // 尾号转移矩阵 + var tnt = data.tailnumbertransition; + if (tnt && tnt.matrix && tnt.matrix.length > 0) { + html += '
| 特码 | '; + for (var z = 0; z < tnt.tails.length; z++) { + html += '' + tnt.tails[z] + ' | '; + } + html += '
|---|---|
| ' + tnt.tails[r] + ' | '; + for (var c = 0; c < tnt.tails.length; c++) { + var pct = tnt.probabilities[r][c]; + var cnt = tnt.matrix[r][c]; + var bg = pct > 20 ? '#e74c3c' : pct > 15 ? '#f39c12' : pct > 10 ? '#3498db' : pct > 0 ? '#95a5a6' : '#f5f5f5'; + var txt = pct > 10 ? '#fff' : '#333'; + html += '' + cnt + '次 ' + pct + '% | ';
+ }
+ html += '
| 特码 | '; + for (var z = 0; z < hnt.heads.length; z++) { + html += '' + hnt.heads[z] + ' | '; + } + html += '
|---|---|
| ' + hnt.heads[r] + ' | '; + for (var c = 0; c < hnt.heads.length; c++) { + var pct = hnt.probabilities[r][c]; + var cnt = hnt.matrix[r][c]; + var bg = pct > 30 ? '#e74c3c' : pct > 20 ? '#f39c12' : pct > 10 ? '#3498db' : pct > 0 ? '#95a5a6' : '#f5f5f5'; + var txt = pct > 10 ? '#fff' : '#333'; + html += '' + cnt + '次 ' + pct + '% | ';
+ }
+ html += '