feat: 付费体系重构 P0 - 配额独立化/简历付费下载/PDF生成

This commit is contained in:
yuzhiran
2026-06-12 09:31:11 +08:00
parent 5d407b4f79
commit 065fe7a186
23 changed files with 965 additions and 106 deletions
@@ -2,12 +2,14 @@ import { Test, TestingModule } from '@nestjs/testing'
import { getModelToken } from '@nestjs/mongoose'
import { PaymentController } from './payment.controller'
import { WechatPayService } from './wechat-pay.service'
import { QuotaService } from '../user/quota.service'
describe('PaymentController', () => {
let controller: PaymentController
let mockUserModel: any
let mockOrderModel: any
let mockWechatPay: any
let mockQuotaService: any
const mockUserId = '507f1f77bcf86cd799439011'
@@ -26,6 +28,10 @@ describe('PaymentController', () => {
jsapiPay: jest.fn().mockResolvedValue({ paySign: 'mock-sign', nonceStr: 'mock-nonce', package: 'prepay_id=mock', timeStamp: '123456', signType: 'RSA' }),
queryOrder: jest.fn().mockResolvedValue({ trade_state: 'SUCCESS', transaction_id: 'wx123' }),
}
mockQuotaService = {
grantCredits: jest.fn().mockResolvedValue(undefined),
setPlanQuota: jest.fn().mockResolvedValue(undefined),
}
const module: TestingModule = await Test.createTestingModule({
controllers: [PaymentController],
@@ -33,6 +39,7 @@ describe('PaymentController', () => {
{ provide: getModelToken('User'), useValue: mockUserModel },
{ provide: getModelToken('PaymentOrder'), useValue: mockOrderModel },
{ provide: WechatPayService, useValue: mockWechatPay },
{ provide: QuotaService, useValue: mockQuotaService },
],
}).compile()
@@ -107,9 +114,9 @@ describe('PaymentController', () => {
})
it('should return order status', async () => {
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ status: 'pending', plan: 'growth' }) })
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ status: 'pending', plan: 'growth', type: 'membership' }) })
const result = await controller.checkOrder('ORD123', mockUserId)
expect(result).toEqual({ status: 'pending', plan: 'growth' })
expect(result).toEqual({ status: 'pending', plan: 'growth', type: 'membership' })
expect(mockOrderModel.findOne).toHaveBeenCalledWith({ outTradeNo: 'ORD123', userId: mockUserId })
})
})
@@ -128,19 +135,23 @@ describe('PaymentController', () => {
})
it('should activate growth plan', async () => {
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ outTradeNo: 'ORD123', userId: mockUserId, status: 'success', plan: 'growth' }) })
const mockUser = { plan: 'free', vipExpireAt: null, sprintExpireAt: null, sprintRemaining: 0, remaining: 0, save: jest.fn().mockResolvedValue(true) }
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ outTradeNo: 'ORD123', userId: mockUserId, status: 'success', plan: 'growth', type: 'membership' }) })
const mockUser = { plan: 'free', vipExpireAt: null, sprintExpireAt: null, sprintRemaining: 0, remaining: 0, interviewCredits: 1, resumeOptimizeCredits: 0, resumeDownloadCredits: 0, freeOptimizeUsed: 0, save: jest.fn().mockResolvedValue(true) }
mockUserModel.findById.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(mockUser) })
const result = await controller.activate(mockUserId, 'ORD123')
expect(result.success).toBe(true)
expect(result.plan).toBe('growth')
expect(mockUser.save).toHaveBeenCalled()
expect(mockUser.plan).toBe('growth')
expect(mockUser.interviewCredits).toBe(999)
expect(mockUser.resumeOptimizeCredits).toBe(20)
expect(mockUser.resumeDownloadCredits).toBe(10)
})
it('should activate sprint plan', async () => {
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ outTradeNo: 'ORD123', userId: mockUserId, status: 'success', plan: 'sprint' }) })
const mockUser = { plan: 'free', vipExpireAt: null, sprintExpireAt: null, sprintRemaining: 0, remaining: 0, save: jest.fn().mockResolvedValue(true) }
mockOrderModel.findOne.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue({ outTradeNo: 'ORD123', userId: mockUserId, status: 'success', plan: 'sprint', type: 'membership' }) })
const mockUser = { plan: 'free', vipExpireAt: null, sprintExpireAt: null, sprintRemaining: 0, remaining: 0, interviewCredits: 1, resumeOptimizeCredits: 0, resumeDownloadCredits: 0, freeOptimizeUsed: 0, save: jest.fn().mockResolvedValue(true) }
mockUserModel.findById.mockReturnValueOnce({ exec: jest.fn().mockResolvedValue(mockUser) })
const result = await controller.activate(mockUserId, 'ORD123')