mirror of
https://github.com/lkddi/Xboard.git
synced 2026-04-11 09:10:52 +08:00
260 lines
6.3 KiB
PHP
260 lines
6.3 KiB
PHP
<?php
|
||
|
||
namespace App\Models;
|
||
|
||
use Illuminate\Database\Eloquent\Model;
|
||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||
|
||
/**
|
||
* App\Models\GiftCardCode
|
||
*
|
||
* @property int $id
|
||
* @property int $template_id 模板ID
|
||
* @property GiftCardTemplate $template 关联模板
|
||
* @property string $code 兑换码
|
||
* @property string|null $batch_id 批次ID
|
||
* @property int $status 状态
|
||
* @property int|null $user_id 使用用户ID
|
||
* @property int|null $used_at 使用时间
|
||
* @property int|null $expires_at 过期时间
|
||
* @property array|null $actual_rewards 实际奖励
|
||
* @property int $usage_count 使用次数
|
||
* @property int $max_usage 最大使用次数
|
||
* @property array|null $metadata 额外数据
|
||
* @property int $created_at
|
||
* @property int $updated_at
|
||
*/
|
||
class GiftCardCode extends Model
|
||
{
|
||
protected $table = 'v2_gift_card_code';
|
||
protected $dateFormat = 'U';
|
||
|
||
// 状态常量
|
||
const STATUS_UNUSED = 0; // 未使用
|
||
const STATUS_USED = 1; // 已使用
|
||
const STATUS_EXPIRED = 2; // 已过期
|
||
const STATUS_DISABLED = 3; // 已禁用
|
||
|
||
protected $fillable = [
|
||
'template_id',
|
||
'code',
|
||
'batch_id',
|
||
'status',
|
||
'user_id',
|
||
'used_at',
|
||
'expires_at',
|
||
'actual_rewards',
|
||
'usage_count',
|
||
'max_usage',
|
||
'metadata'
|
||
];
|
||
|
||
protected $casts = [
|
||
'created_at' => 'timestamp',
|
||
'updated_at' => 'timestamp',
|
||
'used_at' => 'timestamp',
|
||
'expires_at' => 'timestamp',
|
||
'actual_rewards' => 'array',
|
||
'metadata' => 'array'
|
||
];
|
||
|
||
/**
|
||
* 获取状态映射
|
||
*/
|
||
public static function getStatusMap(): array
|
||
{
|
||
return [
|
||
self::STATUS_UNUSED => '未使用',
|
||
self::STATUS_USED => '已使用',
|
||
self::STATUS_EXPIRED => '已过期',
|
||
self::STATUS_DISABLED => '已禁用',
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 获取状态名称
|
||
*/
|
||
public function getStatusNameAttribute(): string
|
||
{
|
||
return self::getStatusMap()[$this->status] ?? '未知状态';
|
||
}
|
||
|
||
/**
|
||
* 关联礼品卡模板
|
||
*/
|
||
public function template(): BelongsTo
|
||
{
|
||
return $this->belongsTo(GiftCardTemplate::class, 'template_id');
|
||
}
|
||
|
||
/**
|
||
* 关联使用用户
|
||
*/
|
||
public function user(): BelongsTo
|
||
{
|
||
return $this->belongsTo(User::class, 'user_id');
|
||
}
|
||
|
||
/**
|
||
* 关联使用记录
|
||
*/
|
||
public function usages(): HasMany
|
||
{
|
||
return $this->hasMany(GiftCardUsage::class, 'code_id');
|
||
}
|
||
|
||
/**
|
||
* 检查是否可用
|
||
*/
|
||
public function isAvailable(): bool
|
||
{
|
||
// 检查状态
|
||
if (in_array($this->status, [self::STATUS_EXPIRED, self::STATUS_DISABLED])) {
|
||
return false;
|
||
}
|
||
|
||
// 检查是否过期
|
||
if ($this->expires_at && $this->expires_at < time()) {
|
||
return false;
|
||
}
|
||
|
||
// 检查使用次数
|
||
if ($this->usage_count >= $this->max_usage) {
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 检查是否已过期
|
||
*/
|
||
public function isExpired(): bool
|
||
{
|
||
return $this->expires_at && $this->expires_at < time();
|
||
}
|
||
|
||
/**
|
||
* 标记为已使用
|
||
*/
|
||
public function markAsUsed(User $user): bool
|
||
{
|
||
$this->status = self::STATUS_USED;
|
||
$this->user_id = $user->id;
|
||
$this->used_at = time();
|
||
$this->usage_count += 1;
|
||
|
||
return $this->save();
|
||
}
|
||
|
||
/**
|
||
* 标记为已过期
|
||
*/
|
||
public function markAsExpired(): bool
|
||
{
|
||
$this->status = self::STATUS_EXPIRED;
|
||
return $this->save();
|
||
}
|
||
|
||
/**
|
||
* 标记为已禁用
|
||
*/
|
||
public function markAsDisabled(): bool
|
||
{
|
||
$this->status = self::STATUS_DISABLED;
|
||
return $this->save();
|
||
}
|
||
|
||
/**
|
||
* 生成兑换码
|
||
*/
|
||
public static function generateCode(string $prefix = 'GC'): string
|
||
{
|
||
do {
|
||
$safePrefix = (string) $prefix;
|
||
$code = $safePrefix . strtoupper(substr(md5(uniqid($safePrefix . mt_rand(), true)), 0, 12));
|
||
} while (self::where('code', $code)->exists());
|
||
|
||
return $code;
|
||
}
|
||
|
||
/**
|
||
* 批量生成兑换码
|
||
*/
|
||
public static function batchGenerate(int $templateId, int $count, array $options = []): string
|
||
{
|
||
$batchId = uniqid('batch_');
|
||
$prefix = $options['prefix'] ?? 'GC';
|
||
$expiresAt = $options['expires_at'] ?? null;
|
||
$maxUsage = $options['max_usage'] ?? 1;
|
||
|
||
$codes = [];
|
||
for ($i = 0; $i < $count; $i++) {
|
||
$codes[] = [
|
||
'template_id' => $templateId,
|
||
'code' => self::generateCode($prefix),
|
||
'batch_id' => $batchId,
|
||
'status' => self::STATUS_UNUSED,
|
||
'expires_at' => $expiresAt,
|
||
'max_usage' => $maxUsage,
|
||
'created_at' => time(),
|
||
'updated_at' => time(),
|
||
];
|
||
}
|
||
|
||
self::insert($codes);
|
||
|
||
return $batchId;
|
||
}
|
||
|
||
/**
|
||
* 设置实际奖励(用于盲盒等)
|
||
*/
|
||
public function setActualRewards(array $rewards): bool
|
||
{
|
||
$this->actual_rewards = $rewards;
|
||
return $this->save();
|
||
}
|
||
|
||
/**
|
||
* 获取实际奖励
|
||
*/
|
||
public function getActualRewards(): array
|
||
{
|
||
return $this->actual_rewards ?? $this->template->rewards ?? [];
|
||
}
|
||
|
||
/**
|
||
* 检查兑换码格式
|
||
*/
|
||
public static function validateCodeFormat(string $code): bool
|
||
{
|
||
// 基本格式验证:字母数字组合,长度8-32
|
||
return preg_match('/^[A-Z0-9]{8,32}$/', $code);
|
||
}
|
||
|
||
/**
|
||
* 根据批次ID获取兑换码
|
||
*/
|
||
public static function getByBatchId(string $batchId)
|
||
{
|
||
return self::where('batch_id', $batchId)->get();
|
||
}
|
||
|
||
/**
|
||
* 清理过期兑换码
|
||
*/
|
||
public static function cleanupExpired(): int
|
||
{
|
||
$count = self::where('status', self::STATUS_UNUSED)
|
||
->where('expires_at', '<', time())
|
||
->count();
|
||
|
||
self::where('status', self::STATUS_UNUSED)
|
||
->where('expires_at', '<', time())
|
||
->update(['status' => self::STATUS_EXPIRED]);
|
||
|
||
return $count;
|
||
}
|
||
} |