Architektura Aplikacji Internetowych / Programowanie .NET

Rok akademicki: 2026/27

Zadanie laboratoryjne: System Rezerwacji Terminów Obron Projektów "ProjectDefense" (ASP.NET Core Razor Pages, EF Core, Identity, Minimal API, Lokalizacja i Eksport Dokumentów)

Prowadzący: Tomasz Goluch
Wersja: 1.0
Wymagany standard: C# 12 / .NET 8+
Maksymalna ocena zadania: 110 punktów (100 pkt podstawowych + 10 pkt bonusowych = 110%)
Forma realizacji: Projekt wielowarstwowy tworzony od zera (Greenfield)


1. Kontekst Biznesowy i Architektoniczny Projektu

Projektujesz scentralizowany system zarządzania zapisami studentów na obrony i oddawanie projektów w salach laboratoryjnych wydziału – ProjectDefense.

Tradycyjne zapisy prowadzone w arkuszach kalkulacyjnych prowadzą do szeregu problemów operacyjnych:

Twoim celem jest zaprojektowanie i zaimplementowanie wieloprojektowego rozwiązania w technologii ASP.NET Core Razor Pages (.NET 8/9) połączonego z bazą danych Entity Framework Core, systemem ról ASP.NET Core Identity, wielojęzycznością (pl-PL / en-US), punktami końcowymi Minimal API (OpenAPI / Swagger), generatorem raportów (ClosedXML / QuestPDF) oraz dedykowanym klientem konsolowym.


2. Inicjalizacja Środowiska (Krok 0: Przygotowanie Solucji od Zera)

System musi składać się z minimum 3 projektów wykonawczych (Web, Core, ConsoleClient) oraz zalecanego projektu testów automatycznych (Tests). Otwórz terminal w pustym katalogu roboczym i wykonaj poniższy skrypt inicjalizacyjny:

# 1. Utworzenie pustego pliku rozwiązania (Solution)
dotnet new sln -n ProjectDefense

# 2. Utworzenie współdzielonej biblioteki klas (Encje domenowe, Kontrakty, obiekty DTO)
dotnet new classlib -n ProjectDefense.Core -f net8.0

# 3. Utworzenie głównej aplikacji internetowej ASP.NET Core Razor Pages z kontami Identity
dotnet new webapp -n ProjectDefense.Web -f net8.0 --auth Individual

# 4. Utworzenie aplikacji klienta konsolowego (.NET 8)
dotnet new console -n ProjectDefense.ConsoleClient -f net8.0

# 5. Utworzenie projektu testów jednostkowych i integracyjnych (xUnit)
dotnet new xunit -n ProjectDefense.Tests -f net8.0

# 6. Dołączenie projektów do rozwiązania
dotnet sln add ProjectDefense.Core/ProjectDefense.Core.csproj
dotnet sln add ProjectDefense.Web/ProjectDefense.Web.csproj
dotnet sln add ProjectDefense.ConsoleClient/ProjectDefense.ConsoleClient.csproj
dotnet sln add ProjectDefense.Tests/ProjectDefense.Tests.csproj

# 7. Konfiguracja referencji między projektami
dotnet add ProjectDefense.Web/ProjectDefense.Web.csproj reference ProjectDefense.Core/ProjectDefense.Core.csproj
dotnet add ProjectDefense.ConsoleClient/ProjectDefense.ConsoleClient.csproj reference ProjectDefense.Core/ProjectDefense.Core.csproj
dotnet add ProjectDefense.Tests/ProjectDefense.Tests.csproj reference ProjectDefense.Core/ProjectDefense.Core.csproj
dotnet add ProjectDefense.Tests/ProjectDefense.Tests.csproj reference ProjectDefense.Web/ProjectDefense.Web.csproj

