vip会员支持补差升级

This commit is contained in:
2026-04-12 14:17:01 +08:00
parent 00b9396dea
commit 82e29753b8
4 changed files with 155 additions and 47 deletions
+29 -5
View File
@@ -35,8 +35,26 @@ class VipPaymentService
*/
public function createLocalOrder(User $user, VipLevel $vipLevel): VipPaymentOrder
{
if ((float) $vipLevel->price <= 0) {
throw new RuntimeException('当前 VIP 等级未设置在线支付价格,暂不支持直接购买。');
$currentVip = $user->isVip() ? $user->vipLevel : null;
$isUpgrade = $currentVip && $vipLevel->isHigherThan($currentVip);
// 如果已经是该等级或更高级别,且不是永久会员续费(逻辑上续费应该用原价,但此处 user 需求是升级补差价)
// 这里我们主要处理补差价升级逻辑。
$price = $isUpgrade
? $vipLevel->getUpgradePrice($currentVip)
: (float) $vipLevel->price;
if ($price < 0.01) {
// 如果差价极小或为 0(例如同级或降级),抛出异常或根据业务逻辑处理
if ($isUpgrade) {
throw new RuntimeException('当前等级差价不足 0.01 元,无法发起升级。');
}
if ($user->vip_level_id === $vipLevel->id) {
// 续费逻辑保持原价
$price = (float) $vipLevel->price;
} else {
throw new RuntimeException('不支持降级购买会员。');
}
}
return VipPaymentOrder::create([
@@ -45,13 +63,15 @@ class VipPaymentService
'user_id' => $user->id,
'vip_level_id' => $vipLevel->id,
'status' => 'created',
'amount' => $vipLevel->price,
'subject' => '购买 VIP 会员 - '.$vipLevel->name,
'amount' => $price,
'subject' => ($isUpgrade ? '【升级】' : '购买') . ' VIP 会员 - ' . $vipLevel->name,
'provider' => 'alipay',
'vip_name' => $vipLevel->name,
'vip_duration_days' => (int) $vipLevel->duration_days,
'meta' => [
'username' => $user->username,
'is_upgrade' => $isUpgrade,
'old_vip_level_id' => $currentVip?->id,
],
]);
}
@@ -188,7 +208,11 @@ class VipPaymentService
if (! $lockedOrder->isVipOpened()) {
// 只在首次成功支付时开通会员,防止重复回调导致会员时长重复叠加。
$user = User::query()->lockForUpdate()->findOrFail($lockedOrder->user_id);
$this->vipService->grantVip($user, $lockedOrder->vip_level_id, (int) $lockedOrder->vip_duration_days);
// 从 meta 中提取是否是升级
$isUpgrade = (bool) ($lockedOrder->meta['is_upgrade'] ?? false);
$this->vipService->grantVip($user, $lockedOrder->vip_level_id, (int) $lockedOrder->vip_duration_days, $isUpgrade);
$lockedOrder->update([
'opened_vip_at' => now(),