feat(ai): 将小班长升级为完全独立的实体用户并支持随机金币发放及持续在线刷级,设定为女兵人设并使用自定义头像
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:AI小班长专属极轻量心跳模拟器
|
||||
*
|
||||
* 专门用于让无法通过浏览器发送真实心跳的 AI实体用户
|
||||
* 也能够完美触原有的法发经验/金币逻辑以及触发随机事件(Autoact)。
|
||||
* 每分钟由 Laravel Scheduler 调用。
|
||||
*
|
||||
* @author ChatRoom Laravel
|
||||
*
|
||||
* @version 1.0.0
|
||||
*/
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Enums\CurrencySource;
|
||||
use App\Events\MessageSent;
|
||||
use App\Jobs\SaveMessageJob;
|
||||
use App\Models\Autoact;
|
||||
use App\Models\Sysparam;
|
||||
use App\Models\User;
|
||||
use App\Services\ChatStateService;
|
||||
use App\Services\UserCurrencyService;
|
||||
use App\Services\VipService;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class AiHeartbeatCommand extends Command
|
||||
{
|
||||
/**
|
||||
* Artisan 指令名称
|
||||
*/
|
||||
protected $signature = 'chatroom:ai-heartbeat';
|
||||
|
||||
/**
|
||||
* 指令描述
|
||||
*/
|
||||
protected $description = '模拟 AI 小班长客户端心跳,触发经验金币与随机事件';
|
||||
|
||||
public function __construct(
|
||||
private readonly ChatStateService $chatState,
|
||||
private readonly VipService $vipService,
|
||||
private readonly UserCurrencyService $currencyService,
|
||||
) {
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function handle(): int
|
||||
{
|
||||
// 1. 检查总开关
|
||||
if (Sysparam::getValue('chatbot_enabled', '0') !== '1') {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// 2. 获取 AI 实体
|
||||
$user = User::where('username', 'AI小班长')->first();
|
||||
if (! $user) {
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
// 3. 常规心跳经验与金币发放
|
||||
// (模拟前端每30-60秒发一次心跳的过程,此处每分钟跑一次,发放单人心跳奖励)
|
||||
$expGain = $this->parseRewardValue(Sysparam::getValue('exp_per_heartbeat', '1'));
|
||||
if ($expGain > 0) {
|
||||
$expMultiplier = $this->vipService->getExpMultiplier($user);
|
||||
$actualExpGain = (int) round($expGain * $expMultiplier);
|
||||
$user->exp_num += $actualExpGain;
|
||||
}
|
||||
|
||||
$jjbGain = $this->parseRewardValue(Sysparam::getValue('jjb_per_heartbeat', '0'));
|
||||
if ($jjbGain > 0) {
|
||||
$jjbMultiplier = $this->vipService->getJjbMultiplier($user);
|
||||
$actualJjbGain = (int) round($jjbGain * $jjbMultiplier);
|
||||
$user->jjb = ($user->jjb ?? 0) + $actualJjbGain;
|
||||
}
|
||||
|
||||
$user->save();
|
||||
$user->refresh();
|
||||
|
||||
// 4. 重算等级(基础心跳升级)
|
||||
$superLevel = (int) Sysparam::getValue('superlevel', '100');
|
||||
$leveledUp = $this->calculateNewLevel($user, $superLevel);
|
||||
|
||||
// 5. 随机事件触发
|
||||
$eventChance = (int) Sysparam::getValue('auto_event_chance', '10');
|
||||
if ($eventChance > 0 && rand(1, 100) <= $eventChance) {
|
||||
$autoEvent = Autoact::randomEvent();
|
||||
if ($autoEvent) {
|
||||
// 执行随机事件的金钱经验惩奖
|
||||
if ($autoEvent->exp_change !== 0) {
|
||||
$this->currencyService->change(
|
||||
$user, 'exp', $autoEvent->exp_change, CurrencySource::AUTO_EVENT, "随机事件:{$autoEvent->text_body}", 1
|
||||
);
|
||||
}
|
||||
if ($autoEvent->jjb_change !== 0) {
|
||||
$this->currencyService->change(
|
||||
$user, 'gold', $autoEvent->jjb_change, CurrencySource::AUTO_EVENT, "随机事件:{$autoEvent->text_body}", 1
|
||||
);
|
||||
}
|
||||
|
||||
$user->refresh();
|
||||
|
||||
// 重新计算等级
|
||||
if ($this->calculateNewLevel($user, $superLevel)) {
|
||||
$leveledUp = true;
|
||||
}
|
||||
|
||||
// 广播随机事件
|
||||
$this->broadcastSystemMessage(
|
||||
'星海小博士',
|
||||
$autoEvent->renderText($user->username),
|
||||
match ($autoEvent->event_type) {
|
||||
'good' => '#16a34a',
|
||||
'bad' => '#dc2626',
|
||||
default => '#7c3aed',
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 6. 如果由于心跳或事件导致了升级,广播升级消息
|
||||
if ($leveledUp) {
|
||||
$this->broadcastSystemMessage(
|
||||
'系统传音',
|
||||
"🌟 天道酬勤!恭喜侠客【{$user->username}】挂机苦修,境界突破至 LV.{$user->user_level}!",
|
||||
'#d97706',
|
||||
'大声宣告'
|
||||
);
|
||||
}
|
||||
|
||||
return Command::SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算并更新用户等级
|
||||
*/
|
||||
private function calculateNewLevel(User $user, int $superLevel): bool
|
||||
{
|
||||
$oldLevel = $user->user_level;
|
||||
|
||||
if ($oldLevel >= $superLevel) {
|
||||
return false; // 管理员不自动升降级
|
||||
}
|
||||
|
||||
$newLevel = Sysparam::calculateLevel($user->exp_num);
|
||||
if ($newLevel !== $oldLevel && $newLevel < $superLevel) {
|
||||
$user->user_level = $newLevel;
|
||||
$user->save();
|
||||
|
||||
return $newLevel > $oldLevel;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析配置的奖励范围,如 "1" 或 "1-5"
|
||||
*/
|
||||
private function parseRewardValue(string $raw): int
|
||||
{
|
||||
$raw = trim($raw);
|
||||
if (str_contains($raw, '-')) {
|
||||
[$min, $max] = explode('-', $raw, 2);
|
||||
|
||||
return rand((int) $min, (int) $max);
|
||||
}
|
||||
|
||||
return (int) $raw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 往所有活跃房间发送系统广播消息
|
||||
*/
|
||||
private function broadcastSystemMessage(string $fromUser, string $content, string $color, string $action = ''): void
|
||||
{
|
||||
$activeRoomIds = $this->chatState->getAllActiveRoomIds();
|
||||
if (empty($activeRoomIds)) {
|
||||
$activeRoomIds = [1];
|
||||
}
|
||||
|
||||
foreach ($activeRoomIds as $roomId) {
|
||||
$sysMsg = [
|
||||
'id' => $this->chatState->nextMessageId($roomId),
|
||||
'room_id' => $roomId,
|
||||
'from_user' => $fromUser,
|
||||
'to_user' => '大家',
|
||||
'content' => $content,
|
||||
'is_secret' => false,
|
||||
'font_color' => $color,
|
||||
'action' => $action,
|
||||
'sent_at' => now()->toDateTimeString(),
|
||||
];
|
||||
|
||||
$this->chatState->pushMessage($roomId, $sysMsg);
|
||||
broadcast(new MessageSent($roomId, $sysMsg));
|
||||
SaveMessageJob::dispatch($sysMsg);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user