Backend Architecture Overview
This page records the backend's cross-module design. For endpoint details, fields, and request formats, use OpenAPI and code as the source of truth.
Code Map
backend/
├── apps/
│ ├── accounts/ # Users, roles, authentication, multi-factor login
│ ├── courses/ # Course records and CourseOccurrence
│ ├── classrooms/ # Classroom master data, free rooms, schedules
│ ├── borrowings/ # Reservations, approval, conflict checks
│ ├── checkins/ # Check-in sessions and records
│ ├── signage/ # Signage device auth, activation, display data
│ ├── observations/ # Observation templates and record model
│ ├── repairs/ # Repair tickets
│ ├── abuse/ # Violation reports
│ └── logs/ # Operation logs
├── classroom_manager/ # Django settings, URLs, Celery entry
├── common/ # System config, uploads, pagination, task status
├── services/ # Cross-module services
└── manage.pyRuntime Shape
manage.py,wsgi.py, andasgi.pydefault toclassroom_manager.settings.base; production containers may explicitly usesettings.prod.- PostgreSQL is the database. Redis is used for the Celery broker/result backend and Django cache.
- REST Framework requires authentication by default; anonymous endpoints opt out explicitly on the view.
- Response shape is not fully uniform. Custom actions often use
{code, message, data}, while default DRF viewset behavior may return native DRF structures. Frontend code and agents need to follow the concrete endpoint.
Authentication and Roles
- Web login works through JWT cookies; WeChat mini program login returns tokens in the response body.
CookieJWTAuthenticationreadsAuthorizationfirst, then thetokencookie.current_roleis request-scoped context.CurrentRoleMiddlewareparses it from JWT orX-Current-Roleand verifies that it belongs touser.roles. Keeping it out of persisted user state lets the same account choose different active roles in different clients.
Signage devices use SignageDeviceAuthentication and device tokens only. Device identity is separate from user identity so a classroom terminal can stay online without depending on a user session.
Shared Model Choices
- Class sessions:
CourseOccurrenceis the source of truth. When adding another schedule source, prefer converting it into course occurrences instead of adding a second "actual schedule" state model. - Classroom occupancy: classroom schedules and free-room lookup are derived from course occurrences plus manual reservations.
- Reservation conflicts: always check both manual reservations and course occupancy.
- Check-ins: sessions are anchored to course occurrences, not detached from the course system.
- Signage: current schedules show course occurrences only and do not merge manual reservations.
- System config: seasonal schedules and semester start dates come from
SystemConfig.
Why Task State Lives In PostgreSQL
Course and user imports can exceed the reliable execution window of a normal HTTP request, so Celery handles the work. Views validate input, save uploaded files, create TaskRecord, and dispatch tasks; workers update task state.
Task status uses common.models_task.TaskRecord as the source of truth and is exposed through the read-only /api/v1/tasks/ viewset. Non-admin users can only see their own tasks; superadmin and secretary can see all tasks. The old Redis task_owner:<task_id> ownership scheme is retired so task ownership does not live in cache-only state.
Known Debt
logs.permissions.IsLogAdminstill reads the legacyuser.rolefield and is not fully integrated with therolesarray.services/file_storage.pyhas a local/S3 abstraction, but the upload endpoint still writes directly to local storage.- Signage devices are not yet integrated into the check-in write flow.
observationshas a model and template endpoint, but the business workflow is still thin. Define permissions and lifecycle before expanding it.