Files
Xboard/app/Models/GiftCardCode.php
2025-07-21 09:28:00 +08:00

260 lines
6.3 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
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;
}
}