功能:商店完善戒指板块
迁移:
- 2026_03_01_153959:shop_items 增加 intimacy_bonus/charm_bonus 字段
Seeder(RingItemsSeeder):
- 银质戒指 500金 亲密+10 魅力+30
- 黄金戒指 2000金 亲密+30 魅力+80
- 红宝石戒指 8000金 亲密+80 魅力+200
- 钻石戒指 30000金 亲密+200 魅力+500
- 传说神戒 100000金 亲密+500 魅力+1000
ShopService:
- buyItem() 分支加 ring 类型
- buyRing():扣金币 + 写入 active UserPurchase(背包持有)
ShopController::items():
- 返回 intimacy_bonus/charm_bonus
- 统计 ring_counts(各戒指持有数量)
shop-panel.blade.php:
- 新增「💍 求婚戒指」分组(排在最后)
- 图标右上角红色数字徽章(持有时)
- 卡片下方显示亲密度/魅力加成
- 购买按钮与现有逻辑复用
This commit is contained in:
@@ -42,13 +42,26 @@ class ShopController extends Controller
|
||||
'price' => $item->price,
|
||||
'type' => $item->type,
|
||||
'duration_days' => $item->duration_days,
|
||||
'intimacy_bonus' => $item->intimacy_bonus,
|
||||
'charm_bonus' => $item->charm_bonus,
|
||||
]);
|
||||
|
||||
// 统计背包中各戒指持有数量
|
||||
$ringCounts = \App\Models\UserPurchase::query()
|
||||
->where('user_id', $user->id)
|
||||
->where('status', 'active')
|
||||
->whereHas('item', fn ($q) => $q->where('type', 'ring'))
|
||||
->selectRaw('shop_item_id, count(*) as qty')
|
||||
->groupBy('shop_item_id')
|
||||
->pluck('qty', 'shop_item_id')
|
||||
->toArray();
|
||||
|
||||
return response()->json([
|
||||
'items' => $items,
|
||||
'user_jjb' => $user->jjb ?? 0,
|
||||
'active_week_effect' => $this->shopService->getActiveWeekEffect($user),
|
||||
'has_rename_card' => $this->shopService->hasRenameCard($user),
|
||||
'ring_counts' => $ringCounts, // [item_id => qty]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ class ShopService
|
||||
'instant' => $this->buyInstant($user, $item),
|
||||
'duration' => $this->buyWeekCard($user, $item),
|
||||
'one_time' => $this->buyRenameCard($user, $item),
|
||||
'ring' => $this->buyRing($user, $item),
|
||||
default => ['ok' => false, 'message' => '未知商品类型'],
|
||||
};
|
||||
}
|
||||
@@ -209,6 +210,31 @@ class ShopService
|
||||
return $purchase->shopItem->effectKey();
|
||||
}
|
||||
|
||||
/**
|
||||
* 购买结婚戒指:扣金币、写入背包(active 状态,等待结婚时消耗)。
|
||||
*
|
||||
* @return array{ok:bool, message:string}
|
||||
*/
|
||||
public function buyRing(User $user, ShopItem $item): array
|
||||
{
|
||||
DB::transaction(function () use ($user, $item): void {
|
||||
// 扣除金币
|
||||
$user->decrement('jjb', $item->price);
|
||||
// 写入背包(active = 未使用,求婚时变为 used_pending→used 或 lost)
|
||||
UserPurchase::create([
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => $item->price,
|
||||
]);
|
||||
});
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'message' => "💍 {$item->name} 购买成功!已放入背包,可用于求婚。",
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户当前激活的改名卡(是否持有未用改名卡)
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:为 shop_items 表添加戒指属性字段
|
||||
*
|
||||
* intimacy_bonus — 结婚时初始亲密度加成
|
||||
* charm_bonus — 结婚时双方魅力加成
|
||||
* 对非戒指类商品均为 NULL,不影响现有数据。
|
||||
*/
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* 添加戒指加成字段。
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('shop_items', function (Blueprint $table): void {
|
||||
$table->unsignedSmallInteger('intimacy_bonus')->default(0)->after('duration_days')->comment('结婚初始亲密度加成(戒指专用)');
|
||||
$table->unsignedSmallInteger('charm_bonus')->default(0)->after('intimacy_bonus')->comment('结婚魅力加成(戒指专用)');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 回滚:删除字段。
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('shop_items', function (Blueprint $table): void {
|
||||
$table->dropColumn(['intimacy_bonus', 'charm_bonus']);
|
||||
});
|
||||
}
|
||||
};
|
||||
92
database/seeders/RingItemsSeeder.php
Normal file
92
database/seeders/RingItemsSeeder.php
Normal file
@@ -0,0 +1,92 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* 文件功能:初始化商店戒指商品数据
|
||||
*
|
||||
* 共 5 档戒指,对应不同价格、亲密度加成、魅力加成。
|
||||
* 可重复运行(updateOrCreate 幂等)。
|
||||
*/
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\ShopItem;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
class RingItemsSeeder extends Seeder
|
||||
{
|
||||
/**
|
||||
* 写入 5 档结婚戒指数据。
|
||||
*/
|
||||
public function run(): void
|
||||
{
|
||||
$rings = [
|
||||
[
|
||||
'slug' => 'ring_silver',
|
||||
'name' => '银质戒指',
|
||||
'icon' => '💍',
|
||||
'description' => '朴素银戒,见证两心相许。',
|
||||
'price' => 500,
|
||||
'type' => 'ring',
|
||||
'intimacy_bonus' => 10,
|
||||
'charm_bonus' => 30,
|
||||
'sort_order' => 101,
|
||||
],
|
||||
[
|
||||
'slug' => 'ring_gold',
|
||||
'name' => '黄金戒指',
|
||||
'icon' => '💛',
|
||||
'description' => '18K黄金,情比金坚。',
|
||||
'price' => 2000,
|
||||
'type' => 'ring',
|
||||
'intimacy_bonus' => 30,
|
||||
'charm_bonus' => 80,
|
||||
'sort_order' => 102,
|
||||
],
|
||||
[
|
||||
'slug' => 'ring_ruby',
|
||||
'name' => '红宝石戒指',
|
||||
'icon' => '❤️',
|
||||
'description' => '鸽血红宝,热烈如初恋。',
|
||||
'price' => 8000,
|
||||
'type' => 'ring',
|
||||
'intimacy_bonus' => 80,
|
||||
'charm_bonus' => 200,
|
||||
'sort_order' => 103,
|
||||
],
|
||||
[
|
||||
'slug' => 'ring_diamond',
|
||||
'name' => '钻石戒指',
|
||||
'icon' => '💎',
|
||||
'description' => '一克拉钻石,誓言永恒。',
|
||||
'price' => 30000,
|
||||
'type' => 'ring',
|
||||
'intimacy_bonus' => 200,
|
||||
'charm_bonus' => 500,
|
||||
'sort_order' => 104,
|
||||
],
|
||||
[
|
||||
'slug' => 'ring_legendary',
|
||||
'name' => '传说神戒',
|
||||
'icon' => '🌟',
|
||||
'description' => '世间仅此一款,天命之环,天作之合。',
|
||||
'price' => 100000,
|
||||
'type' => 'ring',
|
||||
'intimacy_bonus' => 500,
|
||||
'charm_bonus' => 1000,
|
||||
'sort_order' => 105,
|
||||
],
|
||||
];
|
||||
|
||||
foreach ($rings as $ring) {
|
||||
ShopItem::updateOrCreate(
|
||||
['slug' => $ring['slug']],
|
||||
array_merge($ring, [
|
||||
'is_active' => true,
|
||||
'duration_days' => null,
|
||||
])
|
||||
);
|
||||
}
|
||||
|
||||
$this->command->info('✅ 5 枚结婚戒指已写入 shop_items。');
|
||||
}
|
||||
}
|
||||
@@ -318,6 +318,8 @@
|
||||
badge.style.display = 'inline-block';
|
||||
}
|
||||
|
||||
const ringCounts = data.ring_counts || {};
|
||||
|
||||
const groups = [{
|
||||
label: '⚡ 单次特效卡',
|
||||
desc: '立即播放一次,仅自己可见',
|
||||
@@ -333,6 +335,11 @@
|
||||
desc: '',
|
||||
type: 'one_time'
|
||||
},
|
||||
{
|
||||
label: '💍 求婚戒指',
|
||||
desc: '购买后存入背包,求婚时消耗(若被拒绝则遗失)',
|
||||
type: 'ring'
|
||||
},
|
||||
];
|
||||
|
||||
const itemsEl = document.getElementById('shop-items-list');
|
||||
@@ -361,6 +368,8 @@
|
||||
items.forEach(item => {
|
||||
const isRename = item.slug === 'rename_card';
|
||||
const canUseRename = isRename && data.has_rename_card;
|
||||
const isRing = item.type === 'ring';
|
||||
const ownedQty = isRing ? (ringCounts[item.id] || 0) : 0;
|
||||
|
||||
const card = document.createElement('div');
|
||||
card.className = 'shop-card';
|
||||
@@ -369,11 +378,22 @@
|
||||
const row = document.createElement('div');
|
||||
row.className = 'shop-card-row';
|
||||
|
||||
// 图标
|
||||
// 图标区(戒指加持有数徽标)
|
||||
const iconWrap = document.createElement('span');
|
||||
iconWrap.style.cssText =
|
||||
'position:relative; flex-shrink:0; width:28px; text-align:center;';
|
||||
const icon = document.createElement('span');
|
||||
icon.className = 'shop-card-icon';
|
||||
icon.textContent = item.icon;
|
||||
row.appendChild(icon);
|
||||
iconWrap.appendChild(icon);
|
||||
if (isRing && ownedQty > 0) {
|
||||
const badge = document.createElement('span');
|
||||
badge.style.cssText =
|
||||
'position:absolute; top:-4px; right:-4px; background:#f43f5e; color:#fff; font-size:8px; font-weight:800; min-width:14px; height:14px; border-radius:7px; text-align:center; line-height:14px; padding:0 2px;';
|
||||
badge.textContent = ownedQty;
|
||||
iconWrap.appendChild(badge);
|
||||
}
|
||||
row.appendChild(iconWrap);
|
||||
|
||||
// 名称
|
||||
const name = document.createElement('span');
|
||||
@@ -404,6 +424,25 @@
|
||||
card.appendChild(desc);
|
||||
}
|
||||
|
||||
// 戒指:加成信息行
|
||||
if (isRing && (item.intimacy_bonus > 0 || item.charm_bonus > 0)) {
|
||||
const bonus = document.createElement('div');
|
||||
bonus.style.cssText =
|
||||
'font-size:9px; color:#f43f5e; margin-top:3px; display:flex; gap:8px;';
|
||||
if (item.intimacy_bonus > 0) {
|
||||
const b1 = document.createElement('span');
|
||||
b1.textContent = `💞 亲密 +${item.intimacy_bonus}`;
|
||||
bonus.appendChild(b1);
|
||||
}
|
||||
if (item.charm_bonus > 0) {
|
||||
const b2 = document.createElement('span');
|
||||
b2.style.color = '#a855f7';
|
||||
b2.textContent = `✨ 魅力 +${item.charm_bonus}`;
|
||||
bonus.appendChild(b2);
|
||||
}
|
||||
card.appendChild(bonus);
|
||||
}
|
||||
|
||||
section.appendChild(card);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user