perf: 支持约课以及消息推送能力

This commit is contained in:
richarjiang
2026-04-12 21:44:44 +08:00
parent 9639f44698
commit c60821c5ff
28 changed files with 963 additions and 86 deletions

View File

@@ -2,7 +2,13 @@ import { Test, TestingModule } from '@nestjs/testing'
import { NotFoundException } from '@nestjs/common'
import { UserService } from '../user.service'
import { PrismaService } from '../../prisma/prisma.service'
import { MembershipStatus, BookingStatus, UserRole } from '@mp-pilates/shared'
import {
MembershipStatus,
BookingStatus,
UserRole,
SubscriptionMessageScene,
} from '@mp-pilates/shared'
import { ConfigService } from '@nestjs/config'
// ---------------------------------------------------------------------------
// Helpers
@@ -48,11 +54,22 @@ const mockPrisma = {
findUnique: jest.fn(),
update: jest.fn(),
},
subscriptionMessageConsent: {
upsert: jest.fn(),
findMany: jest.fn(),
},
booking: {
findMany: jest.fn(),
},
}
const mockConfigService = {
get: jest.fn((key: string, defaultValue = '') => {
if (key === 'WX_SUBSCRIBE_TEMPLATE_BOOKING_CONFIRMED') return 'tmpl-booking-confirmed'
return defaultValue
}),
}
// ---------------------------------------------------------------------------
// Tests
// ---------------------------------------------------------------------------
@@ -65,6 +82,7 @@ describe('UserService', () => {
providers: [
UserService,
{ provide: PrismaService, useValue: mockPrisma },
{ provide: ConfigService, useValue: mockConfigService },
],
}).compile()
@@ -101,6 +119,15 @@ describe('UserService', () => {
avatarUrl: 'https://example.com/avatar.png',
role: UserRole.MEMBER,
activeMembershipCount: 3,
subscriptionMessageTemplates: {
templates: [
{
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
description: '购卡或预约时请求一次订阅,用于后续预约确认通知推送',
},
],
},
createdAt: new Date('2024-01-01T00:00:00Z').toISOString(),
})
})
@@ -112,6 +139,89 @@ describe('UserService', () => {
})
})
describe('reportSubscriptionMessageRequests', () => {
it('aggregates and returns subscription consent stats', async () => {
mockPrisma.subscriptionMessageConsent.upsert.mockResolvedValue(undefined)
mockPrisma.subscriptionMessageConsent.findMany.mockResolvedValue([
{
userId: 'user-1',
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
totalRequestCount: 2,
acceptCount: 1,
rejectCount: 1,
banCount: 0,
filterCount: 0,
sentCount: 0,
lastResult: 'reject',
lastRequestedAt: new Date('2024-01-03T00:00:00Z'),
lastSentAt: null,
createdAt: new Date('2024-01-01T00:00:00Z'),
updatedAt: new Date('2024-01-03T00:00:00Z'),
},
])
const result = await service.reportSubscriptionMessageRequests('user-1', [
{
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
result: 'reject',
},
])
expect(mockPrisma.subscriptionMessageConsent.upsert).toHaveBeenCalledWith({
where: {
userId_templateId_scene: {
userId: 'user-1',
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
},
},
create: {
userId: 'user-1',
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
totalRequestCount: 1,
acceptCount: 0,
rejectCount: 1,
banCount: 0,
filterCount: 0,
sentCount: 0,
lastResult: 'reject',
lastRequestedAt: expect.any(Date),
},
update: {
totalRequestCount: { increment: 1 },
acceptCount: { increment: 0 },
rejectCount: { increment: 1 },
banCount: { increment: 0 },
filterCount: { increment: 0 },
lastResult: 'reject',
lastRequestedAt: expect.any(Date),
},
})
expect(result).toEqual([
{
userId: 'user-1',
templateId: 'tmpl-booking-confirmed',
scene: SubscriptionMessageScene.BOOKING_CREATED,
totalRequestCount: 2,
acceptCount: 1,
rejectCount: 1,
banCount: 0,
filterCount: 0,
sentCount: 0,
lastResult: 'reject',
lastRequestedAt: '2024-01-03T00:00:00.000Z',
lastSentAt: null,
createdAt: '2024-01-01T00:00:00.000Z',
updatedAt: '2024-01-03T00:00:00.000Z',
},
])
})
})
// -------------------------------------------------------------------------
// updateProfile
// -------------------------------------------------------------------------
@@ -145,6 +255,7 @@ describe('UserService', () => {
expect(result.nickname).toBe('Bob')
expect(result.avatarUrl).toBe('https://example.com/new.png')
expect(result.activeMembershipCount).toBe(1)
expect(result.subscriptionMessageTemplates.templates).toHaveLength(1)
})
it('only includes provided fields in the update payload', async () => {