vip会员支持补差升级
This commit is contained in:
+37
-1
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 关联:该等级下的所有用户
|
||||
*/
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user