# 8. Instalacja wymaganych pakietów NuGet w aplikacji Web (Swagger, Eksport XLSX/PDF, SendGrid)
dotnet add ProjectDefense.Web/ProjectDefense.Web.csproj package Swashbuckle.AspNetCore
dotnet add ProjectDefense.Web/ProjectDefense.Web.csproj package ClosedXML
dotnet add ProjectDefense.Web/ProjectDefense.Web.csproj package QuestPDF
dotnet add ProjectDefense.Web/ProjectDefense.Web.csproj package SendGrid

# 9. Instalacja bibliotek testowych
dotnet add ProjectDefense.Tests/ProjectDefense.Tests.csproj package Microsoft.AspNetCore.Mvc.Testing
dotnet add ProjectDefense.Tests/ProjectDefense.Tests.csproj package Microsoft.EntityFrameworkCore.InMemory
dotnet add ProjectDefense.Tests/ProjectDefense.Tests.csproj package FluentAssertions

3. Model Domenowy i Architektura Danych

Współdzielony model danych oraz kontrakty DTO umieszczamy w projekcie ProjectDefense.Core (dzięki czemu zarówno aplikacja webowa, jak i klient konsolowy operują na tych samych definicjach typów):

  1. ApplicationUser (dziedziczący z IdentityUser):
    • Reprezentuje użytkownika systemu przypisanego do roli "Student" lub "Lecturer" ("Prowadzący").
    • Zawiera dodatkowe właściwości profilowe, np. FullName (Imię i nazwisko), StudentIndexNumber (opcjonalny numer albumu) oraz flagę IsBanned (bool).
  2. Room (Sala):
    • Przechowuje ewidencję sal laboratoryjnych: Id, Name (np. "Laboratorium Sieciowe"), RoomNumber (np. "B214"), WorkstationsCount (liczba stanowisk).
  3. LecturerAvailability (DostępnośćProwadzącego):
    • Definiuje okno dyżuru prowadzącego w konkretnej sali: Id, LecturerId (FK do ApplicationUser), RoomId (FK do Room), StartDate (DateOnly lub DateTime), EndDate, DailyStartTime (TimeOnly lub TimeSpan), DailyEndTime oraz SlotDurationMinutes (int, np. 15 min).
  4. ReservationSlot (Rezerwacja):
    • Reprezentuje pojedynczy, atomowy slot czasowy wygenerowany automatycznie z okna dostępności: Id, LecturerAvailabilityId (FK), StartTime (DateTime), EndTime (DateTime), IsBlockedByLecturer (bool – np. blokada chorobowa) oraz StudentId (nullable FK do ApplicationUser – wartość null oznacza, że termin jest wolny).
  5. StudentBan (BlokadaStudenta – opcjonalna encja audytowa lub obsługa na poziomie użytkownika):
    • Przechowuje informację o nałożonej blokadzie: Id, StudentId, BannedByLecturerId, Reason (powód blokady), BannedUntil (data wygaśnięcia lub blokada permanentna).
