功能:VIP 赞助会员系统
- 新建 vip_levels 表(名称、图标、颜色、经验/金币倍率、专属进入/离开模板) - 默认4个等级种子:白银🥈(×1.5)、黄金🥇(×2.0)、钻石💎(×3.0)、至尊👑(×5.0) - 后台 VIP 等级 CRUD 管理(新增/编辑/删除,配置模板和倍率) - 后台用户编辑弹窗支持设置 VIP 等级和到期时间 - ChatController 心跳经验按 VIP 倍率加成 - FishingController 正向奖励按 VIP 倍率加成(负面惩罚不变) - 在线名单显示 VIP 图标和管理员🛡️标识 - VIP 用户进入/离开使用专属颜色和标题 - 后台侧栏新增「👑 VIP 会员等级」入口
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:AI 厂商配置模型
|
||||
*
|
||||
* 对应 ai_provider_configs 表,管理多个 AI 厂商的 API 配置。
|
||||
* 支持多厂商切换、默认配置、自动故障转移等功能。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Crypt;
|
||||
|
||||
class AiProviderConfig extends Model
|
||||
{
|
||||
/**
|
||||
* 关联的数据库表名
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'ai_provider_configs';
|
||||
|
||||
/**
|
||||
* 可批量赋值的属性
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'provider',
|
||||
'name',
|
||||
'api_key',
|
||||
'api_endpoint',
|
||||
'model',
|
||||
'temperature',
|
||||
'max_tokens',
|
||||
'is_enabled',
|
||||
'is_default',
|
||||
'sort_order',
|
||||
];
|
||||
|
||||
/**
|
||||
* 属性类型转换
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'temperature' => 'float',
|
||||
'max_tokens' => 'integer',
|
||||
'is_enabled' => 'boolean',
|
||||
'is_default' => 'boolean',
|
||||
'sort_order' => 'integer',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取解密后的 API Key
|
||||
*
|
||||
* @return string|null 解密后的 API Key
|
||||
*/
|
||||
public function getDecryptedApiKey(): ?string
|
||||
{
|
||||
if (empty($this->api_key)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return Crypt::decryptString($this->api_key);
|
||||
} catch (\Exception) {
|
||||
// 如果解密失败(可能是明文存储的旧数据),直接返回原值
|
||||
return $this->api_key;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置加密存储的 API Key
|
||||
*
|
||||
* @param string $value 原始 API Key
|
||||
*/
|
||||
public function setApiKeyEncrypted(string $value): void
|
||||
{
|
||||
$this->api_key = Crypt::encryptString($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前默认的 AI 配置
|
||||
*
|
||||
* 返回 is_default=1 且 is_enabled=1 的第一条配置。
|
||||
* 如果没有设置默认,则返回第一条已启用的配置。
|
||||
*/
|
||||
public static function getDefault(): ?self
|
||||
{
|
||||
// 优先获取标记为默认且已启用的
|
||||
$default = static::where('is_default', true)
|
||||
->where('is_enabled', true)
|
||||
->first();
|
||||
|
||||
if ($default) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
// 退而求其次,取第一个已启用的
|
||||
return static::where('is_enabled', true)
|
||||
->orderBy('sort_order')
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有已启用的 AI 厂商配置(按 sort_order 排序)
|
||||
*
|
||||
* 用于故障转移时依次尝试备用厂商。
|
||||
*/
|
||||
public static function getEnabledProviders(): \Illuminate\Database\Eloquent\Collection
|
||||
{
|
||||
return static::where('is_enabled', true)
|
||||
->orderBy('is_default', 'desc') // 默认的排最前面
|
||||
->orderBy('sort_order')
|
||||
->get();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:AI 使用日志模型
|
||||
*
|
||||
* 对应 ai_usage_logs 表,记录每次 AI 接口调用的详细信息,
|
||||
* 包括 token 消耗、响应时间、成功/失败状态等。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class AiUsageLog extends Model
|
||||
{
|
||||
/**
|
||||
* 关联的数据库表名
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $table = 'ai_usage_logs';
|
||||
|
||||
/**
|
||||
* 可批量赋值的属性
|
||||
*
|
||||
* @var array<int, string>
|
||||
*/
|
||||
protected $fillable = [
|
||||
'company_id',
|
||||
'user_id',
|
||||
'provider',
|
||||
'model',
|
||||
'action',
|
||||
'prompt_tokens',
|
||||
'completion_tokens',
|
||||
'total_tokens',
|
||||
'cost',
|
||||
'response_time_ms',
|
||||
'success',
|
||||
'error_message',
|
||||
];
|
||||
|
||||
/**
|
||||
* 属性类型转换
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'prompt_tokens' => 'integer',
|
||||
'completion_tokens' => 'integer',
|
||||
'total_tokens' => 'integer',
|
||||
'cost' => 'float',
|
||||
'response_time_ms' => 'integer',
|
||||
'success' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联用户
|
||||
*/
|
||||
public function user(): \Illuminate\Database\Eloquent\Relations\BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
@@ -36,6 +37,8 @@ class User extends Authenticatable
|
||||
'first_ip',
|
||||
'last_ip',
|
||||
'usersf',
|
||||
'vip_level_id',
|
||||
'hy_time',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -85,4 +88,53 @@ class User extends Authenticatable
|
||||
get: fn () => $this->usersf ?: '1.GIF',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联:用户所属的 VIP 会员等级
|
||||
*/
|
||||
public function vipLevel(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(VipLevel::class, 'vip_level_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断用户是否为有效 VIP(有等级且未过期)
|
||||
*/
|
||||
public function isVip(): bool
|
||||
{
|
||||
if (! $this->vip_level_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// hy_time 为 null 表示永久会员
|
||||
if (! $this->hy_time) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $this->hy_time->isFuture();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 VIP 会员名称(无效则返回空字符串)
|
||||
*/
|
||||
public function vipName(): string
|
||||
{
|
||||
if (! $this->isVip()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->vipLevel?->name ?? '';
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 VIP 会员图标(无效则返回空字符串)
|
||||
*/
|
||||
public function vipIcon(): string
|
||||
{
|
||||
if (! $this->isVip()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->vipLevel?->icon ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:VIP 会员等级模型
|
||||
* 存储会员名称、图标、颜色、倍率、专属进入/离开模板
|
||||
* 后台可完整 CRUD 管理
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class VipLevel extends Model
|
||||
{
|
||||
/** @var string 表名 */
|
||||
protected $table = 'vip_levels';
|
||||
|
||||
/** @var array 可批量赋值字段 */
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'icon',
|
||||
'color',
|
||||
'exp_multiplier',
|
||||
'jjb_multiplier',
|
||||
'join_templates',
|
||||
'leave_templates',
|
||||
'sort_order',
|
||||
'price',
|
||||
'duration_days',
|
||||
];
|
||||
|
||||
/** @var array 类型转换 */
|
||||
protected $casts = [
|
||||
'exp_multiplier' => 'float',
|
||||
'jjb_multiplier' => 'float',
|
||||
'sort_order' => 'integer',
|
||||
'price' => 'integer',
|
||||
'duration_days' => 'integer',
|
||||
];
|
||||
|
||||
/**
|
||||
* 关联:该等级下的所有用户
|
||||
*/
|
||||
public function users(): HasMany
|
||||
{
|
||||
return $this->hasMany(User::class, 'vip_level_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取进入聊天室的专属欢迎语模板数组
|
||||
*/
|
||||
public function getJoinTemplatesArrayAttribute(): array
|
||||
{
|
||||
if (empty($this->join_templates)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($this->join_templates, true);
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取离开聊天室的专属提示语模板数组
|
||||
*/
|
||||
public function getLeaveTemplatesArrayAttribute(): array
|
||||
{
|
||||
if (empty($this->leave_templates)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$decoded = json_decode($this->leave_templates, true);
|
||||
|
||||
return is_array($decoded) ? $decoded : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* 从模板数组中随机选一条,替换 {username} 占位符
|
||||
*
|
||||
* @param array $templates 模板数组
|
||||
* @param string $username 用户名
|
||||
*/
|
||||
public static function renderTemplate(array $templates, string $username): ?string
|
||||
{
|
||||
if (empty($templates)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$template = $templates[array_rand($templates)];
|
||||
|
||||
return str_replace('{username}', $username, $template);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user