mirror of
https://github.com/booklore-app/booklore.git
synced 2026-02-18 00:17:53 +01:00
Added late read module
This commit is contained in:
@@ -17,6 +17,7 @@ import org.springframework.web.bind.annotation.*;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
@RequestMapping("/v1/book")
|
||||
@RestController
|
||||
@@ -30,9 +31,13 @@ public class BookController {
|
||||
return ResponseEntity.ok(booksService.getBook(bookId));
|
||||
}
|
||||
|
||||
@GetMapping()
|
||||
public ResponseEntity<Page<BookDTO>> getBooks(@RequestParam(defaultValue = "0") @Min(0) int page, @RequestParam(defaultValue = "25") @Min(1) @Max(100) int size) {
|
||||
Page<BookDTO> books = booksService.getBooks(page, size);
|
||||
@GetMapping
|
||||
public ResponseEntity<Page<BookDTO>> getBooks(
|
||||
@RequestParam(defaultValue = "0") @Min(0) int page,
|
||||
@RequestParam(defaultValue = "25") @Min(1) @Max(100) int size,
|
||||
@RequestParam(defaultValue = "lastReadTime") String sortBy,
|
||||
@RequestParam(defaultValue = "desc") String sortDir) {
|
||||
Page<BookDTO> books = booksService.getBooks(page, size, sortBy, sortDir);
|
||||
return ResponseEntity.ok(books);
|
||||
}
|
||||
|
||||
@@ -44,6 +49,14 @@ public class BookController {
|
||||
|
||||
@GetMapping("/{bookId}/cover")
|
||||
public ResponseEntity<Resource> getBookCover(@PathVariable long bookId) {
|
||||
Random random = new Random();
|
||||
int delay = 250 + random.nextInt(750);
|
||||
try {
|
||||
Thread.sleep(delay);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
||||
}
|
||||
return ResponseEntity.ok(booksService.getBookCover(bookId));
|
||||
}
|
||||
|
||||
@@ -52,9 +65,20 @@ public class BookController {
|
||||
return booksService.getBookData(bookId);
|
||||
}
|
||||
|
||||
@GetMapping("/{bookId}/viewer-setting")
|
||||
public ResponseEntity<BookViewerSettingDTO> getBookViewerSettings(@PathVariable long bookId) {
|
||||
return ResponseEntity.ok(booksService.getBookViewerSetting(bookId));
|
||||
}
|
||||
|
||||
@PutMapping("/{bookId}/viewer-setting")
|
||||
public ResponseEntity<Void> updateBookViewerSettings(@RequestBody BookViewerSettingDTO bookViewerSettingDTO, @PathVariable long bookId) {
|
||||
booksService.saveBookViewerSetting(bookId, bookViewerSettingDTO);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
|
||||
@PutMapping("/{bookId}/update-last-read")
|
||||
public ResponseEntity<Void> updateBookViewerSettings(@PathVariable long bookId) {
|
||||
booksService.updateLastReadTime(bookId);
|
||||
return ResponseEntity.noContent().build();
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package com.adityachandel.booklore.dto;
|
||||
import com.fasterxml.jackson.annotation.JsonInclude;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -13,5 +14,6 @@ public class BookDTO {
|
||||
private Long libraryId;
|
||||
private String fileName;
|
||||
private String title;
|
||||
private Instant lastReadTime;
|
||||
private List<AuthorDTO> authors = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.adityachandel.booklore.entity;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
@Entity
|
||||
@@ -40,4 +41,7 @@ public class Book {
|
||||
|
||||
@OneToOne(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private BookViewerSetting viewerSetting;
|
||||
|
||||
@Column(name = "last_read_time")
|
||||
private Instant lastReadTime;
|
||||
}
|
||||
|
||||
@@ -19,5 +19,7 @@ public interface BookRepository extends JpaRepository<Book, Long>, JpaSpecificat
|
||||
Optional<Book> findBookByIdAndLibraryId(long id, long libraryId);
|
||||
|
||||
List<Book> findByTitleContainingIgnoreCase(String title);
|
||||
|
||||
Page<Book> findByLastReadTimeIsNotNull(Pageable pageable);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import com.adityachandel.booklore.exception.ErrorCode;
|
||||
import com.adityachandel.booklore.repository.BookRepository;
|
||||
import com.adityachandel.booklore.repository.BookViewerSettingRepository;
|
||||
import com.adityachandel.booklore.service.parser.PdfParser;
|
||||
import com.adityachandel.booklore.transformer.BookSettingTransformer;
|
||||
import com.adityachandel.booklore.transformer.BookTransformer;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -18,6 +19,7 @@ import org.springframework.core.io.UrlResource;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.data.domain.PageImpl;
|
||||
import org.springframework.data.domain.PageRequest;
|
||||
import org.springframework.data.domain.Sort;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
@@ -28,6 +30,7 @@ import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -49,10 +52,13 @@ public class BooksService {
|
||||
return BookTransformer.convertToBookDTO(book);
|
||||
}
|
||||
|
||||
public Page<BookDTO> getBooks(int page, int size) {
|
||||
PageRequest pageRequest = PageRequest.of(page, size);
|
||||
Page<Book> bookPage = bookRepository.findAll(PageRequest.of(page, size));
|
||||
List<BookDTO> bookDTOs = bookPage.getContent().stream().map(BookTransformer::convertToBookDTO).collect(Collectors.toList());
|
||||
public Page<BookDTO> getBooks(int page, int size, String sortBy, String sortDir) {
|
||||
Sort sort = Sort.by(Sort.Direction.fromString(sortDir), sortBy);
|
||||
PageRequest pageRequest = PageRequest.of(page, size, sort);
|
||||
Page<Book> bookPage = bookRepository.findByLastReadTimeIsNotNull(pageRequest);
|
||||
List<BookDTO> bookDTOs = bookPage.getContent().stream()
|
||||
.map(BookTransformer::convertToBookDTO)
|
||||
.collect(Collectors.toList());
|
||||
return new PageImpl<>(bookDTOs, pageRequest, bookPage.getTotalElements());
|
||||
}
|
||||
|
||||
@@ -102,10 +108,6 @@ public class BooksService {
|
||||
Book book = pdfParser.parseBook(filePath.toAbsolutePath().toString(), appProperties.getPathConfig());
|
||||
book.setViewerSetting(BookViewerSetting.builder()
|
||||
.bookId(book.getId())
|
||||
.pageNumber(0)
|
||||
.zoom("page-fit")
|
||||
.spread("off")
|
||||
.sidebar_visible(false)
|
||||
.build());
|
||||
return book;
|
||||
}
|
||||
@@ -147,4 +149,15 @@ public class BooksService {
|
||||
List<Book> books = bookRepository.findByTitleContainingIgnoreCase(title);
|
||||
return books.stream().map(BookTransformer::convertToBookDTO).toList();
|
||||
}
|
||||
|
||||
public BookViewerSettingDTO getBookViewerSetting(long bookId) {
|
||||
BookViewerSetting bookViewerSetting = bookViewerSettingRepository.findById(bookId).orElseThrow(() -> ErrorCode.BOOK_NOT_FOUND.createException(bookId));
|
||||
return BookSettingTransformer.convertToDTO(bookViewerSetting);
|
||||
}
|
||||
|
||||
public void updateLastReadTime(long bookId) {
|
||||
Book book = bookRepository.findById(bookId).orElseThrow(() -> ErrorCode.BOOK_NOT_FOUND.createException(bookId));
|
||||
book.setLastReadTime(Instant.now());
|
||||
bookRepository.save(book);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package com.adityachandel.booklore.transformer;
|
||||
|
||||
import com.adityachandel.booklore.dto.BookViewerSettingDTO;
|
||||
import com.adityachandel.booklore.entity.BookViewerSetting;
|
||||
|
||||
public class BookSettingTransformer {
|
||||
|
||||
public static BookViewerSettingDTO convertToDTO(BookViewerSetting bookViewerSetting) {
|
||||
return BookViewerSettingDTO.builder()
|
||||
.zoom(bookViewerSetting.getZoom())
|
||||
.pageNumber(bookViewerSetting.getPageNumber())
|
||||
.spread(bookViewerSetting.getSpread())
|
||||
.sidebar_visible(bookViewerSetting.isSidebar_visible())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ public class BookTransformer {
|
||||
bookDTO.setLibraryId(book.getLibrary().getId());
|
||||
bookDTO.setFileName(book.getFileName());
|
||||
bookDTO.setTitle(book.getTitle());
|
||||
bookDTO.setLastReadTime(book.getLastReadTime());
|
||||
bookDTO.setAuthors(book.getAuthors().stream().map(AuthorTransformer::toAuthorDTO).collect(Collectors.toList()));
|
||||
return bookDTO;
|
||||
}
|
||||
|
||||
@@ -7,13 +7,16 @@ CREATE TABLE IF NOT EXISTS library
|
||||
|
||||
CREATE TABLE IF NOT EXISTS book
|
||||
(
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
title VARCHAR(255),
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
library_id BIGINT NOT NULL,
|
||||
path VARCHAR(1000) NOT NULL,
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
title VARCHAR(255),
|
||||
file_name VARCHAR(255) NOT NULL,
|
||||
library_id BIGINT NOT NULL,
|
||||
path VARCHAR(1000) NOT NULL,
|
||||
last_read_time TIMESTAMP NULL,
|
||||
CONSTRAINT fk_library FOREIGN KEY (library_id) REFERENCES library (id) ON DELETE CASCADE,
|
||||
CONSTRAINT unique_file_library UNIQUE (file_name, library_id)
|
||||
CONSTRAINT unique_file_library UNIQUE (file_name, library_id),
|
||||
INDEX idx_library_id (library_id),
|
||||
INDEX idx_last_read_time (last_read_time)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS author
|
||||
@@ -29,15 +32,17 @@ CREATE TABLE IF NOT EXISTS book_author_mapping
|
||||
author_id BIGINT NOT NULL,
|
||||
CONSTRAINT fk_book_author_mapping_book FOREIGN KEY (book_id) REFERENCES book (id) ON DELETE CASCADE,
|
||||
CONSTRAINT fk_book_author_mapping_author FOREIGN KEY (author_id) REFERENCES author (id),
|
||||
CONSTRAINT unique_book_author UNIQUE (book_id, author_id)
|
||||
CONSTRAINT unique_book_author UNIQUE (book_id, author_id),
|
||||
INDEX idx_book_id (book_id),
|
||||
INDEX idx_author_id (author_id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS book_viewer_setting
|
||||
(
|
||||
book_id BIGINT PRIMARY KEY,
|
||||
page_number INT DEFAULT 1,
|
||||
zoom VARCHAR(32) DEFAULT 'page-fit',
|
||||
zoom VARCHAR(16) DEFAULT 'page-fit',
|
||||
sidebar_visible BOOLEAN DEFAULT false,
|
||||
spread VARCHAR(32) DEFAULT 'off',
|
||||
spread VARCHAR(16) DEFAULT 'odd',
|
||||
CONSTRAINT fk_book_viewer_setting FOREIGN KEY (book_id) REFERENCES book (id) ON DELETE CASCADE
|
||||
);
|
||||
Reference in New Issue
Block a user