From 13fd6b176a7d6217552dab25df3eee636a85f424 Mon Sep 17 00:00:00 2001 From: "Kayashov.SM" Date: Tue, 26 Aug 2025 01:19:27 +0400 Subject: [PATCH] =?UTF-8?q?=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D1=8B=20=D1=81=D0=BE=D0=B1=D1=8B=D1=82=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- front/src/components/cocktails/Cocktail.js | 6 +- front/src/lib/clients/CocktailClient.js | 6 ++ front/src/requests.js | 1 + .../bar/controller/AuthController.java | 56 ++++++++++++------- .../bar/controller/CocktailController.java | 7 ++- .../bar/model/entity/CocktailEntity.java | 2 + .../ru/kayashov/bar/model/entity/Event.java | 32 +++++++++++ .../kayashov/bar/model/entity/EventType.java | 23 ++++++++ .../bar/repository/EventRepository.java | 7 +++ .../kayashov/bar/security/SecurityConfig.java | 1 + .../ru/kayashov/bar/service/BarService.java | 12 +++- .../kayashov/bar/service/CocktailService.java | 19 +++++++ .../ru/kayashov/bar/service/EventService.java | 23 ++++++++ 13 files changed, 172 insertions(+), 23 deletions(-) create mode 100644 src/main/java/ru/kayashov/bar/model/entity/Event.java create mode 100644 src/main/java/ru/kayashov/bar/model/entity/EventType.java create mode 100644 src/main/java/ru/kayashov/bar/repository/EventRepository.java create mode 100644 src/main/java/ru/kayashov/bar/service/EventService.java diff --git a/front/src/components/cocktails/Cocktail.js b/front/src/components/cocktails/Cocktail.js index 113fd81..a66df72 100644 --- a/front/src/components/cocktails/Cocktail.js +++ b/front/src/components/cocktails/Cocktail.js @@ -13,6 +13,7 @@ import LocalBarIcon from '@mui/icons-material/LocalBar'; import {paths} from "../../path"; import {useAlert} from "../../hooks/useAlert"; import {useUser} from "../../hooks/useUser"; +import {cocktailClient} from "../../lib/clients/CocktailClient"; function renderFavouriteBadge(handleFavourite, row) { const childIcon = row.rating.favourite ? : ; @@ -35,7 +36,7 @@ function renderRating(handleChangeRating, row) { } export function Cocktail({row, handleFavourite, handleChangeRating, handleSelect, deleteHandler}) { - const {notImplement} = useAlert(); + const {createError, createSuccess} = useAlert(); const {user} = useUser(); return ( @@ -59,7 +60,8 @@ export function Cocktail({row, handleFavourite, handleChangeRating, handleSelect image={row.image.includes("thecocktaildb") ? (row.image + "/preview") : row.image} /> - notImplement()}> + cocktailClient.drinkCocktail(row.id, createSuccess, createError)}> {renderFavouriteBadge(handleFavourite, row)} diff --git a/front/src/lib/clients/CocktailClient.js b/front/src/lib/clients/CocktailClient.js index f684c18..57111e2 100644 --- a/front/src/lib/clients/CocktailClient.js +++ b/front/src/lib/clients/CocktailClient.js @@ -196,6 +196,12 @@ class CocktailClient { }).catch(() => createError("Ошибка сохранения")) } + drinkCocktail(id, createSuccess, createError) { + api().post(`${requests.cocktails.drink}/${id}`) + .then(() => createSuccess("Бон аппетит")) + .catch(() => createError("Ошибка отметки коктейля")) + } + } export const cocktailClient = new CocktailClient(); \ No newline at end of file diff --git a/front/src/requests.js b/front/src/requests.js index 4776c9d..18fabd4 100644 --- a/front/src/requests.js +++ b/front/src/requests.js @@ -37,6 +37,7 @@ export const requests = { modal: routes.cocktail + "/modal?id=", favourite: routes.cocktail + "/favourite?id=", rating: routes.cocktail + "/rating?id=", + drink: routes.cocktail + "/drink", }, glass: { list: routes.glass, diff --git a/src/main/java/ru/kayashov/bar/controller/AuthController.java b/src/main/java/ru/kayashov/bar/controller/AuthController.java index 0edd3bf..f5a8abd 100644 --- a/src/main/java/ru/kayashov/bar/controller/AuthController.java +++ b/src/main/java/ru/kayashov/bar/controller/AuthController.java @@ -13,13 +13,17 @@ import org.springframework.web.bind.annotation.RestController; import ru.kayashov.bar.controller.dto.AuthRequestDto; import ru.kayashov.bar.controller.dto.AuthResponseDto; import ru.kayashov.bar.controller.dto.VisitorResponseDto; +import ru.kayashov.bar.model.entity.Event; +import ru.kayashov.bar.model.entity.EventType; import ru.kayashov.bar.model.entity.Visitor; import ru.kayashov.bar.repository.VisitorsRepository; import ru.kayashov.bar.security.JwtTokenProvider; +import ru.kayashov.bar.service.EventService; import ru.kayashov.bar.service.VisitorService; import javax.annotation.security.PermitAll; import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; import java.util.Base64; import java.util.Optional; @@ -33,15 +37,43 @@ public class AuthController { private final VisitorsRepository visitorsRepository; private final PasswordEncoder passwordEncoder; private final VisitorService visitorService; + private final EventService eventService; @PermitAll @PostMapping("/login") public AuthResponseDto checkTelegramChat(@RequestBody AuthRequestDto dto) { + AuthResponseDto responseDto; + Visitor visitor = null; if (dto.getByLogin()) { - return checkLogin(dto.getLogin(), dto.getPassword()); + String login = dto.getLogin(); + String password = dto.getPassword(); + if (login == null || login.isEmpty() || password == null || password.isEmpty()) { + return new AuthResponseDto(null, "Поля не могут быть пустые"); + } + + Optional visitorOpt = visitorsRepository.findByLogin(login); + if (visitorOpt.isEmpty()) { + responseDto = new AuthResponseDto(null, "Не найдет пользователь " + login); + } else { + visitor = visitorOpt.get(); + responseDto = checkLogin(visitor, dto.getPassword()); + } } else { - return parseCode(dto.getCode()); + String decode = new String(Base64.getDecoder().decode(dto.getCode()), StandardCharsets.UTF_8); + String[] decodeArr = decode.split(":"); + visitor = visitorsRepository.findById(Long.valueOf(decodeArr[0])) + .orElseThrow(); + responseDto = parseCode(Integer.valueOf(decodeArr[1]), visitor); } + + eventService.createEvent(Event.builder() + .date(LocalDateTime.now()) + .type(EventType.LOGIN) + .newState(visitor != null ? visitor.getName() : null) + .oldState(responseDto.getError() != null ? responseDto.getError() : "Выполнен вход") + .build()); + + return responseDto; } @PermitAll @@ -65,16 +97,7 @@ public class AuthController { return dto; } - private AuthResponseDto checkLogin(String login, String password) { - if (login == null || login.isEmpty() || password == null || password.isEmpty()) { - return new AuthResponseDto(null, "Поля не могут быть пустые"); - } - - Optional visitorOpt = visitorsRepository.findByLogin(login); - if (visitorOpt.isEmpty()) { - return new AuthResponseDto(null, "Не найдет пользователь " + login); - } - Visitor visitor = visitorOpt.get(); + private AuthResponseDto checkLogin(Visitor visitor, String password) { log.info("Попытка авторизации пользователя {}", visitor.getId()); if (passwordEncoder.matches(password, visitor.getPassword())) { return new AuthResponseDto(jwtTokenProvider.generateToken(visitor), null); @@ -83,18 +106,13 @@ public class AuthController { } } - private AuthResponseDto parseCode(String code) { - String decode = new String(Base64.getDecoder().decode(code), StandardCharsets.UTF_8); - String[] decodeArr = decode.split(":"); - Visitor visitor = visitorsRepository.findById(Long.valueOf(decodeArr[0])) - .orElseThrow(); - + private AuthResponseDto parseCode(Integer code, Visitor visitor) { log.info("Попытка авторизации пользователя {}", visitor.getId()); Integer visitorCode = visitor.getCode(); if (visitorCode == null) { return new AuthResponseDto(null, "Повторите запрос кода из бота"); } - if (Integer.valueOf(decodeArr[1]).equals(visitor.getCode())) { + if (code.equals(visitor.getCode())) { visitor.setCode(null); visitor = visitorsRepository.save(visitor); return new AuthResponseDto(jwtTokenProvider.generateToken(visitor), null); diff --git a/src/main/java/ru/kayashov/bar/controller/CocktailController.java b/src/main/java/ru/kayashov/bar/controller/CocktailController.java index 0fd5b77..2a3c579 100644 --- a/src/main/java/ru/kayashov/bar/controller/CocktailController.java +++ b/src/main/java/ru/kayashov/bar/controller/CocktailController.java @@ -7,6 +7,7 @@ import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; @@ -20,7 +21,6 @@ import ru.kayashov.bar.controller.dto.cocktail.CocktailForIngredientModalDto; import ru.kayashov.bar.controller.dto.cocktail.CocktailForListResponseDto; import ru.kayashov.bar.controller.dto.cocktail.CocktailModalDto; import ru.kayashov.bar.controller.dto.cocktail.CocktailSimpleResponseDto; -import ru.kayashov.bar.controller.dto.cocktail.ReceiptResponseDto; import ru.kayashov.bar.service.CocktailService; import javax.annotation.security.PermitAll; @@ -107,4 +107,9 @@ public class CocktailController { public void addRating(@RequestParam("id") Long id, @RequestParam("rating") Integer rating) { cocktailService.setRating(id, rating); } + + @PostMapping("/drink/{id}") + public void drinkCocktail(@PathVariable Long id) { + cocktailService.drink(id); + } } diff --git a/src/main/java/ru/kayashov/bar/model/entity/CocktailEntity.java b/src/main/java/ru/kayashov/bar/model/entity/CocktailEntity.java index b669138..03ab61d 100644 --- a/src/main/java/ru/kayashov/bar/model/entity/CocktailEntity.java +++ b/src/main/java/ru/kayashov/bar/model/entity/CocktailEntity.java @@ -54,6 +54,8 @@ public class CocktailEntity { @Column(name = "favourite") private Boolean isFavorite = false; + private Integer countDrink = 0; + @OneToMany(mappedBy = "cocktail", cascade = CascadeType.REMOVE) private List receipt; diff --git a/src/main/java/ru/kayashov/bar/model/entity/Event.java b/src/main/java/ru/kayashov/bar/model/entity/Event.java new file mode 100644 index 0000000..2727e15 --- /dev/null +++ b/src/main/java/ru/kayashov/bar/model/entity/Event.java @@ -0,0 +1,32 @@ +package ru.kayashov.bar.model.entity; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import javax.persistence.Entity; +import javax.persistence.EnumType; +import javax.persistence.Enumerated; +import javax.persistence.GeneratedValue; +import javax.persistence.GenerationType; +import javax.persistence.Id; +import java.time.LocalDateTime; + +@Entity +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Event { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private int id; + private LocalDateTime date; + private String oldState; + private String newState; + @Enumerated(EnumType.STRING) + private EventType type; +} diff --git a/src/main/java/ru/kayashov/bar/model/entity/EventType.java b/src/main/java/ru/kayashov/bar/model/entity/EventType.java new file mode 100644 index 0000000..4e65b47 --- /dev/null +++ b/src/main/java/ru/kayashov/bar/model/entity/EventType.java @@ -0,0 +1,23 @@ +package ru.kayashov.bar.model.entity; + +public enum EventType { + LOGIN, + DRINK, + COCKTAIL_CREATE, + COCKTAIL_EDIT, + COCKTAIL_DELETE, + COCKTAIL_UPLOAD_PHOTO, + INGREDIENT_CREATE, + INGREDIENT_EDIT, + INGREDIENT_DELETE, + INGREDIENT_ADD, + INGREDIENT_REMOVE, + INGREDIENT_UPLOAD_PHOTO, + BAR_CREATE, + BAR_DELETE, + BAR_CHANGE, + USER_CREATE, + USER_EDIT, + USER_UPLOAD_PHOTO + +} diff --git a/src/main/java/ru/kayashov/bar/repository/EventRepository.java b/src/main/java/ru/kayashov/bar/repository/EventRepository.java new file mode 100644 index 0000000..3ec61c8 --- /dev/null +++ b/src/main/java/ru/kayashov/bar/repository/EventRepository.java @@ -0,0 +1,7 @@ +package ru.kayashov.bar.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.kayashov.bar.model.entity.Event; + +public interface EventRepository extends JpaRepository { +} \ No newline at end of file diff --git a/src/main/java/ru/kayashov/bar/security/SecurityConfig.java b/src/main/java/ru/kayashov/bar/security/SecurityConfig.java index 562f0fa..03f6a4e 100644 --- a/src/main/java/ru/kayashov/bar/security/SecurityConfig.java +++ b/src/main/java/ru/kayashov/bar/security/SecurityConfig.java @@ -55,6 +55,7 @@ public class SecurityConfig { // Можно указать конкретный путь, * - 1 уровень вложенности, ** - любое количество уровней вложенности .antMatchers("/api/auth/**").permitAll() .antMatchers("/api/cocktail/menu").permitAll() + .antMatchers("/api/cocktail/drink/**").permitAll() .antMatchers("/api/category").permitAll() .antMatchers("/api/glass").permitAll() .antMatchers("/api/ingredient/simple").permitAll() diff --git a/src/main/java/ru/kayashov/bar/service/BarService.java b/src/main/java/ru/kayashov/bar/service/BarService.java index c31c660..f29dd57 100644 --- a/src/main/java/ru/kayashov/bar/service/BarService.java +++ b/src/main/java/ru/kayashov/bar/service/BarService.java @@ -7,12 +7,15 @@ import org.springframework.transaction.annotation.Transactional; import ru.kayashov.bar.controller.dto.bar.BarResponseDto; import ru.kayashov.bar.model.entity.BarEntity; import ru.kayashov.bar.model.entity.CocktailEntity; +import ru.kayashov.bar.model.entity.Event; +import ru.kayashov.bar.model.entity.EventType; import ru.kayashov.bar.model.entity.IngredientEntity; import ru.kayashov.bar.model.entity.ReceiptEntity; import ru.kayashov.bar.repository.BarEntityRepository; import ru.kayashov.bar.repository.CocktailRepository; import ru.kayashov.bar.repository.IngredientRepository; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Optional; @@ -25,8 +28,8 @@ public class BarService { private final BarEntityRepository barEntityRepository; private final IngredientRepository ingredientRepository; private final CocktailRepository cocktailRepository; + private final EventService eventService; -// @Transactional public void changeActiveBar(Long id) { Optional lastBarOpt = barEntityRepository.findByActiveTrue(); if (lastBarOpt.isPresent()) { @@ -55,6 +58,13 @@ public class BarService { cocktailRepository.saveAll(findAllowedCocktails(ingredients)); barEntityRepository.save(barEntity); + + eventService.createEvent(Event.builder() + .date(LocalDateTime.now()) + .type(EventType.BAR_CHANGE) + .newState(barEntity.getName()) + .oldState(lastBarOpt.map(BarEntity::getName).orElse(null)) + .build()); log.info("Бар {} подключен", barEntity.getName()); } diff --git a/src/main/java/ru/kayashov/bar/service/CocktailService.java b/src/main/java/ru/kayashov/bar/service/CocktailService.java index 9412e53..eacd719 100644 --- a/src/main/java/ru/kayashov/bar/service/CocktailService.java +++ b/src/main/java/ru/kayashov/bar/service/CocktailService.java @@ -20,6 +20,8 @@ import ru.kayashov.bar.mapper.CocktailMapper; import ru.kayashov.bar.model.entity.Alcoholic; import ru.kayashov.bar.model.entity.Category; import ru.kayashov.bar.model.entity.CocktailEntity; +import ru.kayashov.bar.model.entity.Event; +import ru.kayashov.bar.model.entity.EventType; import ru.kayashov.bar.model.entity.Glass; import ru.kayashov.bar.model.entity.IngredientEntity; import ru.kayashov.bar.model.entity.ReceiptEntity; @@ -37,6 +39,7 @@ import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import java.io.File; import java.io.IOException; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -48,6 +51,7 @@ import java.util.Objects; public class CocktailService { private final CocktailRepository cocktailRepository; + private final EventService eventService; @Value("${cocktail.photo.path}") private String photoFolder; @@ -294,4 +298,19 @@ public class CocktailService { } return sb.toString(); } + + public void drink(Long id) { + CocktailEntity cocktail = repository.findById(id).orElseThrow(RuntimeException::new); + int count = cocktail.getCountDrink() + 1; + cocktail.setCountDrink(count); + repository.save(cocktail); + + eventService.createEvent(Event.builder() + .type(EventType.DRINK) + .date(LocalDateTime.now()) + .oldState(cocktail.getName()) + .newState(String.valueOf(count)) + .build()); + + } } diff --git a/src/main/java/ru/kayashov/bar/service/EventService.java b/src/main/java/ru/kayashov/bar/service/EventService.java new file mode 100644 index 0000000..474f79b --- /dev/null +++ b/src/main/java/ru/kayashov/bar/service/EventService.java @@ -0,0 +1,23 @@ +package ru.kayashov.bar.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.kayashov.bar.model.entity.Event; +import ru.kayashov.bar.repository.EventRepository; + +import java.util.concurrent.ExecutorService; + +@Slf4j +@Service +@RequiredArgsConstructor +public class EventService { + + private final EventRepository eventRepository; + private final ExecutorService executorService; + + public void createEvent(Event event) { + executorService.submit(() -> {eventRepository.save(event);}); + } + +}