feat: 支持分享邀请好友功能
This commit is contained in:
@@ -5,6 +5,7 @@ import { MembershipStatus, OrderStatus } from '@mp-pilates/shared'
|
||||
import { PaymentService } from '../payment.service'
|
||||
import { WechatPayService } from '../wechat-pay.service'
|
||||
import { PrismaService } from '../../prisma/prisma.service'
|
||||
import { InviteService } from '../../invite/invite.service'
|
||||
|
||||
// ─── Fixtures ─────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -35,6 +36,11 @@ const mockUser = {
|
||||
updatedAt: new Date(),
|
||||
}
|
||||
|
||||
const mockInviteService = {
|
||||
validateInviterForTrialOrder: jest.fn(),
|
||||
recordTrialOrderPaid: jest.fn(),
|
||||
}
|
||||
|
||||
const buildMockOrder = (overrides: Partial<Record<string, unknown>> = {}) => ({
|
||||
id: 'order-uuid-1',
|
||||
userId: mockUser.id,
|
||||
@@ -105,6 +111,7 @@ describe('PaymentService', () => {
|
||||
PaymentService,
|
||||
{ provide: PrismaService, useValue: prisma },
|
||||
{ provide: WechatPayService, useValue: wechat },
|
||||
{ provide: InviteService, useValue: mockInviteService },
|
||||
],
|
||||
}).compile()
|
||||
|
||||
@@ -161,6 +168,16 @@ describe('PaymentService', () => {
|
||||
)
|
||||
})
|
||||
|
||||
it('validates inviter relationship for trial card orders', async () => {
|
||||
prisma.cardType.findUnique.mockResolvedValue({ ...mockCardType, type: 'TRIAL' })
|
||||
prisma.user.findUnique.mockResolvedValue(mockUser)
|
||||
prisma.order.create.mockResolvedValue(buildMockOrder())
|
||||
|
||||
await service.createOrder(mockUser.id, mockCardType.id, 'inviter-001')
|
||||
|
||||
expect(mockInviteService.validateInviterForTrialOrder).toHaveBeenCalledWith(mockUser.id, 'inviter-001')
|
||||
})
|
||||
|
||||
it('throws NotFoundException when cardType does not exist', async () => {
|
||||
prisma.cardType.findUnique.mockResolvedValue(null)
|
||||
|
||||
@@ -232,6 +249,7 @@ describe('PaymentService', () => {
|
||||
|
||||
// membership.create was called
|
||||
expect(prisma.membership.create).toHaveBeenCalledTimes(1)
|
||||
expect(mockInviteService.recordTrialOrderPaid).toHaveBeenCalledWith(pendingOrder.id)
|
||||
|
||||
expect(result).toContain('SUCCESS')
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import { IsUUID } from 'class-validator'
|
||||
import { IsOptional, IsUUID } from 'class-validator'
|
||||
|
||||
export class CreateOrderDto {
|
||||
@IsUUID()
|
||||
cardTypeId!: string
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
inviterId?: string
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ export class PaymentController {
|
||||
@CurrentUser('sub') userId: string,
|
||||
@Body(new ValidationPipe({ whitelist: true })) dto: CreateOrderDto,
|
||||
) {
|
||||
return this.paymentService.createOrder(userId, dto.cardTypeId)
|
||||
return this.paymentService.createOrder(userId, dto.cardTypeId, dto.inviterId)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,9 +3,10 @@ import { PrismaModule } from '../prisma/prisma.module'
|
||||
import { PaymentService } from './payment.service'
|
||||
import { PaymentController } from './payment.controller'
|
||||
import { WechatPayService } from './wechat-pay.service'
|
||||
import { InviteModule } from '../invite/invite.module'
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
imports: [PrismaModule, InviteModule],
|
||||
controllers: [PaymentController],
|
||||
providers: [PaymentService, WechatPayService],
|
||||
exports: [PaymentService, WechatPayService],
|
||||
|
||||
@@ -8,6 +8,7 @@ import { CardType, Order } from '@prisma/client'
|
||||
import { MembershipStatus, 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'
|
||||
|
||||
export interface CreateOrderResult {
|
||||
order: Order
|
||||
@@ -28,11 +29,12 @@ export class PaymentService {
|
||||
constructor(
|
||||
private readonly prisma: PrismaService,
|
||||
private readonly wechatPayService: WechatPayService,
|
||||
private readonly inviteService: InviteService,
|
||||
) {}
|
||||
|
||||
// ─── User: create order ────────────────────────────────────────────────────
|
||||
|
||||
async createOrder(userId: string, cardTypeId: string): Promise<CreateOrderResult> {
|
||||
async createOrder(userId: string, cardTypeId: string, inviterId?: string): Promise<CreateOrderResult> {
|
||||
const cardType = await this.prisma.cardType.findUnique({ where: { id: cardTypeId } })
|
||||
|
||||
if (!cardType) {
|
||||
@@ -47,6 +49,10 @@ export class PaymentService {
|
||||
throw new NotFoundException(`User ${userId} not found`)
|
||||
}
|
||||
|
||||
if (cardType.type === 'TRIAL') {
|
||||
await this.inviteService.validateInviterForTrialOrder(userId, inviterId)
|
||||
}
|
||||
|
||||
const orderNo = `${Date.now()}${Math.random().toString(36).substring(2, 8)}`
|
||||
|
||||
const order = await this.prisma.order.create({
|
||||
@@ -135,6 +141,8 @@ export class PaymentService {
|
||||
}),
|
||||
])
|
||||
|
||||
await this.inviteService.recordTrialOrderPaid(existingOrder.id)
|
||||
|
||||
this.logger.log(`Order PAID and Membership created: orderNo=${notification.orderNo}`)
|
||||
|
||||
// ── Flash sale order: mark as PAID ──
|
||||
|
||||
Reference in New Issue
Block a user