134 lines
5.0 KiB
Java
134 lines
5.0 KiB
Java
package com.aiteacher.book;
|
|
|
|
import com.aiteacher.document.FigureEntity;
|
|
import com.aiteacher.document.FigureRepository;
|
|
import com.aiteacher.document.MarkdownStorageService;
|
|
import org.springframework.beans.factory.annotation.Value;
|
|
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;
|
|
|
|
@Value("${app.features.upload-enabled:true}")
|
|
private boolean uploadEnabled;
|
|
|
|
@Value("${app.features.delete-enabled:true}")
|
|
private boolean deleteEnabled;
|
|
|
|
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 {
|
|
if (!uploadEnabled) return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
|
|
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) {
|
|
if (!deleteEnabled) return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).build();
|
|
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;
|
|
}
|
|
}
|