Files
chatroom/app/Models/LotteryIssue.php
lkddi 27371fe321 新增:双色球彩票系统后端基础(阶段一)
📦 数据库
  - lottery_issues(期次表)
  - lottery_tickets(购票记录表)
  - lottery_pool_logs(奖池流水表,透明展示)

🔩 核心组件
  - LotteryIssue / LotteryTicket / LotteryPoolLog 完整 Model
  - LotteryService:购票/机选/开奖/奖池派发/滚存/超级期预热/公屏广播
  - LotteryController:current/buy/quickPick/history/my 五个接口
  - DrawLotteryJob(每日定时开奖)/ OpenLotteryIssueJob(初始化首期)

💰 货币日志
  - CurrencySource 新增 LOTTERY_BUY / LOTTERY_WIN
  - 所有金币变动均通过 UserCurrencyService::change() 记录流水

🗓️ 调度器
  - 每分钟检查停售/开奖时机
  - 每日 18:00 超级期预热广播

🔧 配置
  - GameConfigSeeder 追加 lottery 默认配置(默认关闭)
  - /games/enabled 接口追加 lottery 开关状态
  - 新增 /lottery/* 路由组(auth 保护)
2026-03-04 15:38:02 +08:00

165 lines
4.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* 文件功能:双色球期次 Model
*
* 管理每期彩票的生命周期open购票中→ closed停售→ settled已开奖
* 提供静态查询方法、奖级判定辅助及号码排序工具。
*
* @author ChatRoom Laravel
*
* @version 1.0.0
*/
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class LotteryIssue extends Model
{
protected $fillable = [
'issue_no',
'status',
'red1', 'red2', 'red3', 'blue',
'pool_amount',
'carry_amount',
'is_super_issue',
'no_winner_streak',
'total_tickets',
'payout_amount',
'sell_closes_at',
'draw_at',
];
/**
* 字段类型转换。
*/
protected function casts(): array
{
return [
'is_super_issue' => 'boolean',
'pool_amount' => 'integer',
'carry_amount' => 'integer',
'payout_amount' => 'integer',
'total_tickets' => 'integer',
'sell_closes_at' => 'datetime',
'draw_at' => 'datetime',
];
}
// ─── 关联 ──────────────────────────────────────────────────────────
/**
* 本期所有购票记录。
*/
public function tickets(): HasMany
{
return $this->hasMany(LotteryTicket::class, 'issue_id');
}
/**
* 本期奖池流水。
*/
public function poolLogs(): HasMany
{
return $this->hasMany(LotteryPoolLog::class, 'issue_id');
}
// ─── 静态查询 ──────────────────────────────────────────────────────
/**
* 获取当前正在购票的期次status=open
*/
public static function currentIssue(): ?static
{
return static::query()->where('status', 'open')->latest()->first();
}
/**
* 获取最新一期(不论状态)。
*/
public static function latestIssue(): ?static
{
return static::query()->latest()->first();
}
/**
* 生成下一期的期号(格式:年份 + 三位序号,如 2026001
*/
public static function nextIssueNo(): string
{
$year = now()->year;
$last = static::query()
->whereYear('created_at', $year)
->latest()
->first();
$seq = $last ? ((int) substr($last->issue_no, -3)) + 1 : 1;
return $year.str_pad($seq, 3, '0', STR_PAD_LEFT);
}
// ─── 业务方法 ──────────────────────────────────────────────────────
/**
* 判断当前期次是否仍在售票中。
*/
public function isOpen(): bool
{
return $this->status === 'open' && now()->lt($this->sell_closes_at);
}
/**
* 距离开奖剩余秒数。
*/
public function secondsUntilDraw(): int
{
if (! $this->draw_at) {
return 0;
}
return max(0, (int) now()->diffInSeconds($this->draw_at, false));
}
/**
* 判断给定号码是否与开奖号码匹配并返回奖级0=未中)。
*
* @param int $r1 用户红球1已排序
* @param int $r2 用户红球2
* @param int $r3 用户红球3
* @param int $b 用户蓝球
*/
public function calcPrizeLevel(int $r1, int $r2, int $r3, int $b): int
{
$userReds = [$r1, $r2, $r3];
$drawReds = [$this->red1, $this->red2, $this->red3];
$redMatches = count(array_intersect($userReds, $drawReds));
$blueMatch = ($b === $this->blue);
return match (true) {
$redMatches === 3 && $blueMatch => 1, // 一等奖
$redMatches === 3 && ! $blueMatch => 2, // 二等奖
$redMatches === 2 && $blueMatch => 3, // 三等奖
$redMatches === 2 && ! $blueMatch => 4, // 四等奖
$redMatches === 1 && $blueMatch => 5, // 五等奖
default => 0, // 未中奖
};
}
/**
* 返回奖级的中文标签。
*/
public static function prizeLevelLabel(int $level): string
{
return match ($level) {
1 => '一等奖',
2 => '二等奖',
3 => '三等奖',
4 => '四等奖',
5 => '五等奖',
default => '未中奖',
};
}
}