fix(server): hide CLOSED rows from preview for legacy mini-program compatibility

The previous fix made publishDaySlots CLOSE rows instead of DELETE them.
That works correctly on the server, but the live mini-program still ships
the old schedule.vue which only knows about isPublished (no status
field). It would re-render the CLOSED rows with the '已发布' badge,
making it look like 'clear day' did nothing.

Hide CLOSED rows from getSchedulePreview so the legacy client shows
'当日暂无排课' for rest days — the same UX it had before this fix
shipped. All-CLOSED days return []; days with a mix return only the
non-CLOSED rows; days the cron hasn't touched still return the ghost
template.

The republished mini-program (with status-aware rendering) will see the
same [] for rest days, which is consistent. Tests: 4 new specs covering
all-OPEN / all-CLOSED / mixed / empty-day cases.
This commit is contained in:
richarjiang
2026-09-10 22:36:41 +08:00
parent 806f3ee770
commit b8c0dd6781
2 changed files with 93 additions and 5 deletions

View File

@@ -231,6 +231,69 @@ describe('TimeSlotService', () => {
})
})
// -------------------------------------------------------------------------
// getSchedulePreview
// -------------------------------------------------------------------------
describe('getSchedulePreview', () => {
const date = '2026-04-07'
it('returns OPEN/FULL rows with isPublished: true', async () => {
mockPrisma.timeSlot.findMany.mockResolvedValueOnce([
makeSlot({ id: 'slot-1', startTime: '09:00', endTime: '10:00', status: TimeSlotStatus.OPEN }),
makeSlot({ id: 'slot-2', startTime: '10:30', endTime: '11:30', status: TimeSlotStatus.FULL }),
])
const result = await service.getSchedulePreview(date)
expect(result).toHaveLength(2)
expect(result.every((s) => s.isPublished === true)).toBe(true)
})
it('HIDES CLOSED rows so the legacy client does not render them as "已发布"', async () => {
// Simulates an admin "clear day" — DB now has 13 CLOSED rows.
mockPrisma.timeSlot.findMany.mockResolvedValueOnce(
Array.from({ length: 13 }, (_, i) =>
makeSlot({
id: `slot-${i}`,
startTime: `${String(8 + Math.floor(i / 2)).padStart(2, '0')}:${i % 2 === 0 ? '00' : '30'}`,
endTime: `${String(8 + Math.floor(i / 2)).padStart(2, '0')}:${i % 2 === 0 ? '30' : '30'}`,
status: TimeSlotStatus.CLOSED,
}),
),
)
const result = await service.getSchedulePreview(date)
// Critical for online compatibility: the legacy client must see []
// so it shows "当日暂无排课", not 13 phantom "已发布" slots.
expect(result).toEqual([])
})
it('returns the OPEN subset and hides CLOSED when mixed', async () => {
mockPrisma.timeSlot.findMany.mockResolvedValueOnce([
makeSlot({ id: 'open-1', status: TimeSlotStatus.OPEN }),
makeSlot({ id: 'closed-1', status: TimeSlotStatus.CLOSED, startTime: '10:00', endTime: '11:00' }),
makeSlot({ id: 'open-2', status: TimeSlotStatus.OPEN, startTime: '10:30', endTime: '11:30' }),
])
const result = await service.getSchedulePreview(date)
expect(result).toHaveLength(2)
expect(result.map((s) => s.id).sort()).toEqual(['open-1', 'open-2'])
expect(result.find((s) => s.id === 'closed-1')).toBeUndefined()
})
it('returns the ghost template when the day has no rows at all', async () => {
mockPrisma.timeSlot.findMany.mockResolvedValueOnce([])
const result = await service.getSchedulePreview(date)
// Ghost = isPublished false, id null.
expect(result.length).toBeGreaterThan(0)
expect(result.every((s) => s.isPublished === false && s.id === null)).toBe(true)
})
})
// -------------------------------------------------------------------------
// createManualSlot
// -------------------------------------------------------------------------