column('color', 'num'); $history = $this ->field('expect,num1,num2,num3,num4,num5,num6,num7,openTime') ->order('openTime', 'desc') ->limit($periods) ->select(); if (empty($history)) { return ['expects' => [], 'data' => [], 'colorMap' => []]; } $expects = []; $data = []; foreach ($history as $row) { $expects[] = (string)$row['expect']; if ($type === 'special') { $data[] = ['num7' => (int)$row['num7']]; } else { $row_data = []; for ($i = 1; $i <= 7; $i++) { $row_data['num' . $i] = (int)$row['num' . $i]; } $data[] = $row_data; } } // 反转数组,使最远的数据在左边,最近的数据在右边(从左往右,从远到近) $expects = array_reverse($expects); $data = array_reverse($data); return [ 'expects' => $expects, 'data' => $data, 'colorMap' => $colorMap ]; } /** * 获取冷热号码 * @param int $periods 查询最近多少期 * @param string $type 查询类型 all=全部号码 special=仅特码 * @return array {hot: [], cold: [], all: []} */ public function getHotColdNumbers($periods = 30, $type = 'all') { $num_model = new Num(); $colorMap = $num_model->column('color', 'num'); $animalMap = $num_model->column('animal', 'num'); $history = $this ->field('expect,num1,num2,num3,num4,num5,num6,num7,openTime') ->order('openTime', 'desc') ->limit($periods) ->select(); if (empty($history)) { return ['hot' => [], 'cold' => [], 'all' => []]; } $fields = ($type === 'special') ? ['num7'] : ['num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7']; // 统计每个号码的出现次数 $count = array_fill(1, 49, 0); $totalAppearances = 0; foreach ($history as $row) { foreach ($fields as $field) { $num = (int)$row[$field]; if ($num >= 1 && $num <= 49) { $count[$num]++; $totalAppearances++; } } } $all = []; for ($num = 1; $num <= 49; $num++) { $percent = $totalAppearances > 0 ? round($count[$num] / $totalAppearances * 100, 1) : 0; $all[] = [ 'num' => $num, 'count' => $count[$num], 'percent' => $percent, 'color' => $colorMap[$num] ?? '—', 'animal' => $animalMap[$num] ?? '—' ]; } // 按出现次数降序排序 $sorted = $all; usort($sorted, function ($a, $b) { return $b['count'] - $a['count']; }); // 热号: top 10, 冷号: bottom 10 $hot = array_slice($sorted, 0, 10); $cold = array_slice($sorted, -10); $cold = array_reverse($cold); return ['hot' => $hot, 'cold' => $cold, 'all' => $all]; } /** * 计算遗漏号码 * @param int $periods 查询最近多少期 * @param string $type 查询类型 all=全部号码 special=仅特码 * @return array [{num: int, omit: int, color: string}, ...] */ public function getMissingNumbers($periods = 10, $type = 'all') { // 查询最近 $periods 期开奖数据 if ($type === 'special') { $history = $this ->field('expect,num7') ->order('openTime', 'desc') ->limit($periods) ->select(); } else { $history = $this ->field('expect,num1,num2,num3,num4,num5,num6,num7') ->order('openTime', 'desc') ->limit($periods) ->select(); } // 收集最近 $periods 期出现过的号码 $appeared = []; foreach ($history as $row) { if ($type === 'special') { $fields = ['num7']; } else { $fields = ['num1', 'num2', 'num3', 'num4', 'num5', 'num6', 'num7']; } foreach ($fields as $field) { if ($row[$field] !== null && $row[$field] !== '') { $appeared[(int)$row[$field]] = true; } } } // 获取遗漏号码(1-49中未出现的) $missing = []; for ($num = 1; $num <= 49; $num++) { if (!isset($appeared[$num])) { $missing[] = $num; } } // 查询更多历史数据用于计算遗漏期数 if ($type === 'special') { $allHistory = $this ->field('num7') ->order('openTime', 'desc') ->limit(500) ->select(); } else { $allHistory = $this ->field('num1,num2,num3,num4,num5,num6,num7') ->order('openTime', 'desc') ->limit(500) ->select(); } // 查询波色映射 $num_model = new Num(); $colorMap = $num_model->column('color', 'num'); // 计算遗漏期数并组装结果 $result = []; foreach ($missing as $num) { $omitCount = $this->calcOmitCount($num, $allHistory, $type); $result[] = [ 'num' => $num, 'omit' => $omitCount, 'color' => $colorMap[$num] ?? '—' ]; } // 按遗漏期数降序排序 usort($result, function ($a, $b) { return $b['omit'] - $a['omit']; }); return $result; } /** * 计算某个号码的遗漏期数 * @param int $num 号码 * @param array $allHistory 历史数据(已按openTime DESC排序) * @param string $type 查询类型 all=全部号码 special=仅特码 * @return int 遗漏期数 */ private function calcOmitCount($num, $allHistory, $type = 'all') { foreach ($allHistory as $idx => $row) { if ($type === 'special') { if ((int)$row['num7'] === $num) { return $idx; } } else { for ($i = 1; $i <= 7; $i++) { if ((int)$row['num' . $i] === $num) { return $idx; } } } } return count($allHistory); } /** * 波色分析 */ public function getColorWaveAnalysis($periods = 30, $type = 'all') { $num_model = new Num(); $colorMap = $num_model->column('color', 'num'); $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['red' => 0, 'blue' => 0, 'green' => 0, 'red_pct' => 0, 'blue_pct' => 0, 'green_pct' => 0, 'details' => []]; $fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7']; $colors = ['红' => 0, '蓝' => 0, '绿' => 0]; $total = 0; foreach ($history as $row) { foreach ($fields as $f) { $num = (int)$row[$f]; $color = $colorMap[$num] ?? ''; if (strpos($color, '红') !== false) { $colors['红']++; $total++; } elseif (strpos($color, '蓝') !== false) { $colors['蓝']++; $total++; } elseif (strpos($color, '绿') !== false) { $colors['绿']++; $total++; } } } return [ 'red' => $colors['红'], 'blue' => $colors['蓝'], 'green' => $colors['绿'], 'red_pct' => $total ? round($colors['红']/$total*100,1) : 0, 'blue_pct' => $total ? round($colors['蓝']/$total*100,1) : 0, 'green_pct' => $total ? round($colors['绿']/$total*100,1) : 0, 'total' => $total ]; } /** * 生肖分析 */ public function getZodiacAnalysis($periods = 30, $type = 'all') { $num_model = new Num(); $animalMap = $num_model->column('animal', 'num'); $colorMap = $num_model->column('color', 'num'); $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['list' => []]; $fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7']; $counts = []; foreach ($history as $row) { foreach ($fields as $f) { $num = (int)$row[$f]; $animal = $animalMap[$num] ?? '未知'; if (!isset($counts[$animal])) $counts[$animal] = ['animal' => $animal, 'count' => 0, 'color' => $colorMap[$num] ?? '—']; $counts[$animal]['count']++; } } $list = array_values($counts); usort($list, function ($a, $b) { return $b['count'] - $a['count']; }); $total = array_sum(array_column($list, 'count')); foreach ($list as &$item) { $item['percent'] = $total ? round($item['count']/$total*100, 1) : 0; } return ['list' => $list]; } /** * 奇偶分析 */ public function getOddEvenAnalysis($periods = 30, $type = 'all') { $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['odd' => 0, 'even' => 0, 'odd_pct' => 0, 'even_pct' => 0, 'per_period' => []]; $fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7']; $odd = 0; $even = 0; $perPeriod = []; foreach ($history as $row) { $p_odd = 0; $p_even = 0; foreach ($fields as $f) { $num = (int)$row[$f]; if ($num % 2 == 0) { $even++; $p_even++; } else { $odd++; $p_odd++; } } $perPeriod[] = ['expect' => $row['expect'], 'odd' => $p_odd, 'even' => $p_even]; } $total = $odd + $even; return [ 'odd' => $odd, 'even' => $even, 'odd_pct' => $total ? round($odd/$total*100, 1) : 0, 'even_pct' => $total ? round($even/$total*100, 1) : 0, 'per_period' => $perPeriod ]; } /** * 大小分析(1-24为小,25-49为大) */ public function getBigSmallAnalysis($periods = 30, $type = 'all') { $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['big' => 0, 'small' => 0, 'big_pct' => 0, 'small_pct' => 0, 'per_period' => []]; $fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7']; $big = 0; $small = 0; $perPeriod = []; foreach ($history as $row) { $p_big = 0; $p_small = 0; foreach ($fields as $f) { $num = (int)$row[$f]; if ($num >= 25) { $big++; $p_big++; } else { $small++; $p_small++; } } $perPeriod[] = ['expect' => $row['expect'], 'big' => $p_big, 'small' => $p_small]; } $total = $big + $small; return [ 'big' => $big, 'small' => $small, 'big_pct' => $total ? round($big/$total*100, 1) : 0, 'small_pct' => $total ? round($small/$total*100, 1) : 0, 'per_period' => $perPeriod ]; } /** * 特码走势 */ public function getSpecialTrend($periods = 30) { $num_model = new Num(); $colorMap = $num_model->column('color', 'num'); // 先取最近 $periods 条数据 $history = $this->field('expect,num7,openTime')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['expects' => [], 'specials' => [], 'colors' => []]; // 反转,使数据从左到右为从远到近 $history = array_reverse($history); $expects = []; $specials = []; $colors = []; foreach ($history as $row) { $expects[] = (string)$row['expect']; $num = (int)$row['num7']; $specials[] = $num; $colors[] = $colorMap[$num] ?? ''; } return ['expects' => $expects, 'specials' => $specials, 'colors' => $colors]; } /** * 连号分析 */ public function getConsecutiveNumbers($periods = 30) { $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['pairs' => [], 'triples' => []]; $pairCount = []; $tripleCount = []; foreach ($history as $row) { $nums = []; for ($i = 1; $i <= 7; $i++) $nums[] = (int)$row['num' . $i]; sort($nums); // 找连号对 for ($i = 0; $i < count($nums) - 1; $i++) { if ($nums[$i + 1] - $nums[$i] === 1) { $pair = $nums[$i] . '-' . $nums[$i + 1]; $pairCount[$pair] = isset($pairCount[$pair]) ? $pairCount[$pair] + 1 : 1; } } // 找连号三连 for ($i = 0; $i < count($nums) - 2; $i++) { if ($nums[$i + 1] - $nums[$i] === 1 && $nums[$i + 2] - $nums[$i + 1] === 1) { $triple = $nums[$i] . '-' . $nums[$i + 1] . '-' . $nums[$i + 2]; $tripleCount[$triple] = isset($tripleCount[$triple]) ? $tripleCount[$triple] + 1 : 1; } } } arsort($pairCount); arsort($tripleCount); return ['pairs' => $pairCount, 'triples' => $tripleCount]; } /** * 尾数分析 */ public function getTailNumbers($periods = 30, $type = 'all') { $num_model = new Num(); $colorMap = $num_model->column('color', 'num'); $animalMap = $num_model->column('animal', 'num'); $history = $this->field('expect,num1,num2,num3,num4,num5,num6,num7')->order('openTime', 'desc')->limit($periods)->select(); if (empty($history)) return ['tails' => [], 'all' => []]; $fields = ($type === 'special') ? ['num7'] : ['num1','num2','num3','num4','num5','num6','num7']; $tailCount = array_fill(0, 10, 0); $all = []; foreach ($history as $row) { foreach ($fields as $f) { $num = (int)$row[$f]; $tail = $num % 10; $tailCount[$tail]++; } } $total = array_sum($tailCount); for ($t = 0; $t <= 9; $t++) { $all[] = ['tail' => $t, 'count' => $tailCount[$t], 'percent' => $total ? round($tailCount[$t]/$total*100, 1) : 0]; } usort($all, function ($a, $b) { return $b['count'] - $a['count']; }); return ['all' => $all]; } /** * 综合统计面板 */ public function getDashboardData($periods = 30, $type = 'all') { return [ 'hotcold' => $this->getHotColdNumbers($periods, $type), 'colorwave' => $this->getColorWaveAnalysis($periods, $type), 'zodiac' => $this->getZodiacAnalysis($periods, $type), 'oddeven' => $this->getOddEvenAnalysis($periods, $type), 'bigsmall' => $this->getBigSmallAnalysis($periods, $type), 'special' => $this->getSpecialTrend($periods), 'tailnumbers' => $this->getTailNumbers($periods, $type) ]; } }