493 lines
16 KiB
PHP
493 lines
16 KiB
PHP
<?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\AiAnalysisRequestService;
|
||
|
||
class LotteryController extends BaseApiController
|
||
{
|
||
public array $notNeedLogin = ['categoryList', 'regionList', 'drawList', 'drawDetail', 'latestDraw', 'gameList', 'latestDrawResult', 'drawResultList', 'aiHistory', '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()
|
||
{
|
||
return $this->respondAnalysis(AiAnalysisRequestService::analyzeLotteryOverview($this->request->get(), $this->userId));
|
||
}
|
||
|
||
/**
|
||
* 获取彩种信息和历史开奖数据(纯数据库查询,不走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()
|
||
{
|
||
return $this->respondLotteryModule('hot_cold');
|
||
}
|
||
|
||
/**
|
||
* AI分析 - 趋势+推荐组合
|
||
*/
|
||
public function aiTrend()
|
||
{
|
||
return $this->respondLotteryModule('trend');
|
||
}
|
||
|
||
/**
|
||
* AI分析 - 统计数据
|
||
*/
|
||
public function aiStats()
|
||
{
|
||
return $this->respondLotteryModule('stats');
|
||
}
|
||
|
||
/**
|
||
* AI分析 - 波色+生肖(六合彩专属)
|
||
*/
|
||
public function aiColorZodiac()
|
||
{
|
||
return $this->respondLotteryModule('color_zodiac');
|
||
}
|
||
|
||
/**
|
||
* 获取最近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);
|
||
}
|
||
|
||
private function respondLotteryModule(string $module)
|
||
{
|
||
$result = AiAnalysisRequestService::analyzeLotteryModule(
|
||
$this->request->get('code', ''),
|
||
$module,
|
||
$this->userId
|
||
);
|
||
if (empty($result['success'])) {
|
||
return $this->fail($result['error'] ?? 'AI分析失败', [
|
||
'remain_count' => $result['remain_count'] ?? null,
|
||
]);
|
||
}
|
||
|
||
$data = is_array($result['data'] ?? null) ? $result['data'] : [];
|
||
$data['remain_count'] = $result['remain_count'] ?? 0;
|
||
$data['unlocked'] = $result['unlocked'] ?? true;
|
||
$data['charged'] = $result['charged'] ?? false;
|
||
return $this->data($data);
|
||
}
|
||
|
||
private function respondAnalysis(array $result)
|
||
{
|
||
if (empty($result['success'])) {
|
||
return $this->fail($result['error'] ?? 'AI分析失败', [
|
||
'remain_count' => $result['remain_count'] ?? null,
|
||
]);
|
||
}
|
||
|
||
unset($result['success'], $result['error'], $result['code']);
|
||
return $this->data($result);
|
||
}
|
||
}
|