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
+37 -1
View File
@@ -73,11 +73,47 @@ class VipLevel extends Model
'exp_multiplier' => 'float',
'jjb_multiplier' => 'float',
'sort_order' => 'integer',
'price' => 'integer',
'price' => 'float',
'duration_days' => 'integer',
'allow_custom_messages' => 'boolean',
];
/**
* 判断当前等级是否高于指定等级。
* 依靠 sort_order 判断。
*/
public function isHigherThan(self|int|null $other): bool
{
if ($other === null) {
return true;
}
$otherOrder = ($other instanceof self)
? $other->sort_order
: self::where('id', $other)->value('sort_order') ?? 0;
return $this->sort_order > $otherOrder;
}
/**
* 计算相对于另一个等级的差价。
* 如果当前等级价格更低,则返回 0
*/
public function getUpgradePrice(self|int|null $other): float
{
if ($other === null) {
return (float) $this->price;
}
$otherPrice = ($other instanceof self)
? (float) $other->price
: (float) (self::where('id', $other)->value('price') ?? 0);
$diff = (float) $this->price - $otherPrice;
return max(0.0, $diff);
}
/**
* 关联:该等级下的所有用户
*/
+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(),
+13 -6
View File
@@ -46,17 +46,24 @@ class VipService
* @param User $user 目标用户
* @param int $vipLevelId VIP 等级 ID
* @param int $days 天数(0=永久)
* @param bool $isUpgrade 是否为补差价升级
*/
public function grantVip(User $user, int $vipLevelId, int $days = 30): void
public function grantVip(User $user, int $vipLevelId, int $days = 30, bool $isUpgrade = false): void
{
$oldVipId = $user->vip_level_id;
$user->vip_level_id = $vipLevelId;
if ($days > 0) {
// 如果用户已有未过期的会员,在现有到期时间上延长
$baseTime = ($user->hy_time && $user->hy_time->isFuture())
? $user->hy_time
: now();
$user->hy_time = $baseTime->addDays($days);
if ($isUpgrade && $oldVipId && $user->hy_time && $user->hy_time->isFuture()) {
// 如果是升级,到期日期保持不变,除非新等级是永久(days=0)
// 此时只需更新等级 ID,无需修改 hy_time。
} else {
// 如果是新购或续费,在现有到期时间上延长
$baseTime = ($user->hy_time && $user->hy_time->isFuture())
? $user->hy_time
: now();
$user->hy_time = $baseTime->addDays($days);
}
} else {
// 永久会员
$user->hy_time = null;