Files
ai-teacher/backend/src/main/java/com/aiteacher/book/BookController.java
T

125 lines
4.6 KiB
Java

package com.aiteacher.book;
import com.aiteacher.document.FigureEntity;
import com.aiteacher.document.FigureRepository;
import com.aiteacher.document.MarkdownStorageService;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@RestController
@RequestMapping("/api/v1/books")
public class BookController {
private final BookService bookService;
private final FigureRepository figureRepository;
private final MarkdownStorageService markdownStorageService;
public BookController(BookService bookService, FigureRepository figureRepository,
MarkdownStorageService markdownStorageService) {
this.bookService = bookService;
this.figureRepository = figureRepository;
this.markdownStorageService = markdownStorageService;
}
@PostMapping(consumes = "multipart/form-data")
public ResponseEntity<?> upload(@RequestParam("file") MultipartFile file) throws IOException {
Book book = bookService.upload(file);
return ResponseEntity.status(HttpStatus.ACCEPTED).body(toSummaryResponse(book));
}
@GetMapping
public ResponseEntity<List<Map<String, Object>>> list() {
List<Map<String, Object>> books = bookService.listAll().stream()
.map(this::toFullResponse)
.toList();
return ResponseEntity.ok(books);
}
@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> get(@PathVariable UUID id) {
Book book = bookService.getById(id);
return ResponseEntity.ok(toFullResponse(book));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> delete(@PathVariable UUID id) {
bookService.delete(id);
return ResponseEntity.noContent().build();
}
@PostMapping("/{id}/reembed")
public ResponseEntity<Map<String, Object>> reembed(@PathVariable UUID id) {
Book book = bookService.reembed(id);
return ResponseEntity.accepted().body(Map.of(
"bookId", book.getId(),
"status", BookStatus.PROCESSING.name()
));
}
@GetMapping(value = "/{id}/pages/{pageNumber}/html", produces = MediaType.TEXT_HTML_VALUE)
public ResponseEntity<String> getPageHtml(@PathVariable UUID id,
@PathVariable int pageNumber) {
bookService.getById(id); // 404 if not found
try {
return ResponseEntity.ok(markdownStorageService.getText(id, pageNumber));
} catch (Exception e) {
return ResponseEntity.notFound().build();
}
}
@GetMapping("/{id}/figures")
public ResponseEntity<List<FigureResponse>> figures(@PathVariable UUID id) {
bookService.getById(id); // 404 if not found
List<FigureResponse> responses = figureRepository.findAllByBookId(id)
.stream()
.map(f -> toFigureResponse(id, f))
.toList();
return ResponseEntity.ok(responses);
}
private FigureResponse toFigureResponse(UUID bookId, FigureEntity f) {
String filename = f.getImagePath().substring(f.getImagePath().lastIndexOf('/') + 1);
String imageUrl = "/api/v1/figures/" + bookId + "/" + filename;
return new FigureResponse(
f.getId(), f.getLabel(), f.getCaption(),
f.getFigureType().name(), f.getPage(), imageUrl,
f.getSectionId(),
null // section title not eagerly loaded here
);
}
private Map<String, Object> toSummaryResponse(Book book) {
return Map.of(
"id", book.getId(),
"title", book.getTitle(),
"fileName", book.getFileName(),
"status", book.getStatus().name(),
"uploadedAt", book.getUploadedAt()
);
}
private Map<String, Object> toFullResponse(Book book) {
var map = new java.util.LinkedHashMap<String, Object>();
map.put("id", book.getId());
map.put("title", book.getTitle());
map.put("fileName", book.getFileName());
map.put("fileSizeBytes", book.getFileSizeBytes());
map.put("pageCount", book.getPageCount());
map.put("status", book.getStatus().name());
map.put("uploadedAt", book.getUploadedAt());
map.put("processedAt", book.getProcessedAt());
if (book.getErrorMessage() != null) {
map.put("errorMessage", book.getErrorMessage());
}
return map;
}
}