diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/exception/GifticonParseException.java b/amatta_server/src/main/java/com/amatta/amatta_server/exception/GifticonParseException.java new file mode 100644 index 0000000..908fd56 --- /dev/null +++ b/amatta_server/src/main/java/com/amatta/amatta_server/exception/GifticonParseException.java @@ -0,0 +1,4 @@ +package com.amatta.amatta_server.exception; + +public class GifticonParseException extends RuntimeException { +} diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/controller/FCMController.java b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/controller/FCMController.java index f5463b5..b76dd47 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/controller/FCMController.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/controller/FCMController.java @@ -6,10 +6,9 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; -import java.util.Map; - @RestController @CrossOrigin(origins = {"https://amatta.site", "http://localhost:5173"}, allowCredentials = "true") @RequestMapping("/fcm") @@ -36,4 +35,9 @@ public ResponseEntity sendTestMessage(@RequestParam("token") String token) { } return new ResponseEntity<>(HttpStatus.OK); } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e) { + return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST); + } } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/dto/TokenRegisterDto.java b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/dto/TokenRegisterDto.java index afc32e3..7daa34c 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/dto/TokenRegisterDto.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/dto/TokenRegisterDto.java @@ -6,14 +6,12 @@ @Getter public class TokenRegisterDto { - @NotEmpty + @NotEmpty(message = "토큰이 존재해야 합니다") private String token; public TokenRegisterDto(String token) { this.token = token; } - public TokenRegisterDto() { - - } + public TokenRegisterDto(){} } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/repository/DeviceTokenRepository.java b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/repository/DeviceTokenRepository.java index 2f2b6f1..22e4f86 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/fcm/repository/DeviceTokenRepository.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/fcm/repository/DeviceTokenRepository.java @@ -29,4 +29,8 @@ void addToken(@Param("uid") long uid, @Query("SELECT token FROM device_token WHERE uid IN " + "(SELECT uid FROM gifticon WHERE expiresat BETWEEN (SELECT now()) AND :expirationThresholdDays AND usedat > (SELECT now()))") List findTokensByUidsOfGifticonsAboutToExpire(@Param("expirationThresholdDays") LocalDate date); + + @Modifying + @Query("DELETE FROM device_token WHERE uid = :uid") + void deleteTokenByUid(@Param("uid") long uid); } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/controller/GifticonController.java b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/controller/GifticonController.java index a9a226f..36184c9 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/controller/GifticonController.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/controller/GifticonController.java @@ -2,6 +2,7 @@ import com.amatta.amatta_server.exception.DuplicateGifticonException; import com.amatta.amatta_server.exception.GifticonNotSupportedException; +import com.amatta.amatta_server.exception.GifticonParseException; import com.amatta.amatta_server.exception.NotAuthenticatedException; import com.amatta.amatta_server.fcm.service.FCMService; import com.amatta.amatta_server.gifticon.dto.GifticonUseDto; @@ -70,12 +71,12 @@ public ResponseEntity notAuthenticatedExceptionHandler() { @ExceptionHandler(GifticonNotSupportedException.class) public ResponseEntity gifticonNotSupportedExceptionHandler() { - return new ResponseEntity<>(GifticonNotSupportedException.message, HttpStatus.BAD_REQUEST); + return new ResponseEntity<>("", HttpStatus.OK); } - @ExceptionHandler(IndexOutOfBoundsException.class) - public ResponseEntity indexOutOfBoundExceptionHandler() { - return new ResponseEntity<>("잘못된 형식의 이미지입니다", HttpStatus.BAD_REQUEST); + @ExceptionHandler(GifticonParseException.class) + public ResponseEntity gifticonParseExceptionHandler() { + return new ResponseEntity<>("", HttpStatus.OK); } @ExceptionHandler(DuplicateGifticonException.class) diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/enums/GifticonMapperEnum.java b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/enums/GifticonMapperEnum.java index 9a8f74a..c3cebe8 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/enums/GifticonMapperEnum.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/enums/GifticonMapperEnum.java @@ -10,11 +10,8 @@ public enum GifticonMapperEnum { KAKAO(KakaoGifticonMapper.getInstance()) { @Override public boolean matches(List texts) { - Pattern pattern = Pattern.compile("(?i)kakaotalk"); - for(String text : texts) { - if(pattern.matcher(text).matches()) { - return true; - } + if(texts.get(texts.size()-1).matches("(?i)^kakaotalk.*")) { + return true; } return false; } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/service/GifticonService.java b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/service/GifticonService.java index e60f97e..5533812 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/service/GifticonService.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/service/GifticonService.java @@ -3,6 +3,7 @@ import com.amatta.amatta_server.aop.ClassRequiresAuth; import com.amatta.amatta_server.exception.DuplicateGifticonException; import com.amatta.amatta_server.exception.GifticonNotSupportedException; +import com.amatta.amatta_server.exception.GifticonParseException; import com.amatta.amatta_server.exception.NotAuthenticatedException; import com.amatta.amatta_server.gifticon.dto.GifticonDto; import com.amatta.amatta_server.gifticon.dto.GifticonImageDto; @@ -65,7 +66,7 @@ public ResponseEntity extractGifticonText(GifticonImageDto dto) { ); } - public Gifticon mapTextToGifticon(GifticonTextDto dto) throws NullPointerException, IndexOutOfBoundsException, GifticonNotSupportedException { + public Gifticon mapTextToGifticon(GifticonTextDto dto) throws GifticonParseException, GifticonNotSupportedException { GifticonMapper mapper = GifticonMapperFactory.getGifticonMapper(dto.getTexts()); return mapper.map(dto.getTexts()); } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/KakaoGifticonMapper.java b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/KakaoGifticonMapper.java index 58bf765..8c0975c 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/KakaoGifticonMapper.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/KakaoGifticonMapper.java @@ -1,5 +1,6 @@ package com.amatta.amatta_server.gifticon.util; +import com.amatta.amatta_server.exception.GifticonParseException; import com.amatta.amatta_server.gifticon.model.Gifticon; import java.sql.Date; @@ -22,66 +23,30 @@ public static GifticonMapper getInstance() { } @Override - public Gifticon map(List list) throws IndexOutOfBoundsException { - int brandNameIndex = list.lastIndexOf("교환처"); - int expirationDateIndex = list.lastIndexOf("유효기간"); - int orderIdIndex = list.lastIndexOf("주문번호"); - - String brandName = list.get(brandNameIndex+1); - String itemName = getItemName(list, brandName); - String barcode = getBarcode(list, brandNameIndex); - - java.sql.Date expiretionDate = Date.valueOf(getExpirationDate(list, expirationDateIndex, orderIdIndex)); - - return Gifticon.builder() - .brandname(brandName) - .expiresat(expiretionDate) - .itemname(itemName) - .barcode(barcode) - .build(); - } - - private String getBarcode(List list, int brandNameIndex) { - int barcodeStartIdx = 0; - for(int i = brandNameIndex - 1; i >= 0; i--) { - if(list.get(i).matches("^\\d{4}$")) { - barcodeStartIdx = i; + public Gifticon map(List list) throws GifticonParseException { + try { + Date expireDate = Date.valueOf(LocalDate.parse(list.get(list.size() - 3) + .replace("유효기간", "").replace(" ", ""), DateTimeFormatter.ofPattern("yyyy년MM월dd일"))); + String brandName = list.get(list.size() - 4).replace("교환처", ""); + String barcode = list.get(list.size() - 5).replace(" ", ""); + + StringBuilder stringBuilder = new StringBuilder(); + for (int i = list.size() - 6; !list.get(i).equals(brandName); i--) { + if (i == 0 && !list.get(i).equals(brandName)) { + stringBuilder.delete(0, stringBuilder.toString().length()); + break; + } + stringBuilder.insert(0, list.get(i)); } - if(!list.get(i).matches("^\\d{4}$")) { - break; - } - } - StringBuilder st = new StringBuilder(); - for(int i = barcodeStartIdx; i < brandNameIndex; i++) { - st.append(list.get(i)); + return Gifticon.builder() + .brandname(brandName) + .expiresat(expireDate) + .itemname(stringBuilder.toString()) + .barcode(barcode) + .build(); + } catch(Exception e) { + throw new GifticonParseException(); } - return st.toString(); - } - - private LocalDate getExpirationDate(List list, int expirationDateIndex, int orderIdIndex) throws IndexOutOfBoundsException { - StringBuilder expirationDate = new StringBuilder(); - for(String date : list.subList(expirationDateIndex+1, orderIdIndex)) { - expirationDate.append(date); - } - - return LocalDate.parse(expirationDate.toString(), DateTimeFormatter.ofPattern("yyyy년MM월dd일")); - } - - private String getItemName(List list, String brandName) throws IndexOutOfBoundsException { - int itemNameStartIndex = list.indexOf(brandName) + 1; - int itemNameEndIndex = 0; - for(int i = list.lastIndexOf(brandName) - 2; i >= 0; i--) { - if(!list.get(i).matches("^\\d{4}$")) { - itemNameEndIndex = i+1; - break; - } - } - StringBuilder itemName = new StringBuilder(); - for(String name : list.subList(itemNameStartIndex, itemNameEndIndex)) { - itemName.append(name).append(" "); - } - - return itemName.toString().trim(); } } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/NoMatchMapper.java b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/NoMatchMapper.java index 1e2e8a2..9898ead 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/NoMatchMapper.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/gifticon/util/NoMatchMapper.java @@ -19,7 +19,7 @@ public static GifticonMapper getInstance() { return instance; } @Override - public Gifticon map(List list) throws IndexOutOfBoundsException, GifticonNotSupportedException { + public Gifticon map(List list) throws GifticonNotSupportedException { throw new GifticonNotSupportedException(); } } diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/user/controller/UserController.java b/amatta_server/src/main/java/com/amatta/amatta_server/user/controller/UserController.java index 2d22bf8..a3ab460 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/user/controller/UserController.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/user/controller/UserController.java @@ -9,6 +9,8 @@ import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.*; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; @@ -64,6 +66,7 @@ public ResponseEntity login(@Valid @RequestBody UserLoginReq userLoginReq, Ht public ResponseEntity logout(HttpServletRequest request) { HttpSession session = request.getSession(false); if (session != null) { + userService.logout(); session.invalidate(); } return new ResponseEntity<>(new UserLogoutRes(true), HttpStatus.OK); diff --git a/amatta_server/src/main/java/com/amatta/amatta_server/user/service/UserService.java b/amatta_server/src/main/java/com/amatta/amatta_server/user/service/UserService.java index 1322060..dd77b34 100644 --- a/amatta_server/src/main/java/com/amatta/amatta_server/user/service/UserService.java +++ b/amatta_server/src/main/java/com/amatta/amatta_server/user/service/UserService.java @@ -1,11 +1,17 @@ package com.amatta.amatta_server.user.service; +import com.amatta.amatta_server.fcm.repository.DeviceTokenRepository; import com.amatta.amatta_server.user.dto.*; import com.amatta.amatta_server.user.model.Users; import com.amatta.amatta_server.user.repository.UserRepository; import org.mindrot.jbcrypt.BCrypt; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import java.util.Objects; @Service @@ -13,10 +19,14 @@ public class UserService { private final UserRepository userRepository; + private final DeviceTokenRepository tokenRepository; + private final MailService mailService; - public UserService(UserRepository userRepository, MailService mailService) { + @Autowired + public UserService(UserRepository userRepository, DeviceTokenRepository tokenRepository, MailService mailService) { this.userRepository = userRepository; + this.tokenRepository = tokenRepository; this.mailService = mailService; } @@ -49,6 +59,22 @@ public Users login(UserLoginReq userLoginReq) { return null; } + public void logout() { + HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest(); + HttpSession session = request.getSession(false); + + if(session == null) { + return; + } + + Users user = (Users) session.getAttribute("User"); + + if(user == null) { + return; + } + tokenRepository.deleteTokenByUid(user.getId()); + } + public UserFindEmailRes findEmail(UserFindEmailReq userFindEmailReq) { Users users = userRepository.findByNameAndPhoneNum(userFindEmailReq.getName(), userFindEmailReq.getPhoneNumber()); if (Objects.nonNull(users)) { diff --git a/amatta_server/src/test/java/com/amatta/amatta_server/DatabaseTest.java b/amatta_server/src/test/java/com/amatta/amatta_server/DatabaseTest.java index 7e5666f..a838ed4 100644 --- a/amatta_server/src/test/java/com/amatta/amatta_server/DatabaseTest.java +++ b/amatta_server/src/test/java/com/amatta/amatta_server/DatabaseTest.java @@ -55,10 +55,10 @@ void token_by_expiration_date_test() { LocalDate now = LocalDate.now(); userRepository.addUser("testest@naver.com", "testest1234", "테스트", "010-1111-1111"); long id = userRepository.last_insert_id(); - gifticonRepository.addGifticon(id, "test".getBytes(), "test".getBytes(), "test brand", "test item", "12341234", + gifticonRepository.addGifticon(id, "test", "test", "test brand", "test item", "12341234", now.plusDays(2), now.plusYears(100), 2000); tokenRepository.addToken(id, "testtoken"); List list = tokenRepository.findTokensByUidsOfGifticonsAboutToExpire(now.plusDays(Gifticon.expirationThresholdDays)); assertNotEquals(0, list.size()); } -} +} \ No newline at end of file diff --git a/amatta_server/src/test/java/com/amatta/amatta_server/user/UserControllerTest.java b/amatta_server/src/test/java/com/amatta/amatta_server/user/UserControllerTest.java index 6c92422..1359321 100644 --- a/amatta_server/src/test/java/com/amatta/amatta_server/user/UserControllerTest.java +++ b/amatta_server/src/test/java/com/amatta/amatta_server/user/UserControllerTest.java @@ -1,5 +1,9 @@ package com.amatta.amatta_server.user; +import com.amatta.amatta_server.fcm.controller.FCMController; +import com.amatta.amatta_server.fcm.dto.TokenRegisterDto; +import com.amatta.amatta_server.fcm.model.FCMToken; +import com.amatta.amatta_server.fcm.repository.DeviceTokenRepository; import com.amatta.amatta_server.user.controller.UserController; import com.amatta.amatta_server.user.dto.UserChangePasswordReq; import com.amatta.amatta_server.user.dto.UserFindPasswordByEmailReq; @@ -8,6 +12,7 @@ import com.amatta.amatta_server.user.repository.UserRepository; import com.amatta.amatta_server.user.service.UserService; import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -17,9 +22,13 @@ import org.springframework.http.MediaType; import org.springframework.mock.web.MockHttpSession; import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.MethodArgumentNotValidException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; @@ -34,10 +43,14 @@ public class UserControllerTest { @Autowired UserController userController; @Autowired + FCMController fcmController; + @Autowired UserService userService; @Autowired UserRepository userRepository; @Autowired + DeviceTokenRepository tokenRepository; + @Autowired MockMvc mockMvc; @Autowired ObjectMapper objectMapper; @@ -339,6 +352,58 @@ void changePassword() throws Exception { .andExpect(jsonPath("success").value(true)) .andExpect(status().isOk()) .andDo(print()); + } + + @Test + @Transactional + @DisplayName("로그아웃 테스트") + void logoutTest() throws Exception { + String email = "ktykty0722@naver.com"; + String password = "testPassword"; + String name = "taewan"; + String phoneNumber = "010-0000-0000"; + UserJoinReq userJoinReq = new UserJoinReq( + email, + password, + name, + phoneNumber + ); + mockMvc.perform(post("/user/join") + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userJoinReq))) + .andExpect(status().isCreated()) + .andDo(print()); + + long id = userRepository.last_insert_id(); + + UserLoginReq userLoginReq = new UserLoginReq(email, password); + MockHttpSession httpSession = new MockHttpSession(); + + mockMvc.perform(post("/user/login") + .session(httpSession) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(userLoginReq))) + .andExpect(jsonPath("success").value(true)) + .andExpect(status().isOk()) + .andDo(print()); + + String requestBody = "{\"token\": \"testtoken\"}"; + + mockMvc.perform(post("/fcm/token") + .session(httpSession) + .contentType(MediaType.APPLICATION_JSON) + .content(requestBody) + ).andExpect(status().is2xxSuccessful()).andDo(print()); + + List list = tokenRepository.findByUid(id); + + assertEquals(list.size(), 1); + + mockMvc.perform(post("/user/logout") + .session(httpSession)).andExpect(status().is2xxSuccessful()); + + List list2 = tokenRepository.findByUid(id); + assertEquals(list2.size(), 0); } }