no message

This commit is contained in:
hajimi
2026-06-11 12:15:29 +08:00
parent 10ebe39c30
commit 96efa1d905
5859 changed files with 815501 additions and 5 deletions
@@ -0,0 +1,641 @@
<?php
namespace app\api\controller;
use app\common\model\lottery\LotteryCategory;
use app\common\model\lottery\LotteryDraw;
use app\common\model\lottery\LotteryGame;
use app\common\model\lottery\LotteryGameCategory;
use app\common\model\lottery\LotteryDrawResult;
use app\common\model\lottery\LotteryAiAnalysis;
use app\common\model\lottery\LotteryNumberMapping;
use app\common\model\lottery\LotteryRegion;
use app\common\service\ai\AiService;
class LotteryController extends BaseApiController
{
public array $notNeedLogin = ['categoryList', 'regionList', 'drawList', 'drawDetail', 'latestDraw', 'aiPredict', 'gameList', 'latestDrawResult', 'drawResultList', 'aiHistory', 'aiHotCold', 'aiTrend', 'aiStats', 'aiColorZodiac', 'aiAnalysisHistory'];
/**
* 彩种游戏列表(一级分类 + 二级彩种)
*/
public function gameList()
{
$categories = LotteryGameCategory::where('is_show', 1)
->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);
}
}