diff --git a/build.gradle b/build.gradle index a207494..8657b4d 100644 --- a/build.gradle +++ b/build.gradle @@ -28,7 +28,9 @@ repositories { } dependencies { - //redis + // thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + // redis implementation 'org.springframework.boot:spring-boot-starter-data-redis' // sms implementation 'net.nurigo:sdk:4.2.7' diff --git a/script/start.sh b/script/start.sh index 46ca59e..b75467b 100644 --- a/script/start.sh +++ b/script/start.sh @@ -15,7 +15,7 @@ cp $PROJECT_ROOT/build/libs/*.jar $JAR_FILE # jar 파일 실행 echo "$TIME_NOW > $JAR_FILE 파일 실행" >> $DEPLOY_LOG -nohup java -jar $JAR_FILE > $APP_LOG 2> $ERROR_LOG & +nohup java -jar -Duser.timezone=Asia/Seoul $JAR_FILE > $APP_LOG 2> $ERROR_LOG & CURRENT_PID=$(pgrep -f $JAR_FILE) echo "$TIME_NOW > 실행된 프로세스 아이디 $CURRENT_PID 입니다." >> $DEPLOY_LOG \ No newline at end of file diff --git a/src/main/java/com/softeer/podo/admin/model/entity/LotsUser.java b/src/main/java/com/softeer/podo/admin/model/entity/LotsUser.java index 5e74a5d..1e7cd4e 100644 --- a/src/main/java/com/softeer/podo/admin/model/entity/LotsUser.java +++ b/src/main/java/com/softeer/podo/admin/model/entity/LotsUser.java @@ -2,6 +2,7 @@ import com.softeer.podo.common.entity.DateEntity; import com.softeer.podo.event.model.entity.LotsComment; +import com.softeer.podo.event.model.entity.TestResult; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Builder; @@ -21,6 +22,11 @@ public class LotsUser extends DateEntity { @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "user_id") private Long id; + + @ManyToOne + @JoinColumn(name = "result_id") + private TestResult testResult; + private String name; @Column(name = "phone_number", unique = true) private String phoneNum; @@ -29,4 +35,5 @@ public class LotsUser extends DateEntity { private Role role; @OneToOne(mappedBy = "lotsUser", orphanRemoval = true, cascade = CascadeType.ALL) private LotsComment lotsComment; + } diff --git a/src/main/java/com/softeer/podo/admin/repository/LotsUserRepository.java b/src/main/java/com/softeer/podo/admin/repository/LotsUserRepository.java index 1e7cde4..22077f8 100644 --- a/src/main/java/com/softeer/podo/admin/repository/LotsUserRepository.java +++ b/src/main/java/com/softeer/podo/admin/repository/LotsUserRepository.java @@ -3,7 +3,9 @@ import com.softeer.podo.admin.model.entity.LotsUser; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; + public interface LotsUserRepository extends JpaRepository { - LotsUser findByNameAndPhoneNum(String name, String phoneNum); + Optional findByPhoneNum(String phoneNum); boolean existsByPhoneNum(String phoneNum); } diff --git a/src/main/java/com/softeer/podo/common/response/ErrorCode.java b/src/main/java/com/softeer/podo/common/response/ErrorCode.java index b82a586..8c111a1 100644 --- a/src/main/java/com/softeer/podo/common/response/ErrorCode.java +++ b/src/main/java/com/softeer/podo/common/response/ErrorCode.java @@ -29,6 +29,11 @@ public enum ErrorCode { INVALID_SELECTION_ERROR(false, HttpStatus.BAD_REQUEST.value(), "선택지 형식이 잘못되었습니다."), USER_NOT_EXIST_ERROR(false, HttpStatus.BAD_REQUEST.value(), "해당 사용자가 아직 이벤트에 응모하지 않았습니다."), EXISTING_COMMENT_ERROR(false, HttpStatus.BAD_REQUEST.value(),"이미 기대평을 작성했습니다."), + LOTS_LINK_NOT_EXISTS_ERROR(false, HttpStatus.INTERNAL_SERVER_ERROR.value(),"공유 링크가 정상적으로 생성되지 않았습니다."), + INVALID_RESULT_TYPE_ERROR(false, HttpStatus.INTERNAL_SERVER_ERROR.value(),"존재하지 않는 드라이버 유형입니다."), + + //etc + AES_ENC_DEC_ERROR(false, HttpStatus.INTERNAL_SERVER_ERROR.value(),"공유링크 생성중 오류가 발생했습니다."), //admin EVENT_NOT_FOUND_ERROR(false, HttpStatus.INTERNAL_SERVER_ERROR.value(),"해당 이벤트를 찾을 수 없습니다."), diff --git a/src/main/java/com/softeer/podo/common/utils/AESUtils.java b/src/main/java/com/softeer/podo/common/utils/AESUtils.java new file mode 100644 index 0000000..eb00727 --- /dev/null +++ b/src/main/java/com/softeer/podo/common/utils/AESUtils.java @@ -0,0 +1,48 @@ +package com.softeer.podo.common.utils; + +import jakarta.annotation.PostConstruct; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Base64; + +@Component +public class AESUtils { + private static final String ALGORITHM = "AES"; + + @Value("${secret.jwt}") + private String baseSecretKey; + + private static String STATIC_BASE_SECRET_KEY; + + @PostConstruct + public void init() { + STATIC_BASE_SECRET_KEY = this.baseSecretKey; + } + + public static String encrypt(String data) throws Exception { + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.ENCRYPT_MODE, makeSecretKeyByString(STATIC_BASE_SECRET_KEY)); + byte[] encryptedData = cipher.doFinal(data.getBytes()); + return Base64.getEncoder().encodeToString(encryptedData); + } + + public static String decrypt(String encryptedData) throws Exception { + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, makeSecretKeyByString(STATIC_BASE_SECRET_KEY)); + byte[] decryptedData = cipher.doFinal(Base64.getDecoder().decode(encryptedData)); + return new String(decryptedData); + } + + // 특정 문자열을 기반으로 SecretKey 객체를 생성하는 메서드 + public static SecretKey makeSecretKeyByString(String password) { + byte[] keyBytes = password.getBytes(StandardCharsets.UTF_8); + keyBytes = Arrays.copyOf(keyBytes, 16); // 128비트(16바이트)로 맞추기 + return new SecretKeySpec(keyBytes, ALGORITHM); + } +} diff --git a/src/main/java/com/softeer/podo/common/utils/URLUtils.java b/src/main/java/com/softeer/podo/common/utils/URLUtils.java new file mode 100644 index 0000000..36ce5b8 --- /dev/null +++ b/src/main/java/com/softeer/podo/common/utils/URLUtils.java @@ -0,0 +1,42 @@ +package com.softeer.podo.common.utils; + +import org.springframework.stereotype.Component; + +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; + +@Component +public class URLUtils { + + /** + * 주어진 문자열을 URL 인코딩합니다. + * + * @param value 인코딩할 문자열 + * @return URL 인코딩된 문자열 + */ + public static String encode(String value) throws UnsupportedEncodingException { + return URLEncoder.encode(value, StandardCharsets.UTF_8); + } + + /** + * 주어진 URL 인코딩된 문자열을 디코딩합니다. + * + * @param value 디코딩할 문자열 + * @return URL 디코딩된 문자열 + * @throws UnsupportedEncodingException 디코딩이 지원되지 않는 경우 발생 + */ + public static String decode(String value) throws UnsupportedEncodingException { + return URLDecoder.decode(value, StandardCharsets.UTF_8); + } + + public static void main(String[] args) { + try { + String uniqueLink = "V1a1Vrqe1oBYCkqkXs7/Uw=="; + String encodedLink = encode(uniqueLink); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/softeer/podo/event/controller/EventLotsApiController.java b/src/main/java/com/softeer/podo/event/controller/EventLotsApiController.java new file mode 100644 index 0000000..48f3682 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/controller/EventLotsApiController.java @@ -0,0 +1,30 @@ +package com.softeer.podo.event.controller; + +import com.softeer.podo.common.response.CommonResponse; +import com.softeer.podo.event.model.dto.LotsTypeRequestDto; +import com.softeer.podo.event.model.dto.LotsTypeResponseDto; +import com.softeer.podo.event.service.EventLotsService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RequestMapping("/lots") +@RestController +@RequiredArgsConstructor +public class EventLotsApiController { + + private final EventLotsService eventLotsService; + + /** + * 제출한 유형테스트 결과에 따라 적절한 드라이버 유형 반환 + */ + @PostMapping("/type") + @Operation(summary = "제출한 유형테스트 결과에 따라 적절한 드라이버 유형 반환") + public CommonResponse getDriverType(@Valid @RequestBody LotsTypeRequestDto dto) { + return new CommonResponse<>(eventLotsService.getProperDriverType(dto)); + } +} diff --git a/src/main/java/com/softeer/podo/event/controller/EventLotsController.java b/src/main/java/com/softeer/podo/event/controller/EventLotsController.java index 1feff31..2e0e638 100644 --- a/src/main/java/com/softeer/podo/event/controller/EventLotsController.java +++ b/src/main/java/com/softeer/podo/event/controller/EventLotsController.java @@ -1,6 +1,5 @@ package com.softeer.podo.event.controller; - import com.softeer.podo.common.response.CommonResponse; import com.softeer.podo.event.model.dto.LotsApplicationRequestDto; import com.softeer.podo.event.model.dto.LotsApplicationResponseDto; @@ -12,28 +11,34 @@ import io.swagger.v3.oas.annotations.Operation; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.web.bind.annotation.*; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; -@RestController @RequestMapping("/v1/lots") +@RestController @RequiredArgsConstructor -@Slf4j public class EventLotsController { private final EventLotsService eventLotsService; - @PostMapping("/application") - @Operation(summary = "유형테스트 결과 제출 Api") - public CommonResponse eventApplication(@Auth AuthInfo authInfo, - @Valid @RequestBody LotsApplicationRequestDto dto) { - return new CommonResponse<>(eventLotsService.applyLotsEvent(authInfo, dto)); - } - @PostMapping("/comment") @Operation(summary = "랜덤추천이벤트 기대평 등록용 Api") - public CommonResponse eventComment(@Auth AuthInfo authInfo, - @Valid @RequestBody LotsCommentRequestDto dto) { - return new CommonResponse<>(eventLotsService.comment(authInfo, dto)); + public CommonResponse eventComment( + @Auth AuthInfo authInfo, + @Valid @RequestBody LotsCommentRequestDto dto + ) { + return new CommonResponse<>(eventLotsService.registerComment(authInfo, dto)); } + + @PostMapping("/application") + @Operation(summary = "랜덤추첨 이벤트 응모하기") + public CommonResponse applyEvent( + @Auth AuthInfo authInfo, + @Valid @RequestBody LotsApplicationRequestDto dto + ) { + return new CommonResponse<>(eventLotsService.applyEvent(authInfo, dto)); + } + } diff --git a/src/main/java/com/softeer/podo/event/controller/EventLotsPageController.java b/src/main/java/com/softeer/podo/event/controller/EventLotsPageController.java new file mode 100644 index 0000000..89047c5 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/controller/EventLotsPageController.java @@ -0,0 +1,43 @@ +package com.softeer.podo.event.controller; + +import com.softeer.podo.event.service.EventLotsService; +import io.swagger.v3.oas.annotations.Operation; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; + +@RequestMapping("/lots") +@Controller +@RequiredArgsConstructor +public class EventLotsPageController { + + private final EventLotsService eventLotsService; + + /** + * 고유 공유링크 클릭 + * {uniqueLink}: 유저 id (암호화) + */ + @GetMapping("/link/{uniqueLink}") + @Operation(summary = "공유링크 클릭시 redirection하기 위함 (사용자 직접 호출용)") + public String shareLinkClick( + HttpServletResponse response, + @PathVariable String uniqueLink + ) { + try { + // 해당 유저에 해당하는 적절한 이벤트 결과 페이지 찾기 + String redirectUrl = eventLotsService.getEventUrl(uniqueLink); + // 이벤트 결과 페이지 반환 +// response.setStatus(HttpServletResponse.SC_FOUND); +// response.setHeader("Location", "/type/"+redirectUrl); + return "/type/"+redirectUrl; + } catch (Exception e) { + // 에러 페이지로 리다이렉션 +// response.setStatus(HttpServletResponse.SC_FOUND); +// response.setHeader("Location", "/error/404"); + return "/error/404"; + } + } +} diff --git a/src/main/java/com/softeer/podo/event/exception/AESExecutionException.java b/src/main/java/com/softeer/podo/event/exception/AESExecutionException.java new file mode 100644 index 0000000..c24662b --- /dev/null +++ b/src/main/java/com/softeer/podo/event/exception/AESExecutionException.java @@ -0,0 +1,11 @@ +package com.softeer.podo.event.exception; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class AESExecutionException extends RuntimeException { + private String message; +} diff --git a/src/main/java/com/softeer/podo/event/exception/EventExceptionHandler.java b/src/main/java/com/softeer/podo/event/exception/EventExceptionHandler.java index 2670f1f..78851c8 100644 --- a/src/main/java/com/softeer/podo/event/exception/EventExceptionHandler.java +++ b/src/main/java/com/softeer/podo/event/exception/EventExceptionHandler.java @@ -17,28 +17,49 @@ public class EventExceptionHandler { @ExceptionHandler(ExistingUserException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public CommonResponse existingPhoneNumbException(ExistingUserException e, HttpServletRequest request) { - log.warn("LOTSAPPLICATION-001> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + log.warn("LOTS-001> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); return new CommonResponse<>(ErrorCode.PHONENUM_EXIST_ERROR); } @ExceptionHandler(InvalidSelectionException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public CommonResponse invalidSelectionException(InvalidSelectionException e, HttpServletRequest request) { - log.warn("LOTSAPPLICATION-002> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + log.warn("LOTS-002> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); return new CommonResponse<>(ErrorCode.INVALID_SELECTION_ERROR); } @ExceptionHandler(UserNotExistException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public CommonResponse userNotExistException(UserNotExistException e, HttpServletRequest request) { - log.warn("LOTSAPPLICATION-003> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + log.warn("LOTS-003> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); return new CommonResponse<>(ErrorCode.USER_NOT_EXIST_ERROR); } @ExceptionHandler(ExistingCommentException.class) @ResponseStatus(HttpStatus.BAD_REQUEST) public CommonResponse existingCommentException(ExistingCommentException e, HttpServletRequest request) { - log.warn("LOTSAPPLICATION-004> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + log.warn("LOTS-004> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); return new CommonResponse<>(ErrorCode.EXISTING_COMMENT_ERROR); } + + @ExceptionHandler(LotsShareLinkNotExistsException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public CommonResponse lotsShareLinkNotExistsException(LotsShareLinkNotExistsException e, HttpServletRequest request) { + log.warn("LOTS-005> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + return new CommonResponse<>(ErrorCode.LOTS_LINK_NOT_EXISTS_ERROR); + } + + @ExceptionHandler(AESExecutionException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public CommonResponse AESExecutionException(AESExecutionException e, HttpServletRequest request) { + log.warn("LOTS-006> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + return new CommonResponse<>(ErrorCode.AES_ENC_DEC_ERROR); + } + + @ExceptionHandler(InvalidResultTypeException.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public CommonResponse invalidResultTypeException(InvalidResultTypeException e, HttpServletRequest request) { + log.warn("LOTS-007> 요청 URI: " + request.getRequestURI() + ", 에러 메세지: " + e.getMessage()); + return new CommonResponse<>(ErrorCode.INVALID_RESULT_TYPE_ERROR); + } } diff --git a/src/main/java/com/softeer/podo/event/exception/InvalidResultTypeException.java b/src/main/java/com/softeer/podo/event/exception/InvalidResultTypeException.java new file mode 100644 index 0000000..8fecc97 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/exception/InvalidResultTypeException.java @@ -0,0 +1,11 @@ +package com.softeer.podo.event.exception; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class InvalidResultTypeException extends RuntimeException { + private String message; +} diff --git a/src/main/java/com/softeer/podo/event/exception/LotsShareLinkNotExistsException.java b/src/main/java/com/softeer/podo/event/exception/LotsShareLinkNotExistsException.java new file mode 100644 index 0000000..3b56081 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/exception/LotsShareLinkNotExistsException.java @@ -0,0 +1,11 @@ +package com.softeer.podo.event.exception; + + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class LotsShareLinkNotExistsException extends RuntimeException { + private String message; +} diff --git a/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationRequestDto.java b/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationRequestDto.java index 60c63ae..01e1434 100644 --- a/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationRequestDto.java +++ b/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationRequestDto.java @@ -5,23 +5,11 @@ import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; -import lombok.Setter; @Getter -@Setter @NoArgsConstructor @AllArgsConstructor public class LotsApplicationRequestDto { @NotBlank - private String answer1; - @NotBlank - private String answer2; - @NotBlank - private String answer3; - @NotBlank - private String answer4; - - public String getSelection(){ - return answer1 + answer2 + answer3 + answer4; - } + private Long resultTypeId; } diff --git a/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationResponseDto.java b/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationResponseDto.java index 8a59859..5a078ee 100644 --- a/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationResponseDto.java +++ b/src/main/java/com/softeer/podo/event/model/dto/LotsApplicationResponseDto.java @@ -1,23 +1,15 @@ package com.softeer.podo.event.model.dto; -import com.softeer.podo.event.model.entity.TestResult; -import com.softeer.podo.event.util.Result; +import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; -import lombok.Builder; -import lombok.Data; +import lombok.Getter; import lombok.NoArgsConstructor; -import java.util.ArrayList; -import java.util.List; - -@Data -@AllArgsConstructor +@Getter @NoArgsConstructor +@AllArgsConstructor public class LotsApplicationResponseDto { - private Result result; - - private String type; // 유형 - private String description; // 유형 설명 문구 - private ArrayList senarioList; + @NotBlank + private String uniqueLink; } diff --git a/src/main/java/com/softeer/podo/event/model/dto/LotsTypeRequestDto.java b/src/main/java/com/softeer/podo/event/model/dto/LotsTypeRequestDto.java new file mode 100644 index 0000000..ca3527f --- /dev/null +++ b/src/main/java/com/softeer/podo/event/model/dto/LotsTypeRequestDto.java @@ -0,0 +1,27 @@ +package com.softeer.podo.event.model.dto; + + +import jakarta.validation.constraints.NotBlank; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class LotsTypeRequestDto { + @NotBlank + private String answer1; + @NotBlank + private String answer2; + @NotBlank + private String answer3; + @NotBlank + private String answer4; + + public String getSelection(){ + return answer1 + answer2 + answer3 + answer4; + } +} diff --git a/src/main/java/com/softeer/podo/event/model/dto/LotsTypeResponseDto.java b/src/main/java/com/softeer/podo/event/model/dto/LotsTypeResponseDto.java new file mode 100644 index 0000000..9845df4 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/model/dto/LotsTypeResponseDto.java @@ -0,0 +1,20 @@ +package com.softeer.podo.event.model.dto; + + +import com.softeer.podo.event.util.Result; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.ArrayList; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class LotsTypeResponseDto { + private Long resultId; + private Result result; + private String type; // 유형 + private String description; // 유형 설명 문구 + private ArrayList scenarioList; +} diff --git a/src/main/java/com/softeer/podo/event/model/dto/SenarioDto.java b/src/main/java/com/softeer/podo/event/model/dto/ScenarioDto.java similarity index 88% rename from src/main/java/com/softeer/podo/event/model/dto/SenarioDto.java rename to src/main/java/com/softeer/podo/event/model/dto/ScenarioDto.java index 1e99c8f..5fe7355 100644 --- a/src/main/java/com/softeer/podo/event/model/dto/SenarioDto.java +++ b/src/main/java/com/softeer/podo/event/model/dto/ScenarioDto.java @@ -5,7 +5,7 @@ @Data @AllArgsConstructor -public class SenarioDto { +public class ScenarioDto { private String title; private String subtitle; private String image; diff --git a/src/main/java/com/softeer/podo/event/model/dto/mapper/LotsEventMapper.java b/src/main/java/com/softeer/podo/event/model/dto/mapper/LotsEventMapper.java index b64448f..42d1330 100644 --- a/src/main/java/com/softeer/podo/event/model/dto/mapper/LotsEventMapper.java +++ b/src/main/java/com/softeer/podo/event/model/dto/mapper/LotsEventMapper.java @@ -1,7 +1,7 @@ package com.softeer.podo.event.model.dto.mapper; -import com.softeer.podo.event.model.dto.LotsApplicationResponseDto; -import com.softeer.podo.event.model.dto.SenarioDto; +import com.softeer.podo.event.model.dto.LotsTypeResponseDto; +import com.softeer.podo.event.model.dto.ScenarioDto; import com.softeer.podo.event.model.entity.TestResult; import org.springframework.stereotype.Component; @@ -10,17 +10,18 @@ @Component public class LotsEventMapper { - public LotsApplicationResponseDto TestResultToApplicationDto (TestResult testResult) { - LotsApplicationResponseDto dto = new LotsApplicationResponseDto(); + public LotsTypeResponseDto TestResultToApplicationDto (TestResult testResult) { + LotsTypeResponseDto dto = new LotsTypeResponseDto(); + dto.setResultId(testResult.getId()); dto.setResult(testResult.getResult()); dto.setDescription(testResult.getDescription()); dto.setType(testResult.getType()); - ArrayList senarioArrayList = new ArrayList<>(); - senarioArrayList.add( new SenarioDto(testResult.getSenario1(), testResult.getSubtitle1(), testResult.getImage1())); - senarioArrayList.add( new SenarioDto(testResult.getSenario2(), testResult.getSubtitle2(), testResult.getImage2())); - senarioArrayList.add( new SenarioDto(testResult.getSenario3(), testResult.getSubtitle3(), testResult.getImage3())); - dto.setSenarioList(senarioArrayList); + ArrayList scenarioArrayList = new ArrayList<>(); + scenarioArrayList.add( new ScenarioDto(testResult.getScenario1(), testResult.getSubtitle1(), testResult.getImage1())); + scenarioArrayList.add( new ScenarioDto(testResult.getScenario2(), testResult.getSubtitle2(), testResult.getImage2())); + scenarioArrayList.add( new ScenarioDto(testResult.getScenario3(), testResult.getSubtitle3(), testResult.getImage3())); + dto.setScenarioList(scenarioArrayList); return dto; } } diff --git a/src/main/java/com/softeer/podo/event/model/entity/LotsShareLink.java b/src/main/java/com/softeer/podo/event/model/entity/LotsShareLink.java new file mode 100644 index 0000000..6ed88f9 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/model/entity/LotsShareLink.java @@ -0,0 +1,32 @@ +package com.softeer.podo.event.model.entity; + +import com.softeer.podo.admin.model.entity.LotsUser; +import com.softeer.podo.common.entity.DateEntity; +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Entity +@Getter +@Table(name = "event_lots_share_links") +@AllArgsConstructor +@NoArgsConstructor +public class LotsShareLink extends DateEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "link_id") + private Long id; + + @OneToOne + @JoinColumn(name = "user_id") + private LotsUser lotsUser; + + private Long count; + private String shareLink; + + public void increaseCount() { + this.count++; + } +} diff --git a/src/main/java/com/softeer/podo/event/model/entity/TestResult.java b/src/main/java/com/softeer/podo/event/model/entity/TestResult.java index fdc51fc..60c8815 100644 --- a/src/main/java/com/softeer/podo/event/model/entity/TestResult.java +++ b/src/main/java/com/softeer/podo/event/model/entity/TestResult.java @@ -28,10 +28,11 @@ public class TestResult { private String subtitle1; //소제목 private String subtitle2; private String subtitle3; - private String senario1; // 시나리오 - private String senario2; - private String senario3; + private String scenario1; // 시나리오 + private String scenario2; + private String scenario3; private String image1; // 이미지 s3 링크 private String image2; private String image3; + private String url; } diff --git a/src/main/java/com/softeer/podo/event/repository/LotsShareLinkRepository.java b/src/main/java/com/softeer/podo/event/repository/LotsShareLinkRepository.java new file mode 100644 index 0000000..71fca91 --- /dev/null +++ b/src/main/java/com/softeer/podo/event/repository/LotsShareLinkRepository.java @@ -0,0 +1,11 @@ +package com.softeer.podo.event.repository; + +import com.softeer.podo.admin.model.entity.LotsUser; +import com.softeer.podo.event.model.entity.LotsShareLink; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface LotsShareLinkRepository extends JpaRepository { + Optional findByLotsUser(LotsUser user); +} diff --git a/src/main/java/com/softeer/podo/event/service/EventLotsService.java b/src/main/java/com/softeer/podo/event/service/EventLotsService.java index 41e3340..27a6d94 100644 --- a/src/main/java/com/softeer/podo/event/service/EventLotsService.java +++ b/src/main/java/com/softeer/podo/event/service/EventLotsService.java @@ -1,66 +1,103 @@ package com.softeer.podo.event.service; -import com.softeer.podo.event.exception.ExistingCommentException; -import com.softeer.podo.event.exception.ExistingUserException; -import com.softeer.podo.event.exception.UserNotExistException; -import com.softeer.podo.event.model.dto.LotsApplicationRequestDto; -import com.softeer.podo.event.model.dto.LotsApplicationResponseDto; -import com.softeer.podo.event.model.dto.LotsCommentRequestDto; -import com.softeer.podo.event.model.dto.LotsCommentResponseDto; +import com.softeer.podo.admin.model.entity.Role; +import com.softeer.podo.common.utils.AESUtils; +import com.softeer.podo.common.utils.URLUtils; +import com.softeer.podo.event.exception.*; +import com.softeer.podo.event.model.dto.*; import com.softeer.podo.event.model.dto.mapper.LotsEventMapper; import com.softeer.podo.event.model.entity.LotsComment; +import com.softeer.podo.event.model.entity.LotsShareLink; import com.softeer.podo.event.model.entity.TestResult; import com.softeer.podo.event.repository.LotsCommentRepository; +import com.softeer.podo.event.repository.LotsShareLinkRepository; import com.softeer.podo.event.repository.TestResultRepository; import com.softeer.podo.event.util.Result; import com.softeer.podo.event.util.SelectionMap; import com.softeer.podo.security.AuthInfo; import com.softeer.podo.admin.model.entity.LotsUser; import com.softeer.podo.admin.repository.LotsUserRepository; -import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class EventLotsService { + private final LotsUserRepository lotsUserRepository; - private final SelectionMap selectionMap; private final TestResultRepository testResultRepository; private final LotsCommentRepository lotsCommentRepository; + private final LotsShareLinkRepository lotsShareLinkRepository; + private final SelectionMap selectionMap; private final LotsEventMapper lotsEventMapper; + @Value("${server.host}") + private String SERVER_HOST; + @Value("${server.port}") + private String SERVER_PORT; + + /** - * 랜덤추천 이벤트 응모 api - * @param authInfo 사용자 토큰 정보 + * 랜덤추천 이벤트에서 적절한 드라이버 타입 반환 * @param dto 선택지 정보 * @return 유형테스트 결과 */ - @Transactional - public LotsApplicationResponseDto applyLotsEvent(AuthInfo authInfo, LotsApplicationRequestDto dto) { + @Transactional(readOnly = true) + public LotsTypeResponseDto getProperDriverType(LotsTypeRequestDto dto) { + // 유형 선택 + Result result = selectionMap.getResult(dto.getSelection()); + TestResult testResult = testResultRepository.findByResult(result); - if(lotsUserRepository.existsByPhoneNum(authInfo.getPhoneNum())){ - throw new ExistingUserException("이미 존재하는 전화번호입니다."); - } + return lotsEventMapper.TestResultToApplicationDto(testResult); + } - Result result = selectionMap.getResult(dto.getSelection()); + /** + * 랜덤 추첨 이벤트 응모 후 공유링크 반환 + * @param dto 드라이버 타입 + * @return 공유링크 + */ + @Transactional + public LotsApplicationResponseDto applyEvent(AuthInfo authInfo, LotsApplicationRequestDto dto) { + if(lotsUserRepository.existsByPhoneNum(authInfo.getPhoneNum())){ + throw new ExistingUserException("이미 이벤트에 응모한 유저입니다."); + } - LotsUser lotsUser = LotsUser.builder() - .name(authInfo.getName()) - .phoneNum(authInfo.getPhoneNum()) - .role(authInfo.getRole()) - .reward("") - .build(); + // 유형 찾기 + TestResult testResult = testResultRepository.findById(dto.getResultTypeId()) + .orElseThrow(() -> new InvalidResultTypeException("잘못된 Result Type 아이디입니다.")); + + // 유저 저장 + LotsUser savedUser = lotsUserRepository.save( + LotsUser.builder() + .name(authInfo.getName()) + .phoneNum(authInfo.getPhoneNum()) + .role(Role.ROLE_USER) + .testResult(testResult) + .build() + ); + + // 고유 링크 생성 + String uniqueLink; + try { + uniqueLink = createUniqueLink(savedUser.getId()); + } catch (Exception e) { + throw new AESExecutionException("userId {"+savedUser.getId()+"} 암호화 과정 중 오류가 발생했습니다."); + } - lotsUserRepository.save(lotsUser); + // 공유정보 생성 + lotsShareLinkRepository.save( + new LotsShareLink(null, savedUser, 0L, uniqueLink) + ); - TestResult testResult = testResultRepository.findByResult(result); - return lotsEventMapper.TestResultToApplicationDto(testResult); + return new LotsApplicationResponseDto(uniqueLink); } + /** * 랜덤추천 이벤트 응모자의 기대평을 등록 * @param authInfo 사용자 정보 @@ -68,18 +105,19 @@ public LotsApplicationResponseDto applyLotsEvent(AuthInfo authInfo, LotsApplicat * @return 등록결과 */ @Transactional - public LotsCommentResponseDto comment(AuthInfo authInfo, LotsCommentRequestDto dto) { - //사용자가 이벤트에 아직 응모하지 않았을때 + public LotsCommentResponseDto registerComment(AuthInfo authInfo, LotsCommentRequestDto dto) { + // 사용자가 이벤트에 아직 응모하지 않았을때 if(!lotsUserRepository.existsByPhoneNum(authInfo.getPhoneNum())){ throw new UserNotExistException("해당 사용자가 아직 이벤트에 응모하지 않았습니다."); } - // 여기에 comment 내용 체크용 로직 구현 + // TODO("여기에 comment 내용 체크용 로직 구현") - LotsUser lotsUser = lotsUserRepository.findByNameAndPhoneNum(authInfo.getName(), authInfo.getPhoneNum()); + LotsUser lotsUser = lotsUserRepository.findByPhoneNum(authInfo.getPhoneNum()) + .orElseThrow(() -> new ExistingUserException("이미 이벤트에 응모한 유저입니다.")); - //이미 comment가 존재할때 - if(lotsCommentRepository.existsByLotsUser(lotsUser)){ + // 이미 comment가 존재할때 + if(lotsCommentRepository.existsByLotsUser(lotsUser)) { throw new ExistingCommentException("이미 기대평을 작성했습니다."); } @@ -89,7 +127,33 @@ public LotsCommentResponseDto comment(AuthInfo authInfo, LotsCommentRequestDto d .build(); lotsCommentRepository.save(comment); - return new LotsCommentResponseDto(comment); } + + + /** + * 사용자의 고유 링크를 받고 복호화한 후 유형 링크 반환 + * @param uniqueLink 사용자의 고유 링크 + * @return 유형 페이지 url + */ + @Transactional + public String getEventUrl(String uniqueLink) throws Exception { +// String decodedUniqueLink = URLUtils.decode(uniqueLink); + Long userId = Long.parseLong(AESUtils.decrypt(uniqueLink)); + LotsUser findUser = lotsUserRepository.findById(userId) + .orElseThrow(() -> new UserNotExistException("유저가 존재하지 않습니다.")); + + // 해당 유저의 공유수 증가 + LotsShareLink findUserShareLink = lotsShareLinkRepository.findByLotsUser(findUser) + .orElseThrow(() -> new LotsShareLinkNotExistsException("해당 유저의 공유링크 정보가 존재하지 않습니다.")); + findUserShareLink.increaseCount(); + + // 해당 유저의 유형 링크 반환 + return findUser.getTestResult().getUrl(); + } + + + private String createUniqueLink(Long userId) throws Exception { + return SERVER_HOST + ":" + SERVER_PORT + "/lots/link/" + URLUtils.encode(AESUtils.encrypt(String.valueOf(userId))); + } } diff --git a/src/main/java/com/softeer/podo/event/util/Result.java b/src/main/java/com/softeer/podo/event/util/Result.java index 77cca70..4f246f0 100644 --- a/src/main/java/com/softeer/podo/event/util/Result.java +++ b/src/main/java/com/softeer/podo/event/util/Result.java @@ -1,10 +1,8 @@ package com.softeer.podo.event.util; -import lombok.Getter; - public enum Result { SAFETY, ADVENTURE, SENS, - EARLYADOPTER + EARLY_ADOPTER } diff --git a/src/main/java/com/softeer/podo/event/util/SelectionMap.java b/src/main/java/com/softeer/podo/event/util/SelectionMap.java index 5393b57..e3667d9 100644 --- a/src/main/java/com/softeer/podo/event/util/SelectionMap.java +++ b/src/main/java/com/softeer/podo/event/util/SelectionMap.java @@ -32,10 +32,10 @@ public void init() { selectionMap.put("ABBB", Result.SENS); selectionMap.put("BBBA", Result.SENS); - selectionMap.put("AABA", Result.EARLYADOPTER); - selectionMap.put("AABB", Result.EARLYADOPTER); - selectionMap.put("BABA", Result.EARLYADOPTER); - selectionMap.put("BABB", Result.EARLYADOPTER); + selectionMap.put("AABA", Result.EARLY_ADOPTER); + selectionMap.put("AABB", Result.EARLY_ADOPTER); + selectionMap.put("BABA", Result.EARLY_ADOPTER); + selectionMap.put("BABB", Result.EARLY_ADOPTER); } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 7b2c4fd..cdcd522 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,3 +1,7 @@ +server: + host: ${SERVER_HOST} + port: 8080 + spring: application: name: podo diff --git a/src/main/resources/templates/error/404.html b/src/main/resources/templates/error/404.html new file mode 100644 index 0000000..6946742 --- /dev/null +++ b/src/main/resources/templates/error/404.html @@ -0,0 +1,11 @@ + + + + + Page Not Found + + +

404 - Page Not Found

+

The page you are looking for might have been removed, had its name changed, or is temporarily unavailable.

+ + diff --git a/src/main/resources/templates/type/adventurer.html b/src/main/resources/templates/type/adventurer.html new file mode 100644 index 0000000..9c2c632 --- /dev/null +++ b/src/main/resources/templates/type/adventurer.html @@ -0,0 +1,34 @@ + + + + + + 다이나믹한 모험가 + + + +
+

다이나믹한 모험가

+

새로운 경험과 모험을 즐기는 당신은 언제나 다이나믹한 삶을 살아갑니다.

+

여행과 탐험을 사랑하는 당신의 모험심은 사람들에게 영감을 줍니다.

+
+ + diff --git a/src/main/resources/templates/type/best_driver.html b/src/main/resources/templates/type/best_driver.html new file mode 100644 index 0000000..db9340b --- /dev/null +++ b/src/main/resources/templates/type/best_driver.html @@ -0,0 +1,34 @@ + + + + + + 안전을 최우선시하는 베스트 드라이버 + + + +
+

안전을 최우선시하는 베스트 드라이버

+

안전을 최우선으로 생각하는 당신은 도로 위의 진정한 안전 지킴이입니다.

+

모든 운전자들이 당신처럼 안전을 중요하게 생각한다면 세상은 더 안전해질 것입니다.

+
+ + diff --git a/src/main/resources/templates/type/early_adopter.html b/src/main/resources/templates/type/early_adopter.html new file mode 100644 index 0000000..824b2b2 --- /dev/null +++ b/src/main/resources/templates/type/early_adopter.html @@ -0,0 +1,34 @@ + + + + + + 호기심 많은 얼리어답터 + + + +
+

호기심 많은 얼리어답터

+

새로운 기술과 제품에 대한 호기심이 가득한 당신은 진정한 얼리어답터입니다.

+

항상 최신 기술을 탐구하며 새로운 것을 시도하는 당신은 혁신의 선두에 서 있습니다.

+
+ + diff --git a/src/main/resources/templates/type/trendsetter.html b/src/main/resources/templates/type/trendsetter.html new file mode 100644 index 0000000..ca6e0d5 --- /dev/null +++ b/src/main/resources/templates/type/trendsetter.html @@ -0,0 +1,34 @@ + + + + + + 감각적인 트렌드세터 + + + +
+

감각적인 트렌드세터

+

최신 트렌드를 누구보다 빠르게 접하고 선도하는 당신은 진정한 트렌드세터입니다.

+

당신의 감각적인 스타일은 언제나 사람들의 시선을 사로잡습니다.

+
+ + diff --git a/src/test/java/com/softeer/podo/admin/service/AdminServiceTest.java b/src/test/java/com/softeer/podo/admin/service/AdminServiceTest.java index 5974ec2..c40f130 100644 --- a/src/test/java/com/softeer/podo/admin/service/AdminServiceTest.java +++ b/src/test/java/com/softeer/podo/admin/service/AdminServiceTest.java @@ -1,17 +1,13 @@ package com.softeer.podo.admin.service; import com.softeer.podo.admin.model.dto.*; -import com.softeer.podo.admin.model.dto.mapper.UserMapper; import com.softeer.podo.admin.model.dto.user.ArrivalUserDto; import com.softeer.podo.admin.model.dto.user.ArrivalUserListDto; import com.softeer.podo.admin.model.dto.user.LotsUserListDto; import com.softeer.podo.admin.model.entity.Event; import com.softeer.podo.admin.repository.EventRepository; import com.softeer.podo.admin.repository.EventRewardRepository; -import com.softeer.podo.event.model.dto.LotsApplicationResponseDto; import jakarta.transaction.Transactional; -import org.json.JSONArray; -import org.json.JSONObject; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired;