feat(ai): 将小班长升级为完全独立的实体用户并支持随机金币发放及持续在线刷级,设定为女兵人设并使用自定义头像

This commit is contained in:
2026-03-26 11:15:11 +08:00
parent c13bb5f35c
commit 4d60893dbe
16 changed files with 523 additions and 101 deletions

View File

@@ -46,8 +46,40 @@ class AiProviderController extends Controller
{
$providers = AiProviderConfig::orderBy('sort_order')->get();
$chatbotEnabled = Sysparam::getValue('chatbot_enabled', '0') === '1';
$chatbotMaxGold = Sysparam::getValue('chatbot_max_gold', '5000');
$chatbotMaxDailyRewards = Sysparam::getValue('chatbot_max_daily_rewards', '1');
return view('admin.ai-providers.index', compact('providers', 'chatbotEnabled'));
return view('admin.ai-providers.index', compact('providers', 'chatbotEnabled', 'chatbotMaxGold', 'chatbotMaxDailyRewards'));
}
/**
* 保存全局设置
*/
public function updateSettings(Request $request): RedirectResponse
{
$data = $request->validate([
'chatbot_max_gold' => 'required|integer|min:1',
'chatbot_max_daily_rewards' => 'required|integer|min:1',
]);
Sysparam::updateOrCreate(
['alias' => 'chatbot_max_gold'],
[
'body' => (string) $data['chatbot_max_gold'],
'guidetxt' => '单次最高发放金币金额',
]
);
Sysparam::clearCache('chatbot_max_gold');
Sysparam::updateOrCreate(
['alias' => 'chatbot_max_daily_rewards'],
[
'body' => (string) $data['chatbot_max_daily_rewards'],
'guidetxt' => '每个用户单日最多获得金币次数',
]
);
Sysparam::clearCache('chatbot_max_daily_rewards');
return back()->with('success', '全局设置保存成功!');
}
/**
@@ -192,11 +224,90 @@ class AiProviderController extends Controller
Sysparam::clearCache('chatbot_enabled');
$status = $newValue === '1' ? '开启' : '关闭';
$isEnabled = $newValue === '1';
// 确保 AI 实体账号存在
$user = \App\Models\User::firstOrCreate(
['username' => 'AI小班长'],
[
'password' => \Illuminate\Support\Facades\Hash::make(\Illuminate\Support\Str::random(16)),
'user_level' => 10,
'sex' => 0, // 女性
'usersf' => 'storage/avatars/ai_bot_cn_girl.png',
'jjb' => 1000000,
'sign' => '本群首席智慧小管家',
]
);
// 防止后期头像变动,强制更新到最新女生头像
if (! str_contains($user->usersf ?? '', 'ai_bot_cn_girl.png')) {
$user->update([
'usersf' => 'storage/avatars/ai_bot_cn_girl.png',
'sex' => 0,
]);
}
$userData = [
'user_id' => $user->id,
'username' => $user->username,
'level' => $user->user_level,
'sex' => $user->sex,
'headface' => $user->headface,
'vip_icon' => $user->vipIcon(),
'vip_name' => $user->vipName(),
'vip_color' => $user->isVip() ? ($user->vipLevel?->color ?? '') : '',
'is_admin' => false,
'position_icon' => '',
'position_name' => '',
];
// 广播机器人进出事件(供前端名单增删)
broadcast(new \App\Events\ChatBotToggled($userData, $isEnabled));
// 像真实的玩家一样,对全网活跃房间进行高调进出场播报
$activeRoomIds = $this->chatState->getAllActiveRoomIds();
if (empty($activeRoomIds)) {
$activeRoomIds = [1]; // 兜底
}
// 把 AI 实体挂名到一个主房间,即可被 app/Console/Commands/AutoSaveExp.php 扫描发经验
$mainRoomId = $activeRoomIds[0];
if ($isEnabled) {
$this->chatState->userJoin($mainRoomId, $user->username, $userData);
} else {
// 清理可能存在的所有房间的残留挂名
foreach ($activeRoomIds as $rId) {
$this->chatState->userLeave($rId, $user->username);
}
}
foreach ($activeRoomIds as $roomId) {
$content = $isEnabled
? '<span style="color: #9333ea; font-weight: bold;">🤖 【AI小班长】 迈着整齐的步伐进入了房间,随时为您服务!</span>'
: '<span style="color: #9ca3af; font-weight: bold;">🤖 【AI小班长】 去休息啦,大家聊得开心!</span>';
$botMsg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => '进出播报',
'to_user' => '大家',
'content' => $content,
'is_secret' => false,
'font_color' => '#9333ea',
'action' => 'system_welcome',
'welcome_user' => $user->username,
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $botMsg);
broadcast(new \App\Events\MessageSent($roomId, $botMsg));
\App\Jobs\SaveMessageJob::dispatch($botMsg);
}
return response()->json([
'status' => 'success',
'message' => "聊天机器人已{$status}",
'enabled' => $newValue === '1',
'enabled' => $isEnabled,
]);
}

View File

@@ -62,6 +62,11 @@ class ChatBotController extends Controller
], 403);
}
$aiUser = \App\Models\User::where('username', 'AI小班长')->first();
if ($aiUser) {
$aiUser->increment('exp_num', 1);
}
$user = Auth::user();
$message = $request->input('message');
$roomId = $request->input('room_id');
@@ -92,40 +97,61 @@ class ChatBotController extends Controller
$reply = str_replace('[ACTION:GIVE_GOLD]', '', $reply);
$reply = trim($reply);
$maxDailyRewards = (int) Sysparam::getValue('chatbot_max_daily_rewards', '1');
$maxGold = (int) Sysparam::getValue('chatbot_max_gold', '5000');
$redisKey = 'ai_chat:give_gold:'.date('Ymd').':'.$user->id;
// 原子操作,防止并发多次领取
if (Redis::setnx($redisKey, 1)) {
Redis::expire($redisKey, 86400); // 缓存 24 小时
$dailyCount = (int) Redis::get($redisKey);
// 给用户发放随机 100~5000 金币
$goldAmount = rand(100, 5000);
$this->currencyService->change(
$user,
'gold',
$goldAmount,
CurrencySource::AI_GIFT,
'AI小班长发善心赠送的金币福利',
$roomId
);
if ($dailyCount < $maxDailyRewards) {
$goldAmount = rand(100, $maxGold);
// 发送全场大广播
$sysMsg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => '系统传音',
'to_user' => '大家',
'content' => "🤖 听闻小萌新哭穷AI小班长看【{$user->username}】骨骼惊奇,大方地赏赐了 {$goldAmount} 枚金币福利!",
'is_secret' => false,
'font_color' => '#d97706', // 橙色醒目
'action' => '大声宣告',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $sysMsg);
broadcast(new MessageSent($roomId, $sysMsg));
SaveMessageJob::dispatch($sysMsg);
if ($aiUser && $aiUser->jjb >= $goldAmount) {
Redis::incr($redisKey);
Redis::expire($redisKey, 86400); // 缓存 24 小时
// 真实扣除 AI 金币
$this->currencyService->change(
$aiUser,
'gold',
-$goldAmount,
CurrencySource::GIFT_SENT,
"赏赐给 {$user->username} 的金币福利",
$roomId
);
// 给用户发放金币
$this->currencyService->change(
$user,
'gold',
$goldAmount,
CurrencySource::AI_GIFT,
'AI小班长发善心赠送的金币福利',
$roomId
);
// 发送全场大广播
$sysMsg = [
'id' => $this->chatState->nextMessageId($roomId),
'room_id' => $roomId,
'from_user' => 'AI小班长',
'to_user' => $user->username,
'content' => "🤖 听闻小萌新哭穷,本班长看你骨骼惊奇,大方地赏赐了 {$goldAmount} 枚金币福利!",
'is_secret' => false,
'font_color' => '#d97706', // 橙色醒目
'action' => '大声宣告',
'sent_at' => now()->toDateTimeString(),
];
$this->chatState->pushMessage($roomId, $sysMsg);
broadcast(new MessageSent($roomId, $sysMsg));
SaveMessageJob::dispatch($sysMsg);
} else {
// 如果余额不足
$reply .= "\n\n(哎呀,我这个月的工资花光啦,没钱发金币了,大家多赏点吧~)";
}
} else {
// 如果已经领过了,修改回复提醒
$reply = $reply."\n\n(系统提示:你今天已经领过金币福利啦,每天只能领一次哦)";
$reply .= "\n\n(系统提示:你今天已经领过金币福利啦,把机会留给其他人吧)";
}
}