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.
publishDaySlots used to DELETE the TimeSlot rows of a day that an admin
just 'cleared', which made the nightly slot-generation cron resurrect
the default schedule the next morning — a real production bug where
classes would reappear on a teacher's rest day.
Changes:
- publishDaySlots now CLOSEs (never DELETEs) orphaned rows, and materializes
the default template for days with no rows so an empty publish still
records intent. update on existing rows preserves their current status,
so re-publishing the page cannot silently reopen a CLOSED rest day.
- SlotGeneratorService.generateSlots pre-fetches dates that already have any
TimeSlot row and skips them, so the cron no longer overwrites admin-touched
days even when admin only set status without changing times.
- SlotGeneratorService.prunePastClosedSlots (new) deletes past CLOSED slots
with no bookings, scheduled nightly at 02:35, so the 'no DELETE' rule
above does not leak rows forever.
- schedule.vue surfaces the CLOSED state with a grey '已关闭' badge and
card style.
Tests: 12 new specs across time-slot.service.spec.ts (publishDaySlots
behaviour) and slot-generator.service.spec.ts (skip touched dates +
prune conditions). Full suite: 315 / 315 passing.
将 cancelBooking 改为接收 actor({ id, isAdmin }),服务端按 JWT role
分流:普通成员仅能取消自己的预约,管理员跳过 owner check。
history 备注自动按 actor 选择「学员/管理员」前缀,审计可追溯。
客户端无感,仍调 PUT /booking/:id/cancel,前端零改动。
Co-Authored-By: Claude Code <noreply@anthropic.com>
## Features
### Admin Schedule Page (`packages/app/src/pages/admin/schedule.vue`)
- Interactive date-based slot editor for managing daily schedules
- Real-time slot editing: start/end times, capacity adjustments
- Slot deletion with conflict warnings when bookings exist
- Add new slots with modal dialog
- Live booking status display (booked count, people names)
- Publish/Save changes with sync feedback
- Revert unsaved changes with confirmation
- Skeleton loading states and empty state handling
- Responsive design with optimized mobile UX
### Backend Enhancements
- **New DTO** (`PublishDaySlotsDto`): Structured slot publishing with validation
- Date string validation
- Slot array with existing slot IDs for updates
- Time and capacity validation per slot
- **Schedule Preview API** (`getSchedulePreview`):
- Check for existing published slots
- Fallback to active WeekTemplates for unpublished dates
- Unified response format with isPublished flag
- **Publish Slots API** (`publishDaySlots`):
- Atomic transaction for consistency
- Update existing slots with new times/capacity
- Create new slots from template data
- Delete unpublished slots or set to CLOSED if bookings exist
- Prevent capacity reduction below existing bookings
- Returns all published slots for feedback
### State Management
- Enhanced admin store with schedule state
- Support for pending/unsaved slot changes
- Optimistic UI updates with server sync
### Documentation
- Comprehensive scheduling system architecture docs
- Quick reference for admin workflows
- Flow diagrams and state transitions
- Implementation guide for future maintenance
## Breaking Changes
None
## Testing Recommendations
- Create slots for future dates via schedule editor
- Verify booking prevention for locked/full slots
- Test capacity adjustments with existing bookings
- Confirm template-based schedule generation
- Verify transaction rollback on publish failures
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>