fix: 修复身体画像约课阻断、页面响应式丢失及多项边界问题
- fix(booking): 修复未认领画像阻断约课问题,支持约课事务内自动认领绑定 - fix(app): 修复 Pinia 状态解构导致响应式失效,补齐 PortraitRadar 组件实例传参 - fix(admin): 修复经营助手复测待办点击 404,优化体验课后线索回访文案 - fix(server): 线下评估日期间隔按中国时区校验,复测按期次单项核销,优化单次评估分享卡与限流清理 - test: 补齐 body-portrait-offline 与 booking 来源画像单元测试 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -160,6 +160,11 @@ function buildTxMock(overrides: Record<string, unknown> = {}) {
|
||||
bookingStatusHistory: {
|
||||
create: jest.fn(),
|
||||
},
|
||||
bodyPortraitAssessment: {
|
||||
findUnique: jest.fn(),
|
||||
findFirst: jest.fn(),
|
||||
update: jest.fn(),
|
||||
},
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
@@ -476,6 +481,58 @@ describe('BookingService', () => {
|
||||
expect(result).toBeDefined()
|
||||
})
|
||||
|
||||
it('binds anonymous originAssessmentId to current user on booking create', async () => {
|
||||
const tx = buildTxMock()
|
||||
tx.timeSlot.findUnique.mockResolvedValue(mockOpenSlot)
|
||||
tx.booking.findUnique.mockResolvedValue(null)
|
||||
tx.membership.findUnique.mockResolvedValue(mockActiveMembership)
|
||||
tx.bodyPortraitAssessment.findUnique.mockResolvedValue({ id: 'anon-assess', userId: null })
|
||||
tx.bodyPortraitAssessment.update.mockResolvedValue({ id: 'anon-assess', userId: MOCK_USER_ID })
|
||||
tx.booking.create.mockResolvedValue({
|
||||
...mockConfirmedBooking,
|
||||
originAssessmentId: 'anon-assess',
|
||||
status: BookingStatus.PENDING_CONFIRMATION,
|
||||
})
|
||||
|
||||
;(prisma.$transaction as jest.Mock).mockImplementation((fn) => fn(tx))
|
||||
;(prisma.booking.findUnique as jest.Mock).mockResolvedValue({
|
||||
...mockConfirmedBooking,
|
||||
originAssessmentId: 'anon-assess',
|
||||
status: BookingStatus.PENDING_CONFIRMATION,
|
||||
timeSlot: mockOpenSlot,
|
||||
membership: mockActiveMembership,
|
||||
})
|
||||
;(prisma.user.findMany as jest.Mock).mockResolvedValue([])
|
||||
|
||||
await service.createBooking(MOCK_USER_ID, { ...dto, originAssessmentId: 'anon-assess' })
|
||||
|
||||
expect(tx.bodyPortraitAssessment.update).toHaveBeenCalledWith({
|
||||
where: { id: 'anon-assess' },
|
||||
data: { userId: MOCK_USER_ID, status: 'CLAIMED', claimedAt: expect.any(Date) },
|
||||
})
|
||||
expect(tx.booking.create).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
data: expect.objectContaining({
|
||||
originAssessmentId: 'anon-assess',
|
||||
}),
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
it('rejects originAssessmentId belonging to another user', async () => {
|
||||
const tx = buildTxMock()
|
||||
tx.timeSlot.findUnique.mockResolvedValue(mockOpenSlot)
|
||||
tx.booking.findUnique.mockResolvedValue(null)
|
||||
tx.membership.findUnique.mockResolvedValue(mockActiveMembership)
|
||||
tx.bodyPortraitAssessment.findUnique.mockResolvedValue({ id: 'other-assess', userId: 'someone-else' })
|
||||
|
||||
;(prisma.$transaction as jest.Mock).mockImplementation((fn) => fn(tx))
|
||||
|
||||
await expect(
|
||||
service.createBooking(MOCK_USER_ID, { ...dto, originAssessmentId: 'other-assess' }),
|
||||
).rejects.toThrow('画像报告不属于当前用户')
|
||||
})
|
||||
|
||||
it('records booking status history when user creates a booking', async () => {
|
||||
const nearFullSlot = { ...mockOpenSlot, bookedCount: 4, capacity: 5 }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user