diff --git a/src/main/java/ru/mcs/sopds/controller/SettingsController.java b/src/main/java/ru/mcs/sopds/controller/SettingsController.java new file mode 100644 index 0000000..2a6f1c8 --- /dev/null +++ b/src/main/java/ru/mcs/sopds/controller/SettingsController.java @@ -0,0 +1,84 @@ +package ru.mcs.sopds.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.*; +import ru.mcs.sopds.service.SettingsService; + +import java.util.HashMap; +import java.util.Map; + +@Controller +@RequestMapping("/settings") +@RequiredArgsConstructor +public class SettingsController { + + private final SettingsService settingsService; + + @GetMapping + public String settingsPage(Model model) { + model.addAttribute("appTitle", "Настройки - SOPDS"); + model.addAttribute("breadcrumbs", new String[]{"Настройки"}); + model.addAttribute("current", "settings"); + + // Передаем все настройки в модель + Map settings = settingsService.getAllSettings(); + model.addAttribute("settings", settings); + + return "settings"; + } + + @PostMapping("/save") + public String saveSettings(@RequestParam Map params, Model model) { + try { + Map settingsToUpdate = new HashMap<>(); + + // Обрабатываем чекбоксы (они приходят только если отмечены) + boolean alphabetMenu = params.containsKey("SOPDS_ALPHABET_MENU"); + boolean scanEnabled = params.containsKey("SOPDS_SCAN_ENABLED"); + + settingsToUpdate.put("SOPDS_ALPHABET_MENU", alphabetMenu); + settingsToUpdate.put("SOPDS_SCAN_ENABLED", scanEnabled); + + // Обрабатываем текстовые поля + if (params.containsKey("SOPDS_SCAN_PATH")) { + settingsToUpdate.put("SOPDS_SCAN_PATH", params.get("SOPDS_SCAN_PATH")); + } + + if (params.containsKey("SOPDS_PAGE_SIZE")) { + try { + int pageSize = Integer.parseInt(params.get("SOPDS_PAGE_SIZE")); + settingsToUpdate.put("SOPDS_PAGE_SIZE", pageSize); + } catch (NumberFormatException e) { + // Оставляем предыдущее значение при ошибке + } + } + + // Сохраняем настройки + settingsService.updateSettings(settingsToUpdate); + + model.addAttribute("systemMessage", Map.of( + "type", "success", + "title", "Успех", + "text", "Настройки успешно сохранены!" + )); + + } catch (Exception e) { + model.addAttribute("systemMessage", Map.of( + "type", "alert", + "title", "Ошибка", + "text", "Произошла ошибка при сохранении настроек: " + e.getMessage() + )); + } + + return settingsPage(model); + } + + @GetMapping("/alphabet/toggle") + public String toggleAlphabetMenu() { + boolean current = settingsService.getAlphabetMenu(); + settingsService.setAlphabetMenu(!current); + return "redirect:/"; + } +} \ No newline at end of file diff --git a/src/main/java/ru/mcs/sopds/controller/WebController.java b/src/main/java/ru/mcs/sopds/controller/WebController.java index 68abfd2..3b2731c 100644 --- a/src/main/java/ru/mcs/sopds/controller/WebController.java +++ b/src/main/java/ru/mcs/sopds/controller/WebController.java @@ -10,6 +10,7 @@ import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.*; +import ru.mcs.sopds.service.SettingsService; import java.util.Arrays; import java.util.HashMap; @@ -21,6 +22,7 @@ public class WebController { private final BookService bookService; + private final SettingsService settingsService; @GetMapping("/") public String mainPage(Model model, HttpServletRequest request) { @@ -28,7 +30,19 @@ model.addAttribute("appTitle", "Simple OPDS Catalog"); model.addAttribute("breadcrumbs", Arrays.asList("Главная")); model.addAttribute("currentPath", request.getRequestURI()); - model.addAttribute("request", request); + + // Получаем настройку алфавитного меню + boolean alphabetMenu = settingsService.getAlphabetMenu(); + model.addAttribute("alphabet", alphabetMenu); + model.addAttribute("current", "catalog"); + + // Языковое меню + Map langMenu = createLangMenu(); + model.addAttribute("langMenu", langMenu); + + // Заглушка для пользователя + User demoUser = createDemoUser(); + model.addAttribute("user", demoUser); // Статистика Map stats = new HashMap<>(); @@ -49,8 +63,24 @@ @RequestParam(defaultValue = "0") int page, @RequestParam(required = false) Long genreId, @RequestParam(required = false) String format, - @RequestParam(required = false) String lang, - Model model) { + @RequestParam(required = false) Integer lang, + Model model, + HttpServletRequest request) { + + model.addAttribute("currentPath", request.getRequestURI()); + model.addAttribute("current", "book"); + + // Получаем настройку алфавитного меню + boolean alphabetMenu = settingsService.getAlphabetMenu(); + model.addAttribute("alphabet", alphabetMenu); + + // Языковое меню + Map langMenu = createLangMenu(); + model.addAttribute("langMenu", langMenu); + + // Заглушка пользователя + User demoUser = createDemoUser(); + model.addAttribute("user", demoUser); Pageable pageable = PageRequest.of(page, 20); Page books; @@ -62,6 +92,12 @@ books = bookService.getAllBooks(pageable); } + // Фильтрация по языку, если указан + if (lang != null && lang > 0) { + String langCode = lang == 1 ? "ru" : "en"; + books = bookService.getBooksByLanguage(langCode, pageable); + } + model.addAttribute("books", books); model.addAttribute("allGenres", bookService.getAllGenres()); model.addAttribute("formats", List.of("fb2", "epub", "pdf", "mobi", "djvu")); @@ -69,7 +105,6 @@ model.addAttribute("format", format); model.addAttribute("lang", lang); model.addAttribute("pageUrl", "/books"); - model.addAttribute("topGenres", bookService.getPopularGenres(5)); return "books"; } @@ -78,7 +113,22 @@ public String authors( @RequestParam(defaultValue = "0") int page, @RequestParam(required = false) String letter, - Model model) { + @RequestParam(required = false) Integer lang, + @RequestParam(required = false) Boolean alphabet, + Model model, + HttpServletRequest request) { + + model.addAttribute("currentPath", request.getRequestURI()); + model.addAttribute("current", "author"); + model.addAttribute("alphabet", alphabet != null ? alphabet : false); + + // Языковое меню + Map langMenu = createLangMenu(); + model.addAttribute("langMenu", langMenu); + + // Заглушка пользователя + User demoUser = createDemoUser(); + model.addAttribute("user", demoUser); Pageable pageable = PageRequest.of(page, 100); Page authors; @@ -89,34 +139,87 @@ authors = bookService.getAllAuthors(pageable); } + // Фильтрация по языку, если указан + if (lang != null && lang > 0) { + String langCode = lang == 1 ? "ru" : "en"; + authors = bookService.getAuthorsByLanguage(langCode, pageable); + } + model.addAttribute("authors", authors); model.addAttribute("letter", letter); - model.addAttribute("topGenres", bookService.getPopularGenres(5)); + model.addAttribute("lang", lang); return "authors"; } - @GetMapping("/genres") - public String genres(Model model) { - model.addAttribute("genres", bookService.getAllGenres()); - model.addAttribute("topGenres", bookService.getPopularGenres(5)); - return "genres"; - } - @GetMapping("/series") public String series( @RequestParam(defaultValue = "0") int page, - Model model) { + @RequestParam(required = false) Integer lang, + @RequestParam(required = false) Boolean alphabet, + Model model, + HttpServletRequest request) { + + model.addAttribute("currentPath", request.getRequestURI()); + model.addAttribute("current", "series"); + model.addAttribute("alphabet", alphabet != null ? alphabet : true); + + // Языковое меню + Map langMenu = createLangMenu(); + model.addAttribute("langMenu", langMenu); + + // Заглушка пользователя + User demoUser = createDemoUser(); + model.addAttribute("user", demoUser); Pageable pageable = PageRequest.of(page, 50); Page series = bookService.getAllSeries(pageable); + // Фильтрация по языку, если указан + if (lang != null && lang > 0) { + String langCode = lang == 1 ? "ru" : "en"; + series = bookService.getSeriesByLanguage(langCode, pageable); + } + model.addAttribute("series", series); - model.addAttribute("topGenres", bookService.getPopularGenres(5)); + model.addAttribute("lang", lang); return "series"; } + // Вспомогательные методы + private Map createLangMenu() { + Map langMenu = new HashMap<>(); + langMenu.put(0, "Все языки"); + langMenu.put(1, "Русский"); + langMenu.put(2, "Английский"); + return langMenu; + } + + private User createDemoUser() { + User demoUser = new User(); + demoUser.setUsername("admin"); + demoUser.setIsSuperuser(true); + return demoUser; + } + + @GetMapping("/catalog") + public String catalog( + @RequestParam(required = false) Boolean alphabet, + Model model, + HttpServletRequest request) { + + model.addAttribute("currentPath", request.getRequestURI()); + model.addAttribute("current", "catalog"); + model.addAttribute("alphabet", alphabet != null ? alphabet : false); + + // Заглушка пользователя + User demoUser = createDemoUser(); + model.addAttribute("user", demoUser); + + return "catalog"; + } + @GetMapping("/book/{id}") public String bookDetail(@PathVariable Long id, Model model) { Book book = bookService.getBookById(id); diff --git a/src/main/java/ru/mcs/sopds/repository/AuthorRepository.java b/src/main/java/ru/mcs/sopds/repository/AuthorRepository.java index f5527be..6e5bb77 100644 --- a/src/main/java/ru/mcs/sopds/repository/AuthorRepository.java +++ b/src/main/java/ru/mcs/sopds/repository/AuthorRepository.java @@ -1,5 +1,7 @@ package ru.mcs.sopds.repository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import ru.mcs.sopds.entity.Author; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -12,4 +14,6 @@ public interface AuthorRepository extends JpaRepository { Page findByFullNameStartingWith(String letter, Pageable pageable); Optional findByFullName(String fullName); + @Query("SELECT DISTINCT a FROM Author a JOIN a.books b WHERE b.lang = :lang") + Page findByBooksLang(@Param("lang") String lang, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/ru/mcs/sopds/repository/BookRepository.java b/src/main/java/ru/mcs/sopds/repository/BookRepository.java index 7eb1a6a..18bbf79 100644 --- a/src/main/java/ru/mcs/sopds/repository/BookRepository.java +++ b/src/main/java/ru/mcs/sopds/repository/BookRepository.java @@ -25,4 +25,6 @@ Page findByTitleContainingIgnoreCase(String title, Pageable pageable); Optional findByFileNameAndFileSize(String fileName, Long fileSize); + + Page findByLang(String lang, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/ru/mcs/sopds/repository/SeriesRepository.java b/src/main/java/ru/mcs/sopds/repository/SeriesRepository.java index 1d652ff..179b560 100644 --- a/src/main/java/ru/mcs/sopds/repository/SeriesRepository.java +++ b/src/main/java/ru/mcs/sopds/repository/SeriesRepository.java @@ -1,5 +1,9 @@ package ru.mcs.sopds.repository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; import ru.mcs.sopds.entity.Series; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @@ -9,4 +13,6 @@ @Repository public interface SeriesRepository extends JpaRepository { Optional findBySeries(String series); + @Query("SELECT DISTINCT s FROM Series s JOIN s.books b WHERE b.lang = :lang") + Page findByBooksLang(@Param("lang") String lang, Pageable pageable); } \ No newline at end of file diff --git a/src/main/java/ru/mcs/sopds/service/BookScannerService.java b/src/main/java/ru/mcs/sopds/service/BookScannerService.java index bdb848a..70debdb 100644 --- a/src/main/java/ru/mcs/sopds/service/BookScannerService.java +++ b/src/main/java/ru/mcs/sopds/service/BookScannerService.java @@ -26,23 +26,26 @@ private final AuthorRepository authorRepository; private final GenreRepository genreRepository; private final SeriesRepository seriesRepository; + private final SettingsService settingsService; - @Value("${sopds.scanner.path:books}") - private String scanPath; + private String getScanPath() { + return (String) settingsService.getSetting("SOPDS_SCAN_PATH"); + } - @Value("${sopds.scanner.enabled:true}") - private boolean scannerEnabled; + private boolean isScannerEnabled() { + return (boolean) settingsService.getSetting("SOPDS_SCAN_ENABLED"); + } private final Set SUPPORTED_FORMATS = Set.of("fb2", "epub", "pdf", "mobi", "djvu"); @Transactional public void scanBooks() { - if (!scannerEnabled) { + if (!isScannerEnabled()) { log.info("Сканер отключен в настройках"); return; } - Path booksPath = Paths.get(scanPath); + Path booksPath = Paths.get(getScanPath()); if (!Files.exists(booksPath)) { log.warn("Директория для сканирования не существует: {}", booksPath.toAbsolutePath()); return; diff --git a/src/main/java/ru/mcs/sopds/service/BookService.java b/src/main/java/ru/mcs/sopds/service/BookService.java index 326ec20..d44129e 100644 --- a/src/main/java/ru/mcs/sopds/service/BookService.java +++ b/src/main/java/ru/mcs/sopds/service/BookService.java @@ -98,4 +98,16 @@ public Page searchBooks(String query, Pageable pageable) { return bookRepository.findByTitleContainingIgnoreCase(query, pageable); } + + public Page getBooksByLanguage(String lang, Pageable pageable) { + return bookRepository.findByLang(lang, pageable); + } + + public Page getAuthorsByLanguage(String lang, Pageable pageable) { + return authorRepository.findByBooksLang(lang, pageable); + } + + public Page getSeriesByLanguage(String lang, Pageable pageable) { + return seriesRepository.findByBooksLang(lang, pageable); + } } \ No newline at end of file diff --git a/src/main/java/ru/mcs/sopds/service/SettingsService.java b/src/main/java/ru/mcs/sopds/service/SettingsService.java new file mode 100644 index 0000000..35d4616 --- /dev/null +++ b/src/main/java/ru/mcs/sopds/service/SettingsService.java @@ -0,0 +1,44 @@ +package ru.mcs.sopds.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +public class SettingsService { + + private final Map settings = new HashMap<>(); + + public SettingsService() { + // Инициализация настроек по умолчанию + settings.put("SOPDS_ALPHABET_MENU", true); + settings.put("SOPDS_SCAN_ENABLED", true); + settings.put("SOPDS_SCAN_PATH", "books"); + settings.put("SOPDS_PAGE_SIZE", 20); + } + + public boolean getAlphabetMenu() { + return (boolean) settings.getOrDefault("SOPDS_ALPHABET_MENU", true); + } + + public void setAlphabetMenu(boolean enabled) { + settings.put("SOPDS_ALPHABET_MENU", enabled); + log.info("SOPDS_ALPHABET_MENU установлен в: {}", enabled); + } + + public Map getAllSettings() { + return new HashMap<>(settings); + } + + public void updateSettings(Map newSettings) { + settings.putAll(newSettings); + log.info("Настройки обновлены: {}", newSettings); + } + + public Object getSetting(String key) { + return settings.get(key); + } +} \ No newline at end of file diff --git a/src/main/resources/templates/authors.html b/src/main/resources/templates/authors.html index 76a9985..ff45b1a 100644 --- a/src/main/resources/templates/authors.html +++ b/src/main/resources/templates/authors.html @@ -25,6 +25,17 @@ +
+

+ Фильтр по языку: + + Все языки + Русский + Английский + + Сбросить фильтр +

+

Авторы

diff --git a/src/main/resources/templates/books.html b/src/main/resources/templates/books.html index 3aa0170..ead80f8 100644 --- a/src/main/resources/templates/books.html +++ b/src/main/resources/templates/books.html @@ -6,6 +6,17 @@ Книги - SOPDS +
+

+ Фильтр по языку: + + Все языки + Русский + Английский + + Сбросить фильтр +

+

diff --git a/src/main/resources/templates/catalog.html b/src/main/resources/templates/catalog.html new file mode 100644 index 0000000..7a21a61 --- /dev/null +++ b/src/main/resources/templates/catalog.html @@ -0,0 +1,20 @@ + + + + Каталоги - SOPDS + + +
+
+
+

Каталоги

+
+

Функционал каталогов находится в разработке.

+
+
+
+
+ + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/menu.html b/src/main/resources/templates/fragments/menu.html index 1cceca7..e9777a3 100644 --- a/src/main/resources/templates/fragments/menu.html +++ b/src/main/resources/templates/fragments/menu.html @@ -2,47 +2,74 @@
- + +
+ +
+ + +
+ +
\ No newline at end of file diff --git a/src/main/resources/templates/fragments/top.html b/src/main/resources/templates/fragments/top.html index 5cc1b3b..d5ca678 100644 --- a/src/main/resources/templates/fragments/top.html +++ b/src/main/resources/templates/fragments/top.html @@ -20,8 +20,8 @@ Выйти (user) -
  • - +
  • + Настройки
  • diff --git a/src/main/resources/templates/main.html b/src/main/resources/templates/main.html index 556d743..eadc690 100644 --- a/src/main/resources/templates/main.html +++ b/src/main/resources/templates/main.html @@ -45,11 +45,11 @@

    -
    -
    - -
    -
    + + + + +
    diff --git a/src/main/resources/templates/settings.html b/src/main/resources/templates/settings.html new file mode 100644 index 0000000..79d3ff3 --- /dev/null +++ b/src/main/resources/templates/settings.html @@ -0,0 +1,218 @@ + + + + Настройки - SOPDS + + + +
    +
    +
    +

    Настройки системы

    + + +
    + + +
    +

    Настройки интерфейса

    + +
    +
    + SOPDS_ALPHABET_MENU +
    + Включить выпадающее меню с выбором языка для книг, авторов и серий +
    +
    +
    + +
    +
    + +
    +
    + Размер страницы +
    + Количество элементов на одной странице +
    +
    +
    + +
    +
    +
    + + +
    +

    Настройки сканера

    + +
    +
    + SOPDS_SCAN_ENABLED +
    + Включить автоматическое сканирование книг +
    +
    +
    + +
    +
    + +
    +
    + Путь для сканирования +
    + Директория для поиска книг +
    +
    +
    + +
    +
    +
    + + +
    +
    +
    + + + Отмена + +
    + +
    +
    + +
    + + +
    +

    Текущие настройки

    +
    +                        Настройки
    +                    
    +
    +
    +
    +
    + + \ No newline at end of file