┌───────────────────────────────────────────────────────────────────────────────────┐
│                 ARCHITEKTURA SYSTEMU PROJECTDEFENSE (.NET 8/9)                    │
├───────────────────────────────────────────────────────────────────────────────────┤
│  [Część 1: 20 pkt] Moduł Prowadzącego   ──► CRUD Sal, Generowanie Slotów, Ban/Move│
│  [Część 2: 20 pkt] Moduł Studenta       ──► Limit 1 Rezerwacji, Anulowanie, Zmiana│
│  [Część 3: 20 pkt] Walidacja i Konflikty──► Wykrywanie Kolizji Sal, Blokada Przesz│
│  [Część 4: 10 pkt] Identity & E-mail    ──► Role (Student/Prowadzący), IEmailSend.│
│  [Część 5: 10 pkt] Internacjonalizacja  ──► Kultury pl-PL i en-US, Zasoby .resx   │
│  [Część 6: 10 pkt] Minimal API & Swagger──► Endpointy DTO, OpenAPI UI, Walidacja  │
│  [Część 7: 10 pkt] Klient Konsolowy     ──► HttpClient + System.Text.Json (GET/POST│
│  [Część 8: 10 pkt] Eksport Danych (+10%)──► Generowanie .txt, .xlsx, .pdf w RAM   │
└───────────────────────────────────────────────────────────────────────────────────┘

4. Szczegółowe Wymagania i Podział Punktów (Łącznie: 110 pkt / 110%)

Część 1: Moduł Prowadzącego i Automatyczny Generator Slotów (20 pkt)

Prowadzący po zalogowaniu uzyskuje dostęp do panelu zarządzania zasobami laboratoryjnymi oraz harmonogramem obron.

Wymagania szczegółowe:
  1. [4 pkt] Pełny CRUD dla encji Room (Sala):
    • Strony Razor (Pages/Lecturer/Rooms/Index, Create, Edit, Delete, Details) pozwalające na dodawanie, edycję i usuwanie sal laboratoryjnych.
  2. [6 pkt] Definiowanie okien dostępności i automatyczny generator slotów (ReservationSlot):
    • Prowadzący wybiera z listy rozwijanej (<select> oparty o SelectList w PageModel) salę, zakres dat (np. od 10.11.2026 do 12.11.2026), dzienny przedział godzinowy (np. 08:00 – 10:00) oraz czas trwania pojedynczego slotu w minutach (np. 15 min).
    • Logika biznesowa: Na podstawie przesłanego formularza serwis automatycznie oblicza i zapisuje w bazie danych indywidualne rekordy ReservationSlot (np. 08:00–08:15, 08:15–08:30, ...) dla każdego dnia ze wskazanego przedziału.
  3. [4 pkt] Przegląd i zarządzanie wszystkimi rezerwacjami:
    • Prowadzący widzi kompletną tabelę rezerwacji z filtrowaniem po sali oraz statusie (kto, w jakiej sali, w jakim terminie).
    • Prowadzący może anulować dowolną rezerwację (nawet zajętą przez studenta – slot staje się ponownie wolny) lub przepisać studenta na inny wolny termin.
  4. [3 pkt] Awaryjna blokada okresu (np. z powodu choroby):
    • Prowadzący może zaznaczyć wybrany przedział czasu i go zablokować (IsBlockedByLecturer = true), co automatycznie zwalnia przypisanych tam studentów i uniemożliwia dokonywanie nowych rezerwacji w tym oknie.
  5. [3 pkt] Mechanizm blokowania (banowania) studentów:
    • Prowadzący może nałożyć blokadę na wybranego studenta (IsBanned = true lub dodanie rekordu StudentBan), co uniemożliwia studentowi zarezerwowanie jakiegokolwiek terminu (lub logowanie do systemu).
💡 Wskazówki implementacyjne: Algorytm generowania slotów z okna dostępności (Rozwiń)
public List<ReservationSlot> GenerateSlots(LecturerAvailability availability)
{
    var generatedSlots = new List<ReservationSlot>();

    for (var date = availability.StartDate.Date; date <= availability.EndDate.Date; date = date.AddDays(1))
    {
        var currentSlotStart = date.Add(availability.DailyStartTime.ToTimeSpan());
        var dayEnd = date.Add(availability.DailyEndTime.ToTimeSpan());

        while (currentSlotStart.AddMinutes(availability.SlotDurationMinutes) <= dayEnd)
        {
            var currentSlotEnd = currentSlotStart.AddMinutes(availability.SlotDurationMinutes);
            generatedSlots.Add(new ReservationSlot
            {
                LecturerAvailability = availability,
                StartTime = currentSlotStart,
                EndTime = currentSlotEnd,
                IsBlockedByLecturer = false,
                StudentId = null
            });
            currentSlotStart = currentSlotEnd;
        }
    }
    return generatedSlots;
}

Część 2: Moduł Studenta i Reguła Pojedynczej Aktywnej Rezerwacji (20 pkt)

Student po zalogowaniu posiada dostęp wyłącznie do widoku harmonogramu oraz zarządzania własnym terminem obrony.

Wymagania szczegółowe:
  1. [5 pkt] Filtrowana lista dostępnych terminów:
    • Student widzi wyłącznie terminy, które są jednocześnie: wolne (StudentId == null), niezablokowane (!IsBlockedByLecturer) oraz znajdują się w przyszłości (StartTime > DateTime.Now).
  2. [6 pkt] Egzekwowanie limitu dokładnie jednej aktywnej rezerwacji:
    • Student może posiadać maksymalnie jeden aktywny (przyszły) zarezerwowany slot w całym systemie.
    • Próba zarezerwowania drugiego terminu bezpośrednio (lub poprzez zmanipulowane żądanie POST) musi zostać odrzucona przez warstwę logiki biznesowej z czytelnym komunikatem błędu w ModelState / TempData.
  3. [5 pkt] Anulowanie własnej rezerwacji:
    • Student może anulować swoją nadchodzącą rezerwację (StudentId = null), co natychmiast zwalnia slot w puli dostępnych terminów dla innych studentów.
  4. [4 pkt] Transakcyjna zmiana terminu (Reschedule):
    • Student posiadający rezerwację może wybrać funkcję „Zmień termin” i wskazać nowy wolny slot – system w ramach jednej spójnej operacji zwalnia dotychczasowy termin i przypisuje studenta do nowego slotu.

Część 3: Zaawansowana Walidacja, Wykrywanie Kolizji Sal i Wygasanie Slotów (20 pkt)

System musi chronić integralność harmonogramu przed sprzecznymi zakresami dat, nakładaniem się dyżurów w tej samej sali oraz edycją zaszłości historycznych.

Wymagania szczegółowe:
  1. [5 pkt] Walidacja formularzy i spójności przedziałów:
    • Czas trwania pojedynczego slotu musi być dodatni i mieścić się w rozsądnym zakresie (np. [Range(5, 180)] minut).
    • EndDate nie może być wcześniejsza niż StartDate, a DailyEndTime musi być późniejsza niż DailyStartTime o co najmniej długość jednego slotu.
  2. [7 pkt] Zapobieganie konfliktom sal laboratoryjnych (Overlap Detection):
    • Żaden prowadzący nie może zdefiniować nowego okna LecturerAvailability dla danej sali, jeśli w bazie danych istnieje już okno dla tej samej sali, którego zakres dat oraz przedział godzinowy nakładają się (kolidują) z nowym wpisem: \[\text{Konflikt} \iff (\text{Start}_A < \text{End}_B) \land (\text{End}_A > \text{Start}_B)\]
  3. [5 pkt] Logika wygasania terminów historycznych (Past Slots Lock):
    • Rezerwacje, które już się odbyły (StartTime <= DateTime.Now), są traktowane jako archiwalne.
    • System musi blokować wszelkie próby anulowania, rezerwacji lub modyfikacji przeszłych slotów zarówno przez studenta, jak i przez prowadzącego (w interfejsie UI oraz w warstwie serwisu).
  4. [3 pkt] Ergonomia i czytelność interfejsu formularzy:
    • Listy wyboru slotów (np. podczas przepisywania studenta) muszą prezentować czytelnie sformatowane etykiety tekstowe, np.: "Sala B214 (Lab. Sieciowe) – Pn. 16.11.2026, godz. 10:15–10:30".

Część 4: Uwierzytelnianie, Autoryzacja (Role) i Potwierdzenie E-mail (10 pkt)

Wymagania szczegółowe:
  1. [3 pkt] Konfiguracja ASP.NET Core Identity:
    • Wykorzystanie lokalnego kontekstu IdentityDbContext<ApplicationUser> z opcją RequireConfirmedAccount = true.
    • Automatyczny Data Seeder przy starcie aplikacji tworzący role "Lecturer" oraz "Student" wraz z przykładowymi kontami testowymi (aby oceniający mógł od razu zalogować się na oba konta).
  2. [4 pkt] Implementacja usługi IEmailSender:
    • Zaimplementuj interfejs IEmailSender przy użyciu wybranej bramki (np. SendGrid, MailGun) lub lokalnego kontenera testowego SMTP (Mailpit / Papercut) z poprawną obsługą treści HTML (text/html, brak uszkodzonych linków z &amp;).
  3. [3 pkt] Ochrona zasobów i ról:
    • Zabezpieczenie stron prowadzącego atrybutem [Authorize(Roles = "Lecturer")] (lub konwencją AuthorizeFolder), a stron studenckich rolą "Student".
    • Użytkownik niezalogowany nie ma dostępu do żadnych widoków operacyjnych (poza stroną logowania, rejestracji i potwierdzenia e-mail).

Część 5: Internacjonalizacja i Lokalizacja (pl-PL oraz en-US) (10 pkt)

Wymagania szczegółowe:
  1. [5 pkt] Konfiguracja kultur i przełącznik języka:
    • Obsługa co najmniej dwóch kultur: pl-PL oraz en-US za pomocą UseRequestLocalization.
    • Umieszczenie w pasku nawigacyjnym _Layout.cshtml przełącznika języka (zapisującego wybór w ciasteczku .AspNetCore.Culture lub przekazującego parametr ?culture=...).
  2. [5 pkt] Pliki zasobów .resx oraz formatowanie dat i komunikatów:
    • Przetłumaczenie nagłówków stron, etykiet formularzy ([Display]) oraz komunikatów walidacji za pomocą plików .resx w katalogu Resources/.
    • Poprawne wyświetlanie oraz walidacja dat i godzin zgodnie z aktywnie wybraną kulturą użytkownika.

Część 6: Minimal API, Obiekty DTO i Dokumentacja Swagger (10 pkt)

Wymagania szczegółowe:
  1. [6 pkt] Punkty końcowe Minimal API:
    • MapGet("/api/slots/available") – zwraca kolekcję wolnych, przyszłych terminów obron w postaci obiektów DTO (AvailableSlotDto).
    • MapGet("/api/rooms") – zwraca listę sal laboratoryjnych (RoomDto).
    • MapPost("/api/slots/{id:int}/book") – pozwala studentowi zarezerwować wskazany termin na podstawie przesłanego obiektu BookSlotRequestDto (z zachowaniem wszystkich reguł walidacji z Części 2 i 3, w tym limitu 1 aktywnej rezerwacji!).
  2. [2 pkt] Integracja OpenAPI / Swagger:
    • Rejestracja AddEndpointsApiExplorer(), AddSwaggerGen(), UseSwagger() oraz UseSwaggerUI().
  3. [2 pkt] Czysta separacja DTO:
    • Całkowity zakaz zwracania surowych encji bazodanowych EF Core bezpośrednio z punktów końcowych API.

Część 7: Aplikacja Klienta Konsolowego (.NET 8) (10 pkt)

Wymagania szczegółowe:
  1. [5 pkt] Pobieranie danych z API (GET):
    • Aplikacja konsolowa (ProjectDefense.ConsoleClient) łączy się z uruchomionym serwerem webowym przy użyciu HttpClient oraz GetFromJsonAsync i wyświetla w terminalu sformatowaną tabelę dostępnych sal oraz wolnych terminów.
  2. [5 pkt] Dokonywanie rezerwacji z konsoli (POST):
    • Po podaniu identyfikatora/adresu e-mail studenta oraz ID wybranego slotu, klient wysyła żądanie PostAsJsonAsync do /api/slots/{id}/book i czytelnie prezentuje odpowiedź serwera (sukces 200 OK lub komunikat błędu biznesowego 400/409).

Część 8: Eksport Harmonogramu Rezerwacji (.txt, .xlsx, .pdf) (10 pkt – Bonus do 110%)

Wymagania szczegółowe:
  1. [2 pkt] Formularz filtrowania eksportu w panelu prowadzącego:
    • Prowadzący wybiera salę laboratoryjną, zakres dat oraz docelowy format pliku.
  2. [2 pkt] Eksport do pliku tekstowego (.txt):
    • Wygenerowanie czytelnego zestawienia tekstowego w kodowaniu UTF-8 (text/plain).
  3. [3 pkt] Eksport do arkusza kalkulacyjnego Excel (.xlsx):
    • Wykorzystanie biblioteki ClosedXML do utworzenia sformatowanej tabeli z pogrubionym nagłówkiem i dopasowaną szerokością kolumn.
  4. [3 pkt] Eksport do dokumentu PDF (.pdf):
    • Wykorzystanie biblioteki QuestPDF (LicenseType.Community) do wygenerowania oficjalnego arkusza obron gotowego do wydruku i wywieszenia na drzwiach laboratorium.

5. Wymagania Dotyczące Pakietu Testów Automatycznych

W projekcie ProjectDefense.Tests przygotuj zestaw testów weryfikujących kluczowe reguły domenowe:

  1. SlotGenerationTests (Testy jednostkowe):
    • Sprawdzenie, czy dla okna 08:00–10:00 i czasu slotu 15 min przez 2 dni generator tworzy dokładnie 16 niepokrywających się slotów.
  2. RoomConflictDetectionTests (Testy jednostkowe / InMemory EF Core):
    • Weryfikacja, czy próba dodania nakładającej się dostępności w tej samej sali (np. 09:00–11:00 przy istniejącym 08:00–10:00) zostaje odrzucona.
  3. StudentBookingRulesTests:
    • Sprawdzenie, że student posiadający już aktywną rezerwację nie może zarezerwować drugiego slotu oraz że próba modyfikacji slotu z przeszłości kończy się błędem.
  4. MinimalApiIntegrationTests (WebApplicationFactory):
    • Test integracyjny wywołujący GET /api/rooms oraz GET /api/slots/available i weryfikujący status 200 OK oraz poprawną strukturę JSON DTO.

6. Tabela Podsumowująca Oceny (Suma: 110 pkt / 110%)

Nr Zakres modułu Weryfikowane mechanizmy, technologie i standardy Punkty
1 Moduł Prowadzącego CRUD sal, formularz z SelectList, automatyczne generowanie slotów, anulowanie, przepisywanie i banowanie. 20 pkt
2 Moduł Studenta Widok wolnych przyszłych terminów, blokada >1 rezerwacji na studenta, anulowanie i zmiana terminu. 20 pkt
3 Walidacja i Logika Domenowa Walidacja przedziałów dat/godzin, wykrywanie kolizji sal (Overlap), blokada edycji przeszłych slotów. 20 pkt
4 Uwierzytelnianie i Autoryzacja IdentityDbContext, role Student i Lecturer, IEmailSender, pełna blokada dostępu dla niezalogowanych. 10 pkt
5 Internacjonalizacja (I18n) Wsparcie kultur pl-PL i en-US, pliki .resx, lokalizacja dat, godzin i komunikatów walidacji. 10 pkt
6 Minimal API i Swagger Endpointy GET/POST, separacja obiektów DTO, konfiguracja OpenAPI / Swagger UI. 10 pkt
7 Klient Konsolowy (.NET 8) Komunikacja przez HttpClient + System.Net.Http.Json, przeglądanie terminów i rezerwacja slotu. 10 pkt
8 Eksport Danych (Bonus +10%) Strumieniowy eksport harmonogramu sali i zakresu dat do formatów .txt, .xlsx (ClosedXML) i .pdf (QuestPDF). 10 pkt
SUMA (100 pkt = 100% oceny podstawowej + 10 pkt bonusowych) 110 pkt (110%)