Skip to content

Commit b3c3407

Browse files
committed
chore: add block 4
1 parent f12adb8 commit b3c3407

13 files changed

Lines changed: 699 additions & 0 deletions

File tree

course/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ Der normale Unterricht startet weiterhin im Root von `db-2-app`. Die Checkpoints
1010
| `block-2-complete` | Block-2-Loesung mit `V2__enforce_ticket_rules.sql` |
1111
| `block-3-start` | Einstieg in Block 3 auf Basis von `block-2-complete` |
1212
| `block-3-complete` | Block-3-Loesung mit Transaktionsworkflow |
13+
| `block-4-start` | Einstieg in Block 4 auf Basis von `block-3-complete` |
14+
| `block-4-complete` | Block-4-Loesung mit Schema-Design-Erweiterungen |
1315

1416
## Arbeitskopie erzeugen
1517

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import jakarta.validation.constraints.NotBlank;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
@Data
9+
@NoArgsConstructor
10+
@Schema(description = "Eingabe für ein neues Ticket")
11+
class CreateTicketRequest {
12+
13+
@NotBlank
14+
@Schema(description = "Kurzer Tickettitel", example = "Datenbankverbindung pruefen")
15+
private String title;
16+
17+
@NotBlank
18+
@Schema(description = "Ticketstatus", example = "open")
19+
private String status;
20+
21+
@Schema(description = "Priorität des Tickets", example = "normal")
22+
private String priority;
23+
24+
@Schema(description = "Optionale fachliche Referenz aus einem externen System", example = "MON-2026-0042")
25+
private String externalReference;
26+
27+
@Schema(description = "Optionaler erster Kommentar", example = "Fehler tritt seit heute Morgen auf.")
28+
private String initialComment;
29+
30+
CreateTicketRequest(String title, String status, String initialComment) {
31+
this(title, status, "normal", null, initialComment);
32+
}
33+
34+
CreateTicketRequest(
35+
String title,
36+
String status,
37+
String priority,
38+
String externalReference,
39+
String initialComment
40+
) {
41+
this.title = title;
42+
this.status = status;
43+
this.priority = priority;
44+
this.externalReference = externalReference;
45+
this.initialComment = initialComment;
46+
}
47+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import io.swagger.v3.oas.annotations.Operation;
4+
import io.swagger.v3.oas.annotations.Parameter;
5+
import io.swagger.v3.oas.annotations.tags.Tag;
6+
import jakarta.validation.Valid;
7+
import org.springframework.http.HttpStatus;
8+
import org.springframework.web.bind.annotation.*;
9+
10+
import java.util.List;
11+
12+
@RestController
13+
@RequestMapping("/api/tickets")
14+
@Tag(name = "Tickets", description = "Ticket Controller")
15+
class TicketController {
16+
17+
private final TicketService ticketService;
18+
19+
TicketController(TicketService ticketService) {
20+
this.ticketService = ticketService;
21+
}
22+
23+
@GetMapping
24+
@Operation(summary = "Tickets lesen", description = "Liest alle nicht geloeschten Tickets oder filtert nach einem Status.")
25+
List<TicketResponse> findTickets(
26+
@Parameter(description = "Optionaler Statusfilter, zum Beispiel open")
27+
@RequestParam(required = false) String status
28+
) {
29+
return ticketService.findTickets(status);
30+
}
31+
32+
@PostMapping
33+
@ResponseStatus(HttpStatus.CREATED)
34+
@Operation(summary = "Ticket erstellen", description = "Erstellt ein Ticket mit Priorität, optionaler fachlicher Referenz und optionalem ersten Kommentar.")
35+
TicketResponse createTicket(@Valid @RequestBody CreateTicketRequest request) {
36+
return ticketService.createTicket(request);
37+
}
38+
39+
@PatchMapping("/{id}/status")
40+
@Operation(summary = "Ticketstatus ändern", description = "Ändert den Status und schreibt ein Ereignis.")
41+
TicketResponse changeStatus(
42+
@PathVariable Long id,
43+
@Valid @RequestBody StatusChangeRequest request
44+
) {
45+
return ticketService.changeStatus(id, request);
46+
}
47+
48+
@DeleteMapping("/{id}")
49+
@ResponseStatus(HttpStatus.NO_CONTENT)
50+
@Operation(summary = "Ticket ausblenden", description = "Markiert ein Ticket mit deleted_at, statt die Zeile physisch zu loeschen.")
51+
void deleteTicket(@PathVariable Long id) {
52+
ticketService.deleteTicket(id);
53+
}
54+
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import jakarta.persistence.*;
4+
import lombok.AccessLevel;
5+
import lombok.Data;
6+
import lombok.NoArgsConstructor;
7+
8+
import java.time.OffsetDateTime;
9+
10+
@Entity
11+
@Table(name = "tickets", schema = "app_starter")
12+
@Data
13+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
14+
class TicketEntity {
15+
16+
@Id
17+
@GeneratedValue(strategy = GenerationType.IDENTITY)
18+
private Long id;
19+
20+
@Column(name = "title")
21+
private String title;
22+
23+
@Column(name = "status")
24+
private String status;
25+
26+
@Column(name = "external_reference")
27+
private String externalReference;
28+
29+
@Column(name = "priority")
30+
private String priority;
31+
32+
@Version
33+
@Column(name = "version")
34+
private Long version;
35+
36+
@Column(name = "created_at", insertable = false, updatable = false)
37+
private OffsetDateTime createdAt;
38+
39+
@Column(name = "updated_at")
40+
private OffsetDateTime updatedAt;
41+
42+
@Column(name = "deleted_at")
43+
private OffsetDateTime deletedAt;
44+
45+
TicketEntity(String title, String status) {
46+
this(title, status, "normal", null);
47+
}
48+
49+
TicketEntity(String title, String status, String priority, String externalReference) {
50+
this.title = title;
51+
this.status = status;
52+
this.priority = priority;
53+
this.externalReference = externalReference;
54+
}
55+
56+
@PrePersist
57+
void markCreated() {
58+
if (updatedAt == null) {
59+
updatedAt = OffsetDateTime.now();
60+
}
61+
}
62+
63+
@PreUpdate
64+
void markUpdated() {
65+
updatedAt = OffsetDateTime.now();
66+
}
67+
68+
void markDeleted() {
69+
deletedAt = OffsetDateTime.now();
70+
}
71+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import org.mapstruct.Mapper;
4+
import org.mapstruct.Mapping;
5+
6+
@Mapper(componentModel = "spring")
7+
interface TicketMapper {
8+
9+
TicketResponse toResponse(TicketEntity entity);
10+
11+
@Mapping(target = "id", ignore = true)
12+
@Mapping(target = "version", ignore = true)
13+
@Mapping(target = "createdAt", ignore = true)
14+
@Mapping(target = "updatedAt", ignore = true)
15+
@Mapping(target = "deletedAt", ignore = true)
16+
TicketEntity toEntity(CreateTicketRequest request);
17+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import org.springframework.data.jpa.repository.JpaRepository;
4+
5+
import java.util.List;
6+
7+
interface TicketRepository extends JpaRepository<TicketEntity, Long> {
8+
9+
List<TicketEntity> findByDeletedAtIsNullOrderByCreatedAtDesc();
10+
11+
List<TicketEntity> findByStatusAndDeletedAtIsNullOrderByCreatedAtDesc(String status);
12+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package ch.hftm.db2.ticketsystem.ticket;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
import lombok.Value;
5+
6+
import java.time.OffsetDateTime;
7+
8+
@Value
9+
@Schema(description = "Antwortmodell für ein Ticket")
10+
class TicketResponse {
11+
12+
@Schema(description = "Technische Ticket-ID", example = "1")
13+
Long id;
14+
15+
@Schema(description = "Kurzer Tickettitel", example = "VPN-Zugriff analysieren")
16+
String title;
17+
18+
@Schema(description = "Aktueller Ticketstatus", example = "open")
19+
String status;
20+
21+
@Schema(description = "Priorität", example = "normal")
22+
String priority;
23+
24+
@Schema(description = "Optionale fachliche Referenz", example = "MON-2026-0042")
25+
String externalReference;
26+
27+
@Schema(description = "Optimistic-Locking-Version", example = "0")
28+
Long version;
29+
30+
@Schema(description = "Erstellzeitpunkt des Tickets")
31+
OffsetDateTime createdAt;
32+
33+
@Schema(description = "Letzter Änderungszeitpunkt des Tickets")
34+
OffsetDateTime updatedAt;
35+
36+
Long id() {
37+
return id;
38+
}
39+
40+
String title() {
41+
return title;
42+
}
43+
44+
String status() {
45+
return status;
46+
}
47+
48+
String priority() {
49+
return priority;
50+
}
51+
52+
String externalReference() {
53+
return externalReference;
54+
}
55+
56+
Long version() {
57+
return version;
58+
}
59+
60+
OffsetDateTime createdAt() {
61+
return createdAt;
62+
}
63+
64+
OffsetDateTime updatedAt() {
65+
return updatedAt;
66+
}
67+
}

0 commit comments

Comments
 (0)