新增:个性装扮支持多份购买,同款续购自动叠加天数
This commit is contained in:
@@ -52,12 +52,14 @@ class DecorationService
|
||||
* 购买装扮:扣金币、写购买记录、更新 users.active_decorations。
|
||||
*
|
||||
* 同槽位的旧装扮会被新购买覆盖(旧装扮不退款),不同槽位可并行持有。
|
||||
* 若购买的是已激活的同款样式,则自动叠加天数而非覆盖重置。
|
||||
*
|
||||
* @param User $user 购买用户
|
||||
* @param ShopItem $item 装扮商品
|
||||
* @param int $quantity 购买份数
|
||||
* @return array{ok:bool, message:string, balance_after?:int, slot?:string, style?:string, expires_at?:string}
|
||||
*/
|
||||
public function purchase(User $user, ShopItem $item): array
|
||||
public function purchase(User $user, ShopItem $item, int $quantity = 1): array
|
||||
{
|
||||
// 根据商品类型映射到对应槽位
|
||||
$slot = self::TYPE_TO_SLOT[$item->type] ?? null;
|
||||
@@ -65,14 +67,29 @@ class DecorationService
|
||||
return ['ok' => false, 'message' => '未知装扮类型'];
|
||||
}
|
||||
|
||||
$totalPrice = $item->price * $quantity;
|
||||
|
||||
// 校验金币余额
|
||||
if ($user->jjb < $item->price) {
|
||||
return ['ok' => false, 'message' => "金币不足,购买 [{$item->name}] 需要 {$item->price} 金币,当前仅有 {$user->jjb} 金币。"];
|
||||
if ($user->jjb < $totalPrice) {
|
||||
return ['ok' => false, 'message' => "金币不足,购买 {$quantity} 份 [{$item->name}] 需要 {$totalPrice} 金币,当前仅有 {$user->jjb} 金币。"];
|
||||
}
|
||||
|
||||
// 计算过期时间(至少 1 天)
|
||||
$days = max(1, (int) ($item->duration_days ?? 1));
|
||||
$expiresAt = Carbon::now()->addDays($days);
|
||||
$totalDays = $days * $quantity;
|
||||
|
||||
// 检查同一槽位是否已激活相同样式 → 叠加天数
|
||||
$decorations = $this->getActiveDecorations($user);
|
||||
$isSameActive = ! empty($decorations[$slot])
|
||||
&& ($decorations[$slot]['style'] ?? '') === $item->slug;
|
||||
|
||||
if ($isSameActive) {
|
||||
// 在现有到期时间上追加天数
|
||||
$existingExpires = Carbon::parse($decorations[$slot]['expires_at']);
|
||||
$expiresAt = $existingExpires->copy()->addDays($totalDays);
|
||||
} else {
|
||||
$expiresAt = Carbon::now()->addDays($totalDays);
|
||||
}
|
||||
|
||||
// 按装扮类型使用不同的流水来源标识,便于后台按类型筛选消费记录
|
||||
$source = match ($item->type) {
|
||||
@@ -84,11 +101,11 @@ class DecorationService
|
||||
};
|
||||
|
||||
// 事务包裹:扣金币、写购买记录、更新激活状态三步原子操作
|
||||
DB::transaction(function () use ($user, $item, $slot, $days, $expiresAt, $source) {
|
||||
DB::transaction(function () use ($user, $item, $slot, $totalPrice, $totalDays, $expiresAt, $source) {
|
||||
// ① 通过统一积分服务扣除金币(含流水记录)
|
||||
$this->currencyService->change(
|
||||
$user, 'gold', -$item->price, $source,
|
||||
"购买装扮:{$item->name}({$days}天)"
|
||||
$user, 'gold', -$totalPrice, $source,
|
||||
"购买装扮:{$item->name}({$totalDays}天)"
|
||||
);
|
||||
|
||||
// ② 写入购买记录(用于后台统计与用户回溯)
|
||||
@@ -96,11 +113,11 @@ class DecorationService
|
||||
'user_id' => $user->id,
|
||||
'shop_item_id' => $item->id,
|
||||
'status' => 'active',
|
||||
'price_paid' => $item->price,
|
||||
'price_paid' => $totalPrice,
|
||||
'expires_at' => $expiresAt,
|
||||
]);
|
||||
|
||||
// ③ 更新用户 active_decorations JSON 字段(同槽位覆盖,不同槽位合并)
|
||||
// ③ 更新用户 active_decorations JSON 字段(同槽位合并,不同槽位追加)
|
||||
$decorations = $this->getActiveDecorations($user);
|
||||
$decorations[$slot] = [
|
||||
'style' => $item->slug,
|
||||
@@ -113,13 +130,19 @@ class DecorationService
|
||||
// 重新读取最新余额,避免缓存脏数据
|
||||
$balanceAfter = (int) $user->fresh()->jjb;
|
||||
|
||||
// 计算叠加后的总天数显示(如果是续费,显示累计总天数)
|
||||
$displayDays = $isSameActive
|
||||
? (int) Carbon::now()->diffInDays(Carbon::parse($expiresAt), false) + 1
|
||||
: $totalDays;
|
||||
|
||||
return [
|
||||
'ok' => true,
|
||||
'message' => "购买成功!{$item->icon} {$item->name} 已激活({$days}天有效)",
|
||||
'message' => "购买成功!{$item->icon} {$item->name} 已激活({$displayDays}天有效)",
|
||||
'balance_after' => $balanceAfter,
|
||||
'slot' => $slot,
|
||||
'style' => $item->slug,
|
||||
'expires_at' => $expiresAt->toIso8601String(),
|
||||
'quantity' => $quantity,
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -49,10 +49,10 @@ class ShopService
|
||||
'auto_fishing' => $this->buyAutoFishingCard($user, $item),
|
||||
ShopItem::TYPE_SIGN_REPAIR => $this->buySignRepairCard($user, $item, $quantity),
|
||||
// ── 个人装扮购买(委托给 DecorationService)───────────────
|
||||
'msg_bubble' => $this->decorationService->purchase($user, $item),
|
||||
'msg_name_color' => $this->decorationService->purchase($user, $item),
|
||||
'msg_text_color' => $this->decorationService->purchase($user, $item),
|
||||
'avatar_frame' => $this->decorationService->purchase($user, $item),
|
||||
'msg_bubble' => $this->decorationService->purchase($user, $item, $quantity),
|
||||
'msg_name_color' => $this->decorationService->purchase($user, $item, $quantity),
|
||||
'msg_text_color' => $this->decorationService->purchase($user, $item, $quantity),
|
||||
'avatar_frame' => $this->decorationService->purchase($user, $item, $quantity),
|
||||
default => ['ok' => false, 'message' => '未知商品类型'],
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user