feat: 支持同卡种续卡叠加并修复过期登录态

支付成功后将次数或有效期叠加到已有会员卡,首页与详情页引导续卡;接口 401 时同步清掉本地登录态。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
richarjiang
2026-09-07 14:04:11 +08:00
parent 801e2f47da
commit 88cd8419c8
22 changed files with 1370 additions and 123 deletions

View File

@@ -5,10 +5,11 @@ import {
NotFoundException,
} from '@nestjs/common'
import { CardType, Order } from '@prisma/client'
import { MembershipStatus, OrderStatus, FlashSaleOrderStatus } from '@mp-pilates/shared'
import { OrderStatus, FlashSaleOrderStatus } from '@mp-pilates/shared'
import { PrismaService } from '../prisma/prisma.service'
import { WechatPayService, WxPaymentParams } from './wechat-pay.service'
import { InviteService } from '../invite/invite.service'
import { MembershipService } from '../membership/membership.service'
export interface CreateOrderResult {
order: Order
@@ -30,6 +31,7 @@ export class PaymentService {
private readonly prisma: PrismaService,
private readonly wechatPayService: WechatPayService,
private readonly inviteService: InviteService,
private readonly membershipService: MembershipService,
) {}
// ─── User: create order ────────────────────────────────────────────────────
@@ -118,32 +120,29 @@ export class PaymentService {
}
const now = new Date()
const expireDate = new Date(now.getTime() + cardType.durationDays * 86_400_000)
await this.prisma.$transaction([
this.prisma.order.update({
await this.prisma.$transaction(async (tx) => {
const membership = await this.membershipService.grantPurchasedCard(
tx,
existingOrder.userId,
cardType,
now,
)
await tx.order.update({
where: { id: existingOrder.id },
data: {
status: OrderStatus.PAID,
wxTransactionId: notification.wxTransactionId,
paidAt: now,
membershipId: membership.id,
},
}),
this.prisma.membership.create({
data: {
userId: existingOrder.userId,
cardTypeId: existingOrder.cardTypeId,
startDate: now,
expireDate,
remainingTimes: cardType.totalTimes ?? null,
status: MembershipStatus.ACTIVE,
},
}),
])
})
})
await this.inviteService.recordTrialOrderPaid(existingOrder.id)
this.logger.log(`Order PAID and Membership created: orderNo=${notification.orderNo}`)
this.logger.log(`Order PAID and membership granted: orderNo=${notification.orderNo}`)
// ── Flash sale order: mark as PAID ──
if (existingOrder.flashSaleId) {