feat(admin): 新增尾首概率分析弹窗

在history页面工具栏添加"尾首概率"按钮,弹窗显示:
- 下一期尾数与前一期尾数相同的概率
- 下一期首位与前一期首位相同的概率
支持切换统计期数(30/50/100/200期)
This commit is contained in:
2026-05-02 15:36:00 +08:00
parent 8b2590c5b5
commit 919fbbc148
4 changed files with 134 additions and 1 deletions
+13 -1
View File
@@ -22,7 +22,7 @@ class History extends Backend
* 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录) * 无需额外权限检查的方法(但仍在 admin 模块内,需要 admin 登录)
* @var array * @var array
*/ */
protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard', 'specialHeatmap', 'specialHotColdAction', 'zoneTransition', 'colorWaveTransition', 'zoneToColorTransition', 'zodiacTransition', 'tailNumberTransition', 'headNumberTransition', 'predict', 'predictV2', 'predictV3', 'optimizeWeights', 'predictByNormalRelation']; protected $noNeedRight = ['missingNum', 'trendData', 'hotColdNumbers', 'colorWaveAnalysis', 'zodiacAnalysis', 'oddEvenAnalysis', 'bigSmallAnalysis', 'specialTrend', 'consecutiveNumbers', 'tailNumbers', 'dashboard', 'specialHeatmap', 'specialHotColdAction', 'zoneTransition', 'colorWaveTransition', 'zoneToColorTransition', 'zodiacTransition', 'tailNumberTransition', 'headNumberTransition', 'tailHeadProb', 'predict', 'predictV2', 'predictV3', 'optimizeWeights', 'predictByNormalRelation'];
public function _initialize() public function _initialize()
{ {
@@ -388,6 +388,18 @@ class History extends Backend
} }
} }
/**
* 尾首概率分析
*/
public function tailHeadProb()
{
if ($this->request->isAjax()) {
$periods = $this->request->get('periods', 100, 'intval');
$result = $this->model->getTailHeadProbability($periods);
$this->success('查询成功', null, $result);
}
}
/** /**
* 综合预测号码 * 综合预测号码
* 基于历史多维度转移概率分析,给出号码预测建议 * 基于历史多维度转移概率分析,给出号码预测建议
+54
View File
@@ -984,6 +984,60 @@ class History extends Model
]; ];
} }
/**
* 尾首概率:下一期尾数/首位与前一期相同的概率
* 统计相邻两期特码的尾数(num%10)和首位(floor(num/10))变化
* @param int $periods 查询最近多少期
* @return array {tailProb: float, headProb: float, totalTransitions: int, periodCount: int}
*/
public function getTailHeadProbability($periods = 100)
{
$history = $this
->field('expect,num7,openTime')
->order('openTime', 'desc')
->limit($periods)
->select();
if (empty($history) || count($history) < 2) {
return ['tailProb' => 0, 'headProb' => 0, 'totalTransitions' => 0, 'periodCount' => 0];
}
// 按时间升序排列(旧→新)
$history = array_reverse($history);
$tailSame = 0;
$headSame = 0;
$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;
}
$totalTransitions++;
// 尾数相同(个位相同)
if (($currentNum % 10) === ($nextNum % 10)) {
$tailSame++;
}
// 首位相同(十位相同)
if (intval(floor($currentNum / 10)) === intval(floor($nextNum / 10))) {
$headSame++;
}
}
return [
'tailProb' => $totalTransitions > 0 ? round($tailSame / $totalTransitions * 100, 2) : 0,
'headProb' => $totalTransitions > 0 ? round($headSame / $totalTransitions * 100, 2) : 0,
'totalTransitions' => $totalTransitions,
'periodCount' => count($history)
];
}
/** /**
* 区域→区域转移矩阵,单元格内显示波色概率 * 区域→区域转移矩阵,单元格内显示波色概率
* 统计上一期特码所在区域后,下一期特码在各区域的分布,以及每个区域内的波色占比 * 统计上一期特码所在区域后,下一期特码在各区域的分布,以及每个区域内的波色占比
@@ -21,6 +21,7 @@
<a href="javascript:;" class="btn btn-primary btn-numberfilter" title="筛号器"><i class="fa fa-filter"></i> 筛号器</a> <a href="javascript:;" class="btn btn-primary btn-numberfilter" title="筛号器"><i class="fa fa-filter"></i> 筛号器</a>
<a href="javascript:;" class="btn btn-success btn-predict" title="智能预测"><i class="fa fa-magic"></i> 智能预测</a> <a href="javascript:;" class="btn btn-success btn-predict" title="智能预测"><i class="fa fa-magic"></i> 智能预测</a>
<a href="javascript:;" class="btn btn-info btn-normal-relation" title="正码关联预测"><i class="fa fa-link"></i> 正码关联预测</a> <a href="javascript:;" class="btn btn-info btn-normal-relation" title="正码关联预测"><i class="fa fa-link"></i> 正码关联预测</a>
<a href="javascript:;" class="btn btn-warning btn-tailheadprob" title="尾首概率"><i class="fa fa-percent"></i> 尾首概率</a>
<!-- <a href="javascript:;" class="btn btn-success btn-dashboard" title="{:__('Dashboard')}"><i class="fa fa-tachometer"></i> {:__('Dashboard')}</a>--> <!-- <a href="javascript:;" class="btn btn-success btn-dashboard" title="{:__('Dashboard')}"><i class="fa fa-tachometer"></i> {:__('Dashboard')}</a>-->
<!-- <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('history/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>--> <!-- <a href="javascript:;" class="btn btn-success btn-add {:$auth->check('history/add')?'':'hide'}" title="{:__('Add')}" ><i class="fa fa-plus"></i> {:__('Add')}</a>-->
</div> </div>
+66
View File
@@ -112,6 +112,11 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
$(document).off('click', '.btn-normal-relation').on('click', '.btn-normal-relation', function () { $(document).off('click', '.btn-normal-relation').on('click', '.btn-normal-relation', function () {
Controller.api.showNormalRelationDialog(); Controller.api.showNormalRelationDialog();
}); });
// 尾首概率按钮事件
$(document).off('click', '.btn-tailheadprob').on('click', '.btn-tailheadprob', function () {
Controller.api.showTailHeadProbabilityDialog();
});
}, },
add: function () { add: function () {
Controller.api.bindevent(); Controller.api.bindevent();
@@ -2163,6 +2168,67 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
} }
$('#nr-result', layero).html(html); $('#nr-result', layero).html(html);
},
/**
* 尾首概率弹窗
*/
showTailHeadProbabilityDialog: function () {
var content = '<div id="tailheadprob-content" style="padding:20px 30px;">';
content += '<div style="text-align:center;margin-bottom:20px;">';
content += '<label style="margin-right:10px;">统计期数:</label>';
content += '<select id="tailheadprob-periods" style="width:100px;padding:5px 10px;">';
content += '<option value="30">近30期</option>';
content += '<option value="50">近50期</option>';
content += '<option value="100" selected>近100期</option>';
content += '<option value="200">近200期</option>';
content += '</select>';
content += ' <button class="btn btn-primary btn-sm" id="btn-tailheadprob-query" style="margin-left:10px;"><i class="fa fa-search"></i> 查询</button>';
content += '</div>';
content += '<div id="tailheadprob-result"></div>';
content += '</div>';
layer.open({
type: 1,
title: '尾首概率分析',
area: ['500px', '380px'],
content: content,
success: function (layero) {
$('#btn-tailheadprob-query', layero).on('click', function () {
var periods = $('#tailheadprob-periods').val();
$.ajax({
url: 'history/tailHeadProb',
type: 'GET',
data: { periods: periods },
dataType: 'json',
success: function (ret) {
if (ret.code == 1) {
var d = ret.msg;
var html = '<div style="margin-top:15px;">';
html += '<div style="padding:15px;background:#f0f9ff;border-radius:8px;margin-bottom:12px;">';
html += '<div style="font-size:14px;font-weight:bold;color:#1565c0;margin-bottom:10px;"><i class="fa fa-bar-chart"></i> 概率统计</div>';
html += '<table class="table table-bordered" style="margin-bottom:0;">';
html += '<tr><th style="width:50%;">对比项</th><th>相同概率</th></tr>';
html += '<tr><td>下一期尾数与前一期尾数相同</td><td style="text-align:center;font-size:18px;font-weight:bold;color:#e65100;">' + d.tailProb + '%</td></tr>';
html += '<tr><td>下一期首位与前一期首位相同</td><td style="text-align:center;font-size:18px;font-weight:bold;color:#e65100;">' + d.headProb + '%</td></tr>';
html += '</table></div>';
html += '<div style="font-size:12px;color:#999;">共分析 ' + d.periodCount + ' 期数据,' + d.totalTransitions + ' 次转移</div>';
html += '</div>';
$('#tailheadprob-result').html(html);
} else {
Toastr.error(ret.msg);
}
},
error: function () {
Toastr.error('请求失败,请重试');
}
});
});
// 初始加载
$('#btn-tailheadprob-query', layero).trigger('click');
}
});
} }
} }
}; };