order('sort', 'asc') ->field('id,label,value,sort') ->select() ->toArray(); $games = LotteryGame::where('is_show', 1) ->order('sort asc, id asc') ->field('id,code,template,name,icon,status,open_status,category,group_id,open_count,sort,has_video') ->select() ->toArray(); $gameMap = []; foreach ($games as $game) { $gameMap[$game['category']][] = $game; } $result = []; foreach ($categories as $cat) { $cat['games'] = $gameMap[$cat['value']] ?? []; $result[] = $cat; } return $this->data($result); } /** * 彩票分类列表 */ public function categoryList() { $domain = request()->domain() . '/'; $list = LotteryCategory::where('is_show', 1) ->order('sort', 'asc') ->field('id,name,code,region,icon,description,draw_rule,draw_time') ->select() ->toArray(); foreach ($list as &$item) { if (!empty($item['icon']) && !str_starts_with($item['icon'], 'http')) { $item['icon'] = $domain . $item['icon']; } } return $this->data($list); } /** * 彩票地区Tab列表 */ public function regionList() { $domain = request()->domain() . '/'; $list = LotteryRegion::where('is_show', 1) ->order('sort', 'asc') ->field('id,label,value,icon') ->select() ->toArray(); foreach ($list as &$item) { if (!empty($item['icon']) && !str_starts_with($item['icon'], 'http')) { $item['icon'] = $domain . $item['icon']; } } return $this->data($list); } /** * 开奖列表 */ public function drawList() { $categoryId = $this->request->get('category_id/d', 0); $pageNo = $this->request->get('page_no/d', 1); $pageSize = $this->request->get('page_size/d', 20); $query = LotteryDraw::where('is_show', 1); if ($categoryId > 0) { $query->where('category_id', $categoryId); } $countQuery = clone $query; $count = $countQuery->count(); $list = $query->order('draw_date desc, period desc') ->page($pageNo, $pageSize) ->select() ->toArray(); // 解析JSON字段 foreach ($list as &$item) { $item['numbers'] = is_string($item['numbers']) ? json_decode($item['numbers'], true) : $item['numbers']; $item['zodiac'] = is_string($item['zodiac']) ? json_decode($item['zodiac'], true) : $item['zodiac']; $item['elements'] = is_string($item['elements']) ? json_decode($item['elements'], true) : $item['elements']; $item['color'] = is_string($item['color']) ? json_decode($item['color'], true) : $item['color']; $item['prize_info'] = is_string($item['prize_info']) ? json_decode($item['prize_info'], true) : $item['prize_info']; } return $this->data([ 'lists' => $list, 'count' => $count, 'page_no' => $pageNo, 'page_size' => $pageSize, 'last_page' => ceil($count / $pageSize), ]); } /** * 开奖详情 */ public function drawDetail() { $id = $this->request->get('id/d', 0); if (empty($id)) { return $this->fail('参数缺失'); } $draw = LotteryDraw::where('id', $id)->where('is_show', 1)->findOrEmpty(); if ($draw->isEmpty()) { return $this->fail('记录不存在'); } $data = $draw->toArray(); $data['numbers'] = is_string($data['numbers']) ? json_decode($data['numbers'], true) : $data['numbers']; $data['zodiac'] = is_string($data['zodiac']) ? json_decode($data['zodiac'], true) : $data['zodiac']; $data['elements'] = is_string($data['elements']) ? json_decode($data['elements'], true) : $data['elements']; $data['color'] = is_string($data['color']) ? json_decode($data['color'], true) : $data['color']; $data['prize_info'] = is_string($data['prize_info']) ? json_decode($data['prize_info'], true) : $data['prize_info']; // 附带分类信息 $category = LotteryCategory::where('id', $data['category_id'])->findOrEmpty(); if (!$category->isEmpty()) { $data['category'] = $category->toArray(); } return $this->data($data); } /** * 各彩种最新一期开奖 */ public function latestDraw() { $categoryId = $this->request->get('category_id/d', 0); $query = LotteryDraw::where('is_show', 1)->where('status', 1); if ($categoryId > 0) { $query->where('category_id', $categoryId); } // 按分类分组取最新一期 if ($categoryId > 0) { $draw = $query->order('draw_date', 'desc')->findOrEmpty(); if ($draw->isEmpty()) { return $this->data(null); } $data = $draw->toArray(); $data['numbers'] = is_string($data['numbers']) ? json_decode($data['numbers'], true) : $data['numbers']; $data['color'] = is_string($data['color']) ? json_decode($data['color'], true) : $data['color']; return $this->data($data); } // 获取所有分类的最新开奖 $categories = LotteryCategory::where('is_show', 1)->order('sort', 'asc')->select()->toArray(); $result = []; foreach ($categories as $cat) { $draw = LotteryDraw::where('category_id', $cat['id']) ->where('is_show', 1) ->where('status', 1) ->order('draw_date', 'desc') ->findOrEmpty(); if (!$draw->isEmpty()) { $d = $draw->toArray(); $d['numbers'] = is_string($d['numbers']) ? json_decode($d['numbers'], true) : $d['numbers']; $d['color'] = is_string($d['color']) ? json_decode($d['color'], true) : $d['color']; $d['category_name'] = $cat['name']; $d['category_code'] = $cat['code']; $result[] = $d; } } return $this->data($result); } /** * 为号码数组附加生肖(zodiac)和波色(color)映射(六合彩专用) */ private function enrichNumbers(array $numbers, string $template = ''): array { if (empty($numbers)) return []; $isHk6 = stripos($template, 'hk6') !== false || stripos($template, 'lhc') !== false; if (!$isHk6) return $numbers; $year = (int) date('Y'); $mapping = LotteryNumberMapping::where('year', $year) ->whereIn('type', [LotteryNumberMapping::TYPE_ZODIAC, LotteryNumberMapping::TYPE_COLOR]) ->order('type asc, sort asc') ->select()->toArray(); $zodiacMap = []; $colorMap = []; foreach ($mapping as $row) { $nums = is_string($row['numbers']) ? json_decode($row['numbers'], true) : $row['numbers']; foreach ($nums as $n) { if ((int) $row['type'] === LotteryNumberMapping::TYPE_ZODIAC) { $zodiacMap[(int) $n] = $row['attr_name']; } else { $colorMap[(int) $n] = $row['attr_code']; } } } $enriched = []; foreach ($numbers as $num) { $n = (int) $num; $enriched[] = [ 'num' => $num, 'zodiac' => $zodiacMap[$n] ?? '', 'color' => $colorMap[$n] ?? 'red', ]; } return $enriched; } /** * 各彩种最新一期开奖(新表 lottery_draw_result) */ public function latestDrawResult() { $code = $this->request->get('code', ''); if ($code) { $draw = LotteryDrawResult::where('code', $code) ->order('draw_time desc') ->findOrEmpty(); if ($draw->isEmpty()) { return $this->data(null); } $data = $draw->toArray(); if (!empty($data['draw_code'])) { $data['status'] = 1; } $rawNumbers = $data['draw_code'] ? explode(',', $data['draw_code']) : []; $game = LotteryGame::where('code', $code)->field('name,template')->findOrEmpty(); $data['game_name'] = $game->isEmpty() ? $code : $game['name']; $template = $game->isEmpty() ? '' : $game['template']; $data['numbers'] = $this->enrichNumbers($rawNumbers, $template); $data['template'] = $template; return $this->data($data); } // 全部彩种,每个取最新一条 $games = LotteryGame::where('is_show', 1) ->order('sort asc, id asc') ->field('code,name,template,category') ->select() ->toArray(); $result = []; foreach ($games as $game) { $draw = LotteryDrawResult::where('code', $game['code']) ->order('draw_time desc') ->findOrEmpty(); if (!$draw->isEmpty()) { $d = $draw->toArray(); if (!empty($d['draw_code'])) { $d['status'] = 1; } $rawNumbers = $d['draw_code'] ? explode(',', $d['draw_code']) : []; $d['numbers'] = $this->enrichNumbers($rawNumbers, $game['template']); $d['game_name'] = $game['name']; $d['template'] = $game['template']; $d['category'] = $game['category']; $result[] = $d; } } return $this->data($result); } /** * 开奖历史列表(新表 lottery_draw_result) */ public function drawResultList() { $code = $this->request->get('code', ''); $pageNo = $this->request->get('page_no/d', 1); $pageSize = $this->request->get('page_size/d', 20); if (empty($code)) { return $this->fail('缺少彩种编码'); } $query = LotteryDrawResult::where('code', $code); $count = (clone $query)->count(); $list = $query->order('draw_time desc, issue desc') ->page($pageNo, $pageSize) ->select() ->toArray(); $game = LotteryGame::where('code', $code)->field('template')->findOrEmpty(); $template = $game->isEmpty() ? '' : $game['template']; foreach ($list as &$item) { if (!empty($item['draw_code'])) { $item['status'] = 1; } $rawNumbers = $item['draw_code'] ? explode(',', $item['draw_code']) : []; $item['numbers'] = $this->enrichNumbers($rawNumbers, $template); } return $this->data([ 'lists' => $list, 'count' => $count, 'page_no' => $pageNo, 'page_size' => $pageSize, 'last_page' => ceil($count / $pageSize), ]); } /** * AI预测分析 */ public function aiPredict() { $categoryId = $this->request->get('category_id/d', 0); $code = $this->request->get('code', ''); // 支持两种查找方式:category_id(旧表)或 code(新表) if (!empty($code)) { $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } $recentDraws = LotteryDrawResult::where('code', $code) ->where('draw_code', '<>', '') ->order('draw_time desc, issue desc') ->limit(30) ->field('issue,draw_time,draw_code') ->select() ->toArray(); foreach ($recentDraws as &$d) { $d['numbers'] = $d['draw_code'] ? explode(',', $d['draw_code']) : []; } $lotteryData = [ 'lottery_type' => $code, 'lottery_name' => $game['name'], 'draw_rule' => $game['template'] ?? '', 'recent_draws' => $recentDraws, ]; $result = AiService::analyzeLottery($lotteryData, $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data([ 'analysis' => $result['data'], 'from_cache' => $result['from_cache'] ?? false, 'category_name' => $game['name'], ]); } if (empty($categoryId)) { return $this->fail('参数缺失'); } $category = LotteryCategory::where('id', $categoryId)->findOrEmpty(); if ($category->isEmpty()) { return $this->fail('彩种不存在'); } // 获取最近30期已开奖数据 $recentDraws = LotteryDraw::where('category_id', $categoryId) ->where('status', 1) ->where('is_show', 1) ->order('draw_date desc, period desc') ->limit(30) ->field('period,draw_date,numbers,special_number,zodiac,color') ->select() ->toArray(); foreach ($recentDraws as &$d) { $d['numbers'] = is_string($d['numbers']) ? json_decode($d['numbers'], true) : $d['numbers']; $d['zodiac'] = is_string($d['zodiac']) ? json_decode($d['zodiac'], true) : $d['zodiac']; $d['color'] = is_string($d['color']) ? json_decode($d['color'], true) : $d['color']; } $lotteryData = [ 'lottery_type' => $category['code'], 'lottery_name' => $category['name'], 'draw_rule' => $category['draw_rule'], 'recent_draws' => $recentDraws, ]; $result = AiService::analyzeLottery($lotteryData, $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data([ 'analysis' => $result['data'], 'from_cache' => $result['from_cache'] ?? false, 'category_name' => $category['name'], ]); } /** * 获取彩种信息和历史开奖数据(纯数据库查询,不走AI) */ public function aiHistory() { $code = $this->request->get('code', ''); if (empty($code)) { return $this->fail('参数缺失'); } $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } $recentDraws = LotteryDrawResult::where('code', $code) ->where('draw_code', '<>', '') ->order('draw_time desc, issue desc') ->limit(30) ->field('issue,draw_time,draw_code') ->select() ->toArray(); foreach ($recentDraws as &$d) { $d['numbers'] = $d['draw_code'] ? explode(',', $d['draw_code']) : []; unset($d['draw_code']); } return $this->data([ 'game_name' => $game['name'], 'game_code' => $code, 'template' => $game['template'] ?? '', 'recent_draws' => $recentDraws, ]); } /** * AI分析 - 热冷号 */ public function aiHotCold() { $code = $this->request->get('code', ''); if (empty($code)) { return $this->fail('参数缺失'); } $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } $recentDraws = $this->getRecentDrawCodes($code, 30); $lotteryData = [ 'lottery_type' => $code, 'lottery_name' => $game['name'], 'draw_rule' => $game['template'] ?? '', 'recent_draws' => $recentDraws, ]; $result = AiService::analyzeLotteryModule($lotteryData, 'hot_cold', $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data($result['data']); } /** * AI分析 - 趋势+推荐组合 */ public function aiTrend() { $code = $this->request->get('code', ''); if (empty($code)) { return $this->fail('参数缺失'); } $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } $recentDraws = $this->getRecentDrawCodes($code, 30); $lotteryData = [ 'lottery_type' => $code, 'lottery_name' => $game['name'], 'draw_rule' => $game['template'] ?? '', 'recent_draws' => $recentDraws, ]; $result = AiService::analyzeLotteryModule($lotteryData, 'trend', $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data($result['data']); } /** * AI分析 - 统计数据 */ public function aiStats() { $code = $this->request->get('code', ''); if (empty($code)) { return $this->fail('参数缺失'); } $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } $recentDraws = $this->getRecentDrawCodes($code, 30); $lotteryData = [ 'lottery_type' => $code, 'lottery_name' => $game['name'], 'draw_rule' => $game['template'] ?? '', 'recent_draws' => $recentDraws, ]; $result = AiService::analyzeLotteryModule($lotteryData, 'stats', $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data($result['data']); } /** * AI分析 - 波色+生肖(六合彩专属) */ public function aiColorZodiac() { $code = $this->request->get('code', ''); if (empty($code)) { return $this->fail('参数缺失'); } $game = LotteryGame::where('code', $code)->findOrEmpty(); if ($game->isEmpty()) { return $this->fail('彩种不存在'); } if (($game['template'] ?? '') !== 'HK6') { return $this->fail('该彩种不支持波色生肖分析'); } $recentDraws = $this->getRecentDrawCodes($code, 30); $year = (int) date('Y'); $colorMap = LotteryNumberMapping::getMapByType($year, LotteryNumberMapping::TYPE_COLOR); $zodiacMap = LotteryNumberMapping::getMapByType($year, LotteryNumberMapping::TYPE_ZODIAC); $lotteryData = [ 'lottery_type' => $code, 'lottery_name' => $game['name'], 'draw_rule' => $game['template'] ?? '', 'recent_draws' => $recentDraws, 'number_mapping' => [ 'color' => $colorMap, 'zodiac' => $zodiacMap, ], ]; $result = AiService::analyzeLotteryModule($lotteryData, 'color_zodiac', $this->userId); if (!$result['success']) { return $this->fail($result['error'] ?? 'AI分析失败'); } return $this->data($result['data']); } /** * 获取最近N期开奖号码(格式化为号码数组) */ private function getRecentDrawCodes(string $code, int $limit = 30): array { $rows = LotteryDrawResult::where('code', $code) ->where('draw_code', '<>', '') ->order('draw_time desc, issue desc') ->limit($limit) ->field('issue,draw_time,draw_code') ->select() ->toArray(); foreach ($rows as &$row) { $row['numbers'] = $row['draw_code'] ? array_map('intval', explode(',', $row['draw_code'])) : []; unset($row['draw_code']); } return $rows; } /** * 获取某期的AI分析历史记录 */ public function aiAnalysisHistory() { $code = $this->request->get('code', ''); $issue = $this->request->get('issue', ''); if (empty($code) || empty($issue)) { return $this->fail('参数缺失'); } $rows = LotteryAiAnalysis::where('code', $code) ->where('issue', $issue) ->field('module,result') ->select() ->toArray(); $result = []; foreach ($rows as $row) { $result[$row['module']] = json_decode($row['result'], true); } return $this->data($result); } }