diff --git a/app/Http/Controllers/ChatController.php b/app/Http/Controllers/ChatController.php index ed28500..5fb16d4 100644 --- a/app/Http/Controllers/ChatController.php +++ b/app/Http/Controllers/ChatController.php @@ -17,8 +17,10 @@ use App\Events\UserLeft; use App\Http\Requests\SendMessageRequest; use App\Jobs\SaveMessageJob; use App\Models\Autoact; +use App\Models\Gift; use App\Models\Room; use App\Models\Sysparam; +use App\Models\User; use App\Services\ChatStateService; use App\Services\MessageFilterService; use App\Services\VipService; @@ -433,6 +435,99 @@ class ChatController extends Controller ]); } + /** + * 送花/礼物:消耗金币给目标用户增加魅力值 + * + * 根据 gift_id 查找 gifts 表中的礼物类型,读取对应的金币消耗和魅力增量。 + * 送花成功后在聊天室广播带图片的消息。 + */ + public function sendFlower(Request $request): JsonResponse + { + $request->validate([ + 'to_user' => 'required|string', + 'room_id' => 'required|integer', + 'gift_id' => 'required|integer', + 'count' => 'sometimes|integer|min:1|max:99', + ]); + + $user = Auth::user(); + if (! $user) { + return response()->json(['status' => 'error', 'message' => '请先登录'], 401); + } + + $toUsername = $request->input('to_user'); + $roomId = $request->input('room_id'); + $giftId = $request->integer('gift_id'); + $count = $request->integer('count', 1); + + // 不能给自己送花 + if ($toUsername === $user->username) { + return response()->json(['status' => 'error', 'message' => '不能给自己送花哦~']); + } + + // 查找礼物类型 + $gift = Gift::where('id', $giftId)->where('is_active', true)->first(); + if (! $gift) { + return response()->json(['status' => 'error', 'message' => '礼物不存在或已下架']); + } + + // 查找目标用户 + $toUser = User::where('username', $toUsername)->first(); + if (! $toUser) { + return response()->json(['status' => 'error', 'message' => '用户不存在']); + } + + $totalCost = $gift->cost * $count; + $totalCharm = $gift->charm * $count; + + // 检查金币余额 + if (($user->jjb ?? 0) < $totalCost) { + return response()->json([ + 'status' => 'error', + 'message' => "金币不足!送 {$count} 份【{$gift->name}】需要 {$totalCost} 金币,您当前有 ".($user->jjb ?? 0).' 枚。', + ]); + } + + // 扣除金币、增加对方魅力 + $user->jjb = ($user->jjb ?? 0) - $totalCost; + $user->save(); + + $toUser->meili = ($toUser->meili ?? 0) + $totalCharm; + $toUser->save(); + + // 构建礼物图片 URL + $giftImageUrl = $gift->image ? "/images/gifts/{$gift->image}" : ''; + + // 广播送花消息(含图片标记,前端识别后渲染图片) + $countText = $count > 1 ? " {$count} 份" : ''; + $sysMsg = [ + 'id' => $this->chatState->nextMessageId($roomId), + 'room_id' => $roomId, + 'from_user' => '系统传音', + 'to_user' => '大家', + 'content' => "{$gift->emoji} {$user->username} 向 {$toUsername} 送出了{$countText}【{$gift->name}】!魅力 +{$totalCharm}!", + 'is_secret' => false, + 'font_color' => '#e91e8f', + 'action' => '', + 'sent_at' => now()->toDateTimeString(), + 'gift_image' => $giftImageUrl, + 'gift_name' => $gift->name, + ]; + + $this->chatState->pushMessage($roomId, $sysMsg); + broadcast(new MessageSent($roomId, $sysMsg)); + SaveMessageJob::dispatch($sysMsg); + + return response()->json([ + 'status' => 'success', + 'message' => "送花成功!花费 {$totalCost} 金币,{$toUsername} 魅力 +{$totalCharm}", + 'data' => [ + 'my_jjb' => $user->jjb, + 'target_charm' => $toUser->meili, + ], + ]); + } + /** * 解析奖励数值配置(支持固定值或范围格式) * diff --git a/app/Models/Gift.php b/app/Models/Gift.php new file mode 100644 index 0000000..ed8d0c3 --- /dev/null +++ b/app/Models/Gift.php @@ -0,0 +1,53 @@ + 'integer', + 'charm' => 'integer', + 'sort_order' => 'integer', + 'is_active' => 'boolean', + ]; + + /** + * 获取所有启用的礼物列表(按排序字段排列) + * + * @return \Illuminate\Database\Eloquent\Collection + */ + public static function activeList() + { + return static::where('is_active', true) + ->orderBy('sort_order') + ->orderBy('cost') + ->get(); + } +} diff --git a/database/migrations/2026_02_27_005300_create_gifts_table.php b/database/migrations/2026_02_27_005300_create_gifts_table.php new file mode 100644 index 0000000..7230a0a --- /dev/null +++ b/database/migrations/2026_02_27_005300_create_gifts_table.php @@ -0,0 +1,57 @@ +id(); + $table->string('name', 50)->comment('礼物名称'); + $table->string('emoji', 10)->comment('显示图标/emoji'); + $table->string('image', 100)->nullable()->comment('礼物图片路径(相对 /images/gifts/)'); + $table->unsignedInteger('cost')->default(0)->comment('消耗金币数'); + $table->unsignedInteger('charm')->default(1)->comment('增加魅力值'); + $table->unsignedTinyInteger('sort_order')->default(0)->comment('排序(越小越靠前)'); + $table->boolean('is_active')->default(true)->comment('是否启用'); + $table->timestamps(); + }); + + // 填充默认鲜花数据(图片文件名与 sort_order 对应) + DB::table('gifts')->insert([ + ['name' => '小雏菊', 'emoji' => '🌼', 'image' => 'daisy.png', 'cost' => 5, 'charm' => 1, 'sort_order' => 1, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '玫瑰花', 'emoji' => '🌹', 'image' => 'rose.png', 'cost' => 10, 'charm' => 2, 'sort_order' => 2, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '向日葵', 'emoji' => '🌻', 'image' => 'sunflower.png', 'cost' => 20, 'charm' => 5, 'sort_order' => 3, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '樱花束', 'emoji' => '🌸', 'image' => 'sakura.png', 'cost' => 50, 'charm' => 12, 'sort_order' => 4, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '满天星', 'emoji' => '💐', 'image' => 'bouquet.png', 'cost' => 100, 'charm' => 30, 'sort_order' => 5, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '蓝色妖姬', 'emoji' => '🪻', 'image' => 'bluerose.png', 'cost' => 200, 'charm' => 66, 'sort_order' => 6, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ['name' => '钻石花冠', 'emoji' => '👑', 'image' => 'crown.png', 'cost' => 520, 'charm' => 188, 'sort_order' => 7, 'is_active' => true, 'created_at' => now(), 'updated_at' => now()], + ]); + } + + /** + * 回滚:删除 gifts 表 + */ + public function down(): void + { + Schema::dropIfExists('gifts'); + } +}; diff --git a/public/css/chat.css b/public/css/chat.css index 122de5b..9e2e149 100644 --- a/public/css/chat.css +++ b/public/css/chat.css @@ -628,4 +628,25 @@ a:hover { .avatar-option.selected { border-color: #336699; box-shadow: 0 0 6px rgba(51, 102, 153, 0.5); +} + +/* 送花礼物弹跳动画 */ +@keyframes giftBounce { + 0% { + transform: scale(0.3) rotate(-15deg); + opacity: 0; + } + + 50% { + transform: scale(1.3) rotate(5deg); + opacity: 1; + } + + 70% { + transform: scale(0.9) rotate(-3deg); + } + + 100% { + transform: scale(1) rotate(0deg); + } } \ No newline at end of file diff --git a/public/images/gifts/bluerose.png b/public/images/gifts/bluerose.png new file mode 100644 index 0000000..e7de0af Binary files /dev/null and b/public/images/gifts/bluerose.png differ diff --git a/public/images/gifts/bouquet.png b/public/images/gifts/bouquet.png new file mode 100644 index 0000000..fecad4d Binary files /dev/null and b/public/images/gifts/bouquet.png differ diff --git a/public/images/gifts/crown.png b/public/images/gifts/crown.png new file mode 100644 index 0000000..1fc4447 Binary files /dev/null and b/public/images/gifts/crown.png differ diff --git a/public/images/gifts/daisy.png b/public/images/gifts/daisy.png new file mode 100644 index 0000000..88008ba Binary files /dev/null and b/public/images/gifts/daisy.png differ diff --git a/public/images/gifts/rose.png b/public/images/gifts/rose.png new file mode 100644 index 0000000..07ebc0a Binary files /dev/null and b/public/images/gifts/rose.png differ diff --git a/public/images/gifts/sakura.png b/public/images/gifts/sakura.png new file mode 100644 index 0000000..1d5530a Binary files /dev/null and b/public/images/gifts/sakura.png differ diff --git a/public/images/gifts/sunflower.png b/public/images/gifts/sunflower.png new file mode 100644 index 0000000..f5721de Binary files /dev/null and b/public/images/gifts/sunflower.png differ diff --git a/resources/views/chat/partials/scripts.blade.php b/resources/views/chat/partials/scripts.blade.php index 918b3fb..7460ac4 100644 --- a/resources/views/chat/partials/scripts.blade.php +++ b/resources/views/chat/partials/scripts.blade.php @@ -240,7 +240,14 @@ // 管理员公告/系统传音:大字醒目样式 div.style.cssText = 'background: linear-gradient(135deg, #fef2f2, #fff1f2); border: 2px solid #ef4444; border-radius: 6px; padding: 8px 12px; margin: 4px 0; box-shadow: 0 2px 4px rgba(239,68,68,0.15);'; - html = `
${msg.content}
`; + // 如果是送花消息,显示礼物图片 + let giftHtml = ''; + if (msg.gift_image) { + giftHtml = + `${msg.gift_name || ''}`; + } + html = + `
${msg.content}${giftHtml}
`; } else { // 其他系统用户(钓鱼播报、AI小助手等):普通样式 html = diff --git a/resources/views/chat/partials/user-actions.blade.php b/resources/views/chat/partials/user-actions.blade.php index 9e344b5..6424535 100644 --- a/resources/views/chat/partials/user-actions.blade.php +++ b/resources/views/chat/partials/user-actions.blade.php @@ -62,6 +62,7 @@ {{-- ═══════════ 用户名片弹窗 (Alpine.js) ═══════════ --}} +@php $gifts = \App\Models\Gift::activeList(); @endphp
+ }, + + async sendGift() { + if (this.sendingGift || !this.selectedGiftId) return; + this.sendingGift = true; + try { + const res = await fetch('/gift/flower', { + method: 'POST', + headers: { + 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Content-Type' + : 'application/json' , 'Accept' : 'application/json' }, body: JSON.stringify({ to_user: this.userInfo.username, + room_id: window.chatContext.roomId, gift_id: this.selectedGiftId, count: this.giftCount }) }); const data=await + res.json(); alert(data.message); if (data.status === 'success') { this.showUserModal=false; this.giftCount=1; } } + catch (e) { alert('网络异常'); } this.sendingGift=false; } }">