From d63679a163f612c52242975a6949d94256ec9f8b Mon Sep 17 00:00:00 2001 From: Hajin Date: Mon, 12 May 2025 19:22:28 +0900 Subject: [PATCH 01/12] =?UTF-8?q?feat:=20Swagger=20=EC=84=B8=ED=8C=85,=20?= =?UTF-8?q?=EB=A9=A4=EB=B2=84=20=EC=B6=94=EA=B0=80=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hajin/build.gradle | 3 ++ .../umc/study/apiPayload/ApiResponse.java | 1 - .../apiPayload/code/status/ErrorStatus.java | 1 + .../apiPayload/exception/ExceptionAdvice.java | 2 +- .../handler/FoodCategoryHandler.java | 10 ++++ .../exception/handler/TempHandler.java | 1 + .../java/umc/study/config/SwaggerConfig.java | 39 +++++++++++++++ .../umc/study/converter/UserConverter.java | 47 +++++++++++++++++++ .../study/converter/UserPreferConverter.java | 17 +++++++ .../src/main/java/umc/study/domain/Users.java | 10 +++- .../java/umc/study/domain/enums/Gender.java | 2 +- .../study/domain/mapping/FoodPreference.java | 17 ++++++- .../FoodCategoryRepository.java | 9 ++++ .../UserRepository/UserRepository.java | 9 ++++ .../TempService/TempQueryServiceImpl.java | 1 - .../UserService/UserCommandService.java | 10 ++++ .../UserService/UserCommandServiceImpl.java | 41 ++++++++++++++++ .../service/UserService/UserQueryService.java | 4 ++ .../UserService/UserQueryServiceImpl.java | 4 ++ .../web/controller/TempRestController.java | 7 +++ .../web/controller/UserRestController.java | 29 ++++++++++++ .../java/umc/study/web/dto/TempResponse.java | 8 ++-- .../umc/study/web/dto/UserRequestDTO.java | 24 ++++++++++ .../umc/study/web/dto/UserResponseDTO.java | 20 ++++++++ 24 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 hajin/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java create mode 100644 hajin/src/main/java/umc/study/config/SwaggerConfig.java create mode 100644 hajin/src/main/java/umc/study/converter/UserConverter.java create mode 100644 hajin/src/main/java/umc/study/converter/UserPreferConverter.java create mode 100644 hajin/src/main/java/umc/study/repository/UserRepository/FoodCategoryRepository.java create mode 100644 hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserCommandService.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserQueryService.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java create mode 100644 hajin/src/main/java/umc/study/web/controller/UserRestController.java create mode 100644 hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java create mode 100644 hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java diff --git a/hajin/build.gradle b/hajin/build.gradle index 7e151dd..1c9673a 100644 --- a/hajin/build.gradle +++ b/hajin/build.gradle @@ -29,6 +29,9 @@ dependencies { implementation 'org.hibernate.orm:hibernate-core:6.0.2.Final' implementation 'mysql:mysql-connector-java:8.0.33' + // Swagger + implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + // Bean Validation implementation 'org.hibernate.validator:hibernate-validator:8.0.0.Final' diff --git a/hajin/src/main/java/umc/study/apiPayload/ApiResponse.java b/hajin/src/main/java/umc/study/apiPayload/ApiResponse.java index 5c8bad6..dfbade4 100644 --- a/hajin/src/main/java/umc/study/apiPayload/ApiResponse.java +++ b/hajin/src/main/java/umc/study/apiPayload/ApiResponse.java @@ -21,7 +21,6 @@ public class ApiResponse { private T reslt; // 성공한 경우 응답 생성 - public static ApiResponse onSuccess(T result){ return new ApiResponse<>(true, SuccessStatus._OK.getCode() , SuccessStatus._OK.getMessage(), result); } diff --git a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java index 5b5ab4e..fe838e0 100644 --- a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java +++ b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -15,6 +15,7 @@ public enum ErrorStatus implements BaseErrorCode { _BAD_REQUEST(HttpStatus.BAD_REQUEST,"COMMON400","잘못된 요청입니다."), _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), + FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), // 멤버 관려 에러 MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), diff --git a/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java b/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java index f90cf8c..04f616e 100644 --- a/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java +++ b/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java @@ -69,7 +69,7 @@ private ResponseEntity handleExceptionInternal(Exception e, ErrorReasonD HttpHeaders headers, HttpServletRequest request) { ApiResponse body = ApiResponse.onFailure(reason.getCode(),reason.getMessage(),null); -// e.printStackTrace(); +// e.printStackTrace(); WebRequest webRequest = new ServletWebRequest(request); return super.handleExceptionInternal( diff --git a/hajin/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java b/hajin/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java new file mode 100644 index 0000000..ad89c54 --- /dev/null +++ b/hajin/src/main/java/umc/study/apiPayload/exception/handler/FoodCategoryHandler.java @@ -0,0 +1,10 @@ +package umc.study.apiPayload.exception.handler; + +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.GeneralException; + +public class FoodCategoryHandler extends GeneralException { + public FoodCategoryHandler(ErrorStatus errorStatus) { + super(errorStatus); + } +} diff --git a/hajin/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java b/hajin/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java index 6ea0f76..22d3de6 100644 --- a/hajin/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java +++ b/hajin/src/main/java/umc/study/apiPayload/exception/handler/TempHandler.java @@ -9,4 +9,5 @@ public class TempHandler extends GeneralException { public TempHandler(BaseErrorCode errorCode) { super(errorCode); } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/config/SwaggerConfig.java b/hajin/src/main/java/umc/study/config/SwaggerConfig.java new file mode 100644 index 0000000..48c8fee --- /dev/null +++ b/hajin/src/main/java/umc/study/config/SwaggerConfig.java @@ -0,0 +1,39 @@ +package umc.study.config; + +import io.swagger.v3.oas.models.Components; +import io.swagger.v3.oas.models.OpenAPI; +import io.swagger.v3.oas.models.info.Info; +import io.swagger.v3.oas.models.security.SecurityRequirement; +import io.swagger.v3.oas.models.security.SecurityScheme; +import io.swagger.v3.oas.models.servers.Server; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class SwaggerConfig { + + @Bean + public OpenAPI UMCstudyAPI() { + Info info = new Info() + .title("UMC Server WorkBook API") + .description("UMC Server WorkBook API 명세서") + .version("1.0.0"); + + String jwtSchemeName = "JWT TOKEN"; + // API 요청헤더에 인증정보 포함 + SecurityRequirement securityRequirement = new SecurityRequirement().addList(jwtSchemeName); + // SecuritySchemes 등록 + Components components = new Components() + .addSecuritySchemes(jwtSchemeName, new SecurityScheme() + .name(jwtSchemeName) + .type(SecurityScheme.Type.HTTP) // HTTP 방식 + .scheme("bearer") + .bearerFormat("JWT")); + + return new OpenAPI() + .addServersItem(new Server().url("/")) + .info(info) + .addSecurityItem(securityRequirement) + .components(components); + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/converter/UserConverter.java b/hajin/src/main/java/umc/study/converter/UserConverter.java new file mode 100644 index 0000000..55958ad --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/UserConverter.java @@ -0,0 +1,47 @@ +package umc.study.converter; + +import umc.study.domain.Users; +import umc.study.domain.enums.Gender; +import umc.study.web.dto.UserResponseDTO; +import umc.study.web.dto.UserRequestDTO; + +import java.time.LocalDateTime; +import java.util.ArrayList; + +public class UserConverter { + + public static UserResponseDTO.JoinResultDTO toJoinResultDTO(Users user) { + return UserResponseDTO.JoinResultDTO.builder() + .userId((long) user.getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static Users toUser(UserRequestDTO.JoinDto request) { + + Gender gender = null; + + switch (request.getGender()) { + case 1: + gender = Gender.MALE; + break; + case 2: + gender = Gender.FEMALE; + break; + case 3: + gender = Gender.NONE; + break; + } + + return Users.builder() + .address(request.getAddress()) + .gender(gender) + .age(request.getAge()) + .name(request.getName()) + .foodPreferenceList(new ArrayList<>()) + .email(request.getEmail()) + .birth(request.getBirth()) + .point(request.getPoint()) + .build(); + } +} diff --git a/hajin/src/main/java/umc/study/converter/UserPreferConverter.java b/hajin/src/main/java/umc/study/converter/UserPreferConverter.java new file mode 100644 index 0000000..a512683 --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/UserPreferConverter.java @@ -0,0 +1,17 @@ +package umc.study.converter; + +import umc.study.domain.Food; +import umc.study.domain.mapping.FoodPreference; + +import java.util.List; +import java.util.stream.Collectors; + +public class UserPreferConverter { + public static List toUserPreferList(List foodList) { + return foodList.stream() + .map(food -> FoodPreference.builder() + .food(food) + .build() + ).collect(Collectors.toList()); + } +} diff --git a/hajin/src/main/java/umc/study/domain/Users.java b/hajin/src/main/java/umc/study/domain/Users.java index 05ce7b5..18d6117 100644 --- a/hajin/src/main/java/umc/study/domain/Users.java +++ b/hajin/src/main/java/umc/study/domain/Users.java @@ -2,6 +2,9 @@ import jakarta.persistence.*; import lombok.*; +import org.hibernate.annotations.ColumnDefault; +import org.hibernate.annotations.DynamicInsert; +import org.hibernate.annotations.DynamicUpdate; import umc.study.domain.common.BaseEntity; import umc.study.domain.enums.Gender; import umc.study.domain.enums.SocialType; @@ -17,6 +20,8 @@ @Entity @Getter +@DynamicUpdate // update시 null인경우 쿼리를 보내지 않음 +@DynamicInsert // insert시 null인경우 쿼리를 보내지 않음 @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본 생성자 @AllArgsConstructor // 모든 매개변수 생성자 @@ -35,9 +40,12 @@ public class Users extends BaseEntity { private LocalDateTime birth; + private int age; + @Column(nullable = false, length = 40) private String address; + @ColumnDefault("0") private int point; @Column(nullable = false, length = 40) @@ -46,7 +54,7 @@ public class Users extends BaseEntity { @Column(nullable = false, length = 40) private String phone_number; - private boolean phoneCertification; + private boolean phone_certification; @Enumerated(EnumType.STRING) @Column(columnDefinition = "VARCHAR(10) DEFAULT 'ACTIVE'") diff --git a/hajin/src/main/java/umc/study/domain/enums/Gender.java b/hajin/src/main/java/umc/study/domain/enums/Gender.java index ee212a8..345b5b7 100644 --- a/hajin/src/main/java/umc/study/domain/enums/Gender.java +++ b/hajin/src/main/java/umc/study/domain/enums/Gender.java @@ -1,5 +1,5 @@ package umc.study.domain.enums; public enum Gender { - MALE, FEMALE + MALE, FEMALE, NONE } diff --git a/hajin/src/main/java/umc/study/domain/mapping/FoodPreference.java b/hajin/src/main/java/umc/study/domain/mapping/FoodPreference.java index c5ced7b..52693f8 100644 --- a/hajin/src/main/java/umc/study/domain/mapping/FoodPreference.java +++ b/hajin/src/main/java/umc/study/domain/mapping/FoodPreference.java @@ -4,13 +4,14 @@ import lombok.*; import umc.study.domain.Food; import umc.study.domain.Users; +import umc.study.domain.common.BaseEntity; @Entity @Getter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor -public class FoodPreference { +public class FoodPreference extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @@ -22,4 +23,18 @@ public class FoodPreference { @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "food_id") private Food food; + + public void setUser(Users user){ + //코드 재사용할 때 같이 쓰는거 + if(this.user != null) + user.getFoodPreferenceList().remove(this); + //(회원가입)지금 쓰는거 + this.user = user; + user.getFoodPreferenceList().add(this); + } + + public void setFoodCategory(Food foodCategory){ + this.food = foodCategory; + } + } diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/FoodCategoryRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/FoodCategoryRepository.java new file mode 100644 index 0000000..ef2be51 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/UserRepository/FoodCategoryRepository.java @@ -0,0 +1,9 @@ +package umc.study.repository.UserRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import umc.study.domain.Food; + +@Repository +public interface FoodCategoryRepository extends JpaRepository { +} diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java new file mode 100644 index 0000000..cb086a6 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -0,0 +1,9 @@ +package umc.study.repository.UserRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import umc.study.domain.Users; + +@Repository +public interface UserRepository extends JpaRepository { +} diff --git a/hajin/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java index c6aaf2c..012ab1c 100644 --- a/hajin/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/TempService/TempQueryServiceImpl.java @@ -13,6 +13,5 @@ public class TempQueryServiceImpl implements TempQueryService{ public void CheckFlag(Integer flag) { if (flag == 1) throw new TempHandler(ErrorStatus.TEMP_EXCEPTION); - } } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java b/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java new file mode 100644 index 0000000..56febb8 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java @@ -0,0 +1,10 @@ +package umc.study.service.UserService; + +import org.springframework.stereotype.Service; +import umc.study.domain.Users; +import umc.study.web.dto.UserRequestDTO; + +@Service +public interface UserCommandService { + public Users joinMember(UserRequestDTO.JoinDto request); +} diff --git a/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java new file mode 100644 index 0000000..8171852 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java @@ -0,0 +1,41 @@ +package umc.study.service.UserService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.FoodCategoryHandler; +import umc.study.converter.UserConverter; +import umc.study.converter.UserPreferConverter; +import umc.study.domain.Food; +import umc.study.domain.Users; +import umc.study.domain.mapping.FoodPreference; +import umc.study.repository.UserRepository.FoodCategoryRepository; +import umc.study.repository.UserRepository.UserRepository; +import umc.study.web.dto.UserRequestDTO; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class UserCommandServiceImpl implements UserCommandService { + + private final UserRepository userRepository; + private final FoodCategoryRepository foodCategoryRepository; + + @Override + @Transactional + public Users joinMember(UserRequestDTO.JoinDto request) { + Users newUser = UserConverter.toUser(request); + //userRepository.save(newUser); + List foodList = request.getPreferFood().stream() + .map( category -> { + return foodCategoryRepository.findById(category).orElseThrow(()-> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); + }).collect(Collectors.toList()); + List foodPreferList = UserPreferConverter.toUserPreferList(foodList); + foodPreferList.forEach(foodPreference->{foodPreference.setUser(newUser); }); + return userRepository.save(newUser); + } + +} diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java new file mode 100644 index 0000000..ad41fac --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java @@ -0,0 +1,4 @@ +package umc.study.service.UserService; + +public interface UserQueryService { +} diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java new file mode 100644 index 0000000..ab8492d --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -0,0 +1,4 @@ +package umc.study.service.UserService; + +public class UserQueryServiceImpl { +} diff --git a/hajin/src/main/java/umc/study/web/controller/TempRestController.java b/hajin/src/main/java/umc/study/web/controller/TempRestController.java index 6747ebe..fe5a936 100644 --- a/hajin/src/main/java/umc/study/web/controller/TempRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/TempRestController.java @@ -1,6 +1,9 @@ package umc.study.web.controller; import lombok.RequiredArgsConstructor; + +import lombok.extern.slf4j.Slf4j; +import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; @@ -13,6 +16,7 @@ @RestController @RequestMapping("/temp") @RequiredArgsConstructor +@Slf4j public class TempRestController { private final TempQueryService tempQueryService; @@ -23,7 +27,10 @@ public ApiResponse testAPI(){ @GetMapping("/exception") public ApiResponse exceptionAPI(@RequestParam Integer flag){ + log.info("exception API 호출됨 - flag: {}", flag); tempQueryService.CheckFlag(flag); + log.info("/exception 정상 처리 완료"); return ApiResponse.onSuccess(TempConverter.toTempExceptionDTO(flag)); + //log.info("여기는 안찍힌다는 거지?"); } } diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java new file mode 100644 index 0000000..ad73b7b --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -0,0 +1,29 @@ +package umc.study.web.controller; + +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; +import umc.study.apiPayload.ApiResponse; +import umc.study.converter.UserConverter; +import umc.study.domain.Users; +import umc.study.service.UserService.UserCommandService; +import umc.study.web.dto.UserRequestDTO; +import umc.study.web.dto.UserResponseDTO; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users") +public class UserRestController { + + private final UserCommandService userCommandService; + + // Valid앞에 @RequestBody가 있었는데 없어도 되는거 아닌가?(의문) + @PostMapping("/") + public ApiResponse join(@RequestBody @Valid UserRequestDTO.JoinDto request){ + Users user = userCommandService.joinMember(request); + return ApiResponse.onSuccess(UserConverter.toJoinResultDTO(user)); + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/TempResponse.java b/hajin/src/main/java/umc/study/web/dto/TempResponse.java index 81bc6b0..a9734a8 100644 --- a/hajin/src/main/java/umc/study/web/dto/TempResponse.java +++ b/hajin/src/main/java/umc/study/web/dto/TempResponse.java @@ -9,16 +9,16 @@ public class TempResponse { @Builder @Getter - @NoArgsConstructor - @AllArgsConstructor + //@NoArgsConstructor + //@AllArgsConstructor public static class TempTestDTO{ String testString; } @Builder @Getter - @NoArgsConstructor - @AllArgsConstructor + //@NoArgsConstructor + //@AllArgsConstructor public static class TempExceptionDTO{ Integer flag; } diff --git a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java new file mode 100644 index 0000000..abadbd6 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java @@ -0,0 +1,24 @@ +package umc.study.web.dto; + +import lombok.Getter; +//import umc.study.domain.enums.Gender; + +import java.time.LocalDateTime; +import java.util.List; + +public class UserRequestDTO { + + @Getter + public static class JoinDto{ + String name; + Integer gender; // 받을 때는 정수 형태로 + LocalDateTime birth; + Integer age; + String address; + Integer point; + String phone_number; + Boolean phone_certification; + String email; + List preferFood; + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java new file mode 100644 index 0000000..02cbaae --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java @@ -0,0 +1,20 @@ +package umc.study.web.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.time.LocalDateTime; + +public class UserResponseDTO { + + @Builder + @Getter + //@NoArgsConstructor + //@AllArgsConstructor + public static class JoinResultDTO{ + Long userId; + LocalDateTime createdAt; + } +} From 2248d2a43db86d737657a80ada1fc9b93b056054 Mon Sep 17 00:00:00 2001 From: Hajin Date: Tue, 13 May 2025 01:23:18 +0900 Subject: [PATCH 02/12] =?UTF-8?q?feat:=20annotation=20=ED=99=9C=EC=9A=A9?= =?UTF-8?q?=20validation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hajin/build.gradle | 5 ++- .../annotation/ExistCategories.java | 19 ++++++++++ .../validator/CategoriesExistValidator.java | 37 +++++++++++++++++++ .../umc/study/web/dto/UserRequestDTO.java | 2 + 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 hajin/src/main/java/umc/study/validation/annotation/ExistCategories.java create mode 100644 hajin/src/main/java/umc/study/validation/validator/CategoriesExistValidator.java diff --git a/hajin/build.gradle b/hajin/build.gradle index 1c9673a..56c824d 100644 --- a/hajin/build.gradle +++ b/hajin/build.gradle @@ -32,8 +32,11 @@ dependencies { // Swagger implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0' + // Anotation Validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + // Bean Validation - implementation 'org.hibernate.validator:hibernate-validator:8.0.0.Final' + //implementation 'org.hibernate.validator:hibernate-validator:8.0.0.Final' // QueryDSL implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' diff --git a/hajin/src/main/java/umc/study/validation/annotation/ExistCategories.java b/hajin/src/main/java/umc/study/validation/annotation/ExistCategories.java new file mode 100644 index 0000000..4720c49 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/annotation/ExistCategories.java @@ -0,0 +1,19 @@ +package umc.study.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.study.validation.validator.CategoriesExistValidator; + + +import java.lang.annotation.*; + +@Documented // 사용자 정의 어노테이션 +@Constraint(validatedBy = CategoriesExistValidator.class) // Documented로 validation을 할 수 있도록 제공하는 어노테이션 +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) //어노테이션 적용 범위 지정 +@Retention(RetentionPolicy.RUNTIME) // 생명주기 지정 +public @interface ExistCategories { + + String message() default "해당하는 카테고리가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/validation/validator/CategoriesExistValidator.java b/hajin/src/main/java/umc/study/validation/validator/CategoriesExistValidator.java new file mode 100644 index 0000000..5a8821b --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/validator/CategoriesExistValidator.java @@ -0,0 +1,37 @@ +package umc.study.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.repository.UserRepository.FoodCategoryRepository; +import umc.study.validation.annotation.ExistCategories; + +import java.util.List; + +@Component +@RequiredArgsConstructor +public class CategoriesExistValidator implements ConstraintValidator> { + + private final FoodCategoryRepository foodCategoryRepository; + + @Override + public void initialize(ExistCategories constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(List values, ConstraintValidatorContext context) { + boolean isValid = values.stream() + .allMatch(value -> foodCategoryRepository.existsById(value)); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.FOOD_CATEGORY_NOT_FOUND.toString()).addConstraintViolation(); + } + + return isValid; + + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java index abadbd6..9c805ed 100644 --- a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java @@ -1,6 +1,7 @@ package umc.study.web.dto; import lombok.Getter; +import umc.study.validation.annotation.ExistCategories; //import umc.study.domain.enums.Gender; import java.time.LocalDateTime; @@ -19,6 +20,7 @@ public static class JoinDto{ String phone_number; Boolean phone_certification; String email; + @ExistCategories List preferFood; } } From af95b257d244df0cc76e08f7be2290f1d3063bb8 Mon Sep 17 00:00:00 2001 From: Hajin Date: Tue, 13 May 2025 10:52:20 +0900 Subject: [PATCH 03/12] =?UTF-8?q?feat:=EA=B0=80=EA=B2=8C=EC=97=90=20?= =?UTF-8?q?=EB=A6=AC=EB=B7=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 2 +- .../umc/study/converter/ReviewConverter.java | 27 +++++++++++++ .../main/java/umc/study/domain/Review.java | 9 +++++ .../src/main/java/umc/study/domain/Store.java | 6 +++ .../umc/study/domain/enums/SocialType.java | 2 +- .../ReviewRepository/ReviewRepository.java | 7 ++++ .../UserRepository/UserRepository.java | 2 +- .../service/ReviewService/ReviewService.java | 9 +++++ .../ReviewService/ReviewServiceImpl.java | 37 ++++++++++++++++++ .../validation/annotation/ExistStore.java | 18 +++++++++ .../validator/StoreExistValidator.java | 38 +++++++++++++++++++ .../web/controller/ReviewRestController.java | 28 ++++++++++++++ .../umc/study/web/dto/ReviewRequestDTO.java | 12 ++++++ .../umc/study/web/dto/ReviewResponseDTO.java | 15 ++++++++ .../umc/study/web/dto/UserRequestDTO.java | 1 + 15 files changed, 210 insertions(+), 3 deletions(-) create mode 100644 hajin/src/main/java/umc/study/converter/ReviewConverter.java create mode 100644 hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java create mode 100644 hajin/src/main/java/umc/study/service/ReviewService/ReviewService.java create mode 100644 hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java create mode 100644 hajin/src/main/java/umc/study/validation/annotation/ExistStore.java create mode 100644 hajin/src/main/java/umc/study/validation/validator/StoreExistValidator.java create mode 100644 hajin/src/main/java/umc/study/web/controller/ReviewRestController.java create mode 100644 hajin/src/main/java/umc/study/web/dto/ReviewRequestDTO.java create mode 100644 hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java diff --git a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java index fe838e0..1d9a3d3 100644 --- a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java +++ b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -16,7 +16,7 @@ public enum ErrorStatus implements BaseErrorCode { _UNAUTHORIZED(HttpStatus.UNAUTHORIZED,"COMMON401","인증이 필요합니다."), _FORBIDDEN(HttpStatus.FORBIDDEN, "COMMON403", "금지된 요청입니다."), FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), - + STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), // 멤버 관려 에러 MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."), diff --git a/hajin/src/main/java/umc/study/converter/ReviewConverter.java b/hajin/src/main/java/umc/study/converter/ReviewConverter.java new file mode 100644 index 0000000..1e08111 --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/ReviewConverter.java @@ -0,0 +1,27 @@ +package umc.study.converter; + +import umc.study.domain.Review; +import umc.study.domain.Users; +import umc.study.web.dto.ReviewRequestDTO; +import umc.study.web.dto.ReviewResponseDTO; + +import java.time.LocalDateTime; + +public class ReviewConverter { + + public static ReviewResponseDTO.ReviewResultDto toDto(Review review) { + return ReviewResponseDTO.ReviewResultDto.builder() + .reviewId(review.getId()) + .createdAt(LocalDateTime.now()) + .build(); + } + + public static Review toReview(Users user, ReviewRequestDTO.ReviewDto request) { + return Review.builder() + .user(user) + .title(request.getTitle()) + .content(request.getContent()) + .build(); + + } +} diff --git a/hajin/src/main/java/umc/study/domain/Review.java b/hajin/src/main/java/umc/study/domain/Review.java index 9eb6a34..bb4a1e6 100644 --- a/hajin/src/main/java/umc/study/domain/Review.java +++ b/hajin/src/main/java/umc/study/domain/Review.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; import umc.study.domain.common.BaseEntity; +import umc.study.validation.annotation.ExistStore; import java.util.ArrayList; import java.util.List; @@ -36,4 +37,12 @@ public class Review extends BaseEntity { @OneToMany(mappedBy ="review", cascade = CascadeType.ALL) private List ReviewImageList = new ArrayList<>(); + + public void setUser(Users user) { + this.user = user; + } + + public void setStore(Store store) { + this.store = store; + } } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/Store.java b/hajin/src/main/java/umc/study/domain/Store.java index 8dde890..38e1993 100644 --- a/hajin/src/main/java/umc/study/domain/Store.java +++ b/hajin/src/main/java/umc/study/domain/Store.java @@ -3,6 +3,7 @@ import jakarta.persistence.*; import lombok.*; import umc.study.domain.common.BaseEntity; +import umc.study.validation.annotation.ExistStore; import java.util.ArrayList; import java.util.List; @@ -49,4 +50,9 @@ public String toString() { '}'; } + public void addReview(Review review) { + this.ReviewList.add(review); + review.setStore(this); // 연관 관계 동기화 + } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/enums/SocialType.java b/hajin/src/main/java/umc/study/domain/enums/SocialType.java index ce82420..0d6dd26 100644 --- a/hajin/src/main/java/umc/study/domain/enums/SocialType.java +++ b/hajin/src/main/java/umc/study/domain/enums/SocialType.java @@ -1,5 +1,5 @@ package umc.study.domain.enums; public enum SocialType { - KAKAO, NAVER, GOOGLE + KAKAO, NAVER, GOOGLE, NONE } diff --git a/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java new file mode 100644 index 0000000..cd85269 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java @@ -0,0 +1,7 @@ +package umc.study.repository.ReviewRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.study.domain.Review; + +public interface ReviewRepository extends JpaRepository { +} diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java index cb086a6..a613976 100644 --- a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -5,5 +5,5 @@ import umc.study.domain.Users; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { } diff --git a/hajin/src/main/java/umc/study/service/ReviewService/ReviewService.java b/hajin/src/main/java/umc/study/service/ReviewService/ReviewService.java new file mode 100644 index 0000000..a58dbbc --- /dev/null +++ b/hajin/src/main/java/umc/study/service/ReviewService/ReviewService.java @@ -0,0 +1,9 @@ +package umc.study.service.ReviewService; + +import umc.study.domain.Review; +import umc.study.domain.Store; +import umc.study.web.dto.ReviewRequestDTO; + +public interface ReviewService { + public Review save(Long userId, Long storeId, ReviewRequestDTO.ReviewDto request); +} diff --git a/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java new file mode 100644 index 0000000..fcc6ff9 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java @@ -0,0 +1,37 @@ +package umc.study.service.ReviewService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.study.converter.ReviewConverter; +import umc.study.domain.Review; +import umc.study.domain.Store; +import umc.study.domain.Users; +import umc.study.repository.ReviewRepository.ReviewRepository; +import umc.study.repository.StoreRepository.StoreRepository; +import umc.study.repository.UserRepository.UserRepository; +import umc.study.web.dto.ReviewRequestDTO; + +@Service +@RequiredArgsConstructor +public class ReviewServiceImpl implements ReviewService{ + + private final ReviewRepository reviewRepository; + private final UserRepository userRepository; + private final StoreRepository storeRepository; + + @Override + public Review save(Long userId, Long storeId, ReviewRequestDTO.ReviewDto request) { + // 사용자 조회, 1번 사용자만 리뷰 작성 + Users user = userRepository.findAll().stream().findFirst() + .orElseThrow(() -> new IllegalArgumentException("No users available")); + + Store store = storeRepository.getReferenceById(storeId); + // 리뷰 생성 + Review review = ReviewConverter.toReview(user,request); + review.setUser(user); // 사용자 정보 매핑 + + review.setStore(store); + store.addReview(review); + return reviewRepository.save(review); + } +} diff --git a/hajin/src/main/java/umc/study/validation/annotation/ExistStore.java b/hajin/src/main/java/umc/study/validation/annotation/ExistStore.java new file mode 100644 index 0000000..0e70412 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/annotation/ExistStore.java @@ -0,0 +1,18 @@ +package umc.study.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.study.validation.validator.StoreExistValidator; + +import java.lang.annotation.*; + +@Documented // 사용자 정의 어노테이션 +@Constraint(validatedBy = StoreExistValidator.class) // Documented로 validation을 할 수 있도록 제공하는 어노테이션 +@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER }) //어노테이션 적용 범위 지정 +@Retention(RetentionPolicy.RUNTIME) // 생명주기 지정 +public @interface ExistStore { + + String message() default "해당하는 가게가 존재하지 않습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/validation/validator/StoreExistValidator.java b/hajin/src/main/java/umc/study/validation/validator/StoreExistValidator.java new file mode 100644 index 0000000..4387109 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/validator/StoreExistValidator.java @@ -0,0 +1,38 @@ +package umc.study.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.repository.StoreRepository.StoreRepository; +import umc.study.validation.annotation.ExistCategories; +import umc.study.validation.annotation.ExistStore; + +import java.util.List; + +public class StoreExistValidator implements ConstraintValidator> { + + private final StoreRepository storeRepository; + + public StoreExistValidator(StoreRepository storeRepository) { + this.storeRepository = storeRepository; + } + + @Override + public void initialize(ExistStore constraintAnnotation) { + ConstraintValidator.super.initialize(constraintAnnotation); + } + + @Override + public boolean isValid(List values, ConstraintValidatorContext context) { + boolean isValid = values.stream() + .allMatch(value -> storeRepository.existsById(value)); + + if (!isValid) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate(ErrorStatus.STORE_NOT_FOUND.toString()).addConstraintViolation(); + } + + return isValid; + + } +} diff --git a/hajin/src/main/java/umc/study/web/controller/ReviewRestController.java b/hajin/src/main/java/umc/study/web/controller/ReviewRestController.java new file mode 100644 index 0000000..1ca430d --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/ReviewRestController.java @@ -0,0 +1,28 @@ +package umc.study.web.controller; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; +import umc.study.apiPayload.ApiResponse; +import umc.study.converter.ReviewConverter; +import umc.study.domain.Review; +import umc.study.domain.Store; +import umc.study.service.ReviewService.ReviewService; +import umc.study.validation.annotation.ExistStore; +import umc.study.web.dto.ReviewRequestDTO; +import umc.study.web.dto.ReviewResponseDTO; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/users/{userId}/stores/{storeId}/reviews") +public class ReviewRestController { + + private final ReviewService reviewService; + + @PostMapping("/") + public ApiResponse review(@PathVariable Long userId, @PathVariable @ExistStore Long storeId, @RequestBody @Valid ReviewRequestDTO.ReviewDto request){ + Review review = reviewService.save(userId, storeId, request); + return ApiResponse.onSuccess(ReviewConverter.toDto(review)); + } + +} diff --git a/hajin/src/main/java/umc/study/web/dto/ReviewRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/ReviewRequestDTO.java new file mode 100644 index 0000000..e25dc68 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/ReviewRequestDTO.java @@ -0,0 +1,12 @@ +package umc.study.web.dto; + +import lombok.Getter; + +public class ReviewRequestDTO { + + @Getter + public static class ReviewDto{ + String title; + String content; + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java new file mode 100644 index 0000000..a8f663f --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java @@ -0,0 +1,15 @@ +package umc.study.web.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +public class ReviewResponseDTO { + @Builder + @Getter + public static class ReviewResultDto{ + Long reviewId; + LocalDateTime createdAt; + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java index 9c805ed..4d798c3 100644 --- a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java @@ -23,4 +23,5 @@ public static class JoinDto{ @ExistCategories List preferFood; } + } From 729c2023190964749c776d77b7493690d15b5ca0 Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Mon, 19 May 2025 01:12:22 +0900 Subject: [PATCH 04/12] =?UTF-8?q?=EA=B0=80=EA=B2=8C=EC=97=90=20=EB=AF=B8?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/exception/ExceptionAdvice.java | 2 -- .../umc/study/converter/MissionConverter.java | 35 +++++++++++++++++++ .../main/java/umc/study/domain/Mission.java | 15 ++++---- .../main/java/umc/study/domain/Region.java | 2 +- .../src/main/java/umc/study/domain/Store.java | 9 +++++ .../umc/study/domain/common/BaseEntity.java | 2 ++ .../umc/study/domain/mapping/UserMission.java | 4 +++ .../AdminRepository/AdminRepository.java | 7 ++++ .../AdminRepository/AdminRepositoryImpl.java | 4 +++ .../MissionRepositoryImpl.java | 8 ++--- .../service/AdminService/AdminService.java | 8 +++++ .../AdminService/AdminServiceImpl.java | 32 +++++++++++++++++ .../ReviewService/ReviewServiceImpl.java | 3 +- .../web/controller/AdminRestController.java | 30 ++++++++++++++++ .../umc/study/web/dto/MissionRequestDTO.java | 35 +++++++++++++++++++ .../umc/study/web/dto/MissionResponseDTO.java | 22 ++++++++++++ 16 files changed, 201 insertions(+), 17 deletions(-) create mode 100644 hajin/src/main/java/umc/study/converter/MissionConverter.java create mode 100644 hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepository.java create mode 100644 hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepositoryImpl.java create mode 100644 hajin/src/main/java/umc/study/service/AdminService/AdminService.java create mode 100644 hajin/src/main/java/umc/study/service/AdminService/AdminServiceImpl.java create mode 100644 hajin/src/main/java/umc/study/web/controller/AdminRestController.java create mode 100644 hajin/src/main/java/umc/study/web/dto/MissionRequestDTO.java create mode 100644 hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java diff --git a/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java b/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java index 04f616e..a140a9b 100644 --- a/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java +++ b/hajin/src/main/java/umc/study/apiPayload/exception/ExceptionAdvice.java @@ -25,8 +25,6 @@ @Slf4j @RestControllerAdvice(annotations = {RestController.class}) public class ExceptionAdvice extends ResponseEntityExceptionHandler { - - @ExceptionHandler public ResponseEntity validation(ConstraintViolationException e, WebRequest request) { String errorMessage = e.getConstraintViolations().stream() diff --git a/hajin/src/main/java/umc/study/converter/MissionConverter.java b/hajin/src/main/java/umc/study/converter/MissionConverter.java new file mode 100644 index 0000000..b076e8f --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/MissionConverter.java @@ -0,0 +1,35 @@ +package umc.study.converter; + +import umc.study.domain.Mission; +import umc.study.domain.Store; +import umc.study.web.dto.MissionRequestDTO; +import umc.study.web.dto.MissionResponseDTO; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class MissionConverter { + + public static MissionResponseDTO.MissionResultDTO toDto(Mission mission){ + return MissionResponseDTO.MissionResultDTO.builder() + .missionId(mission.getId()) + .storeId(mission.getStore().getId()) + .title(mission.getTitle()) + .body(mission.getBody()) + .rewardPoint(mission.getRewardPoint()) + .created_at(mission.getCreatedAt()) + .updated_at(mission.getUpdatedAt()) + .build(); + } + + public static Mission toMission(Store store, MissionRequestDTO.MissionDto request) { + return Mission.builder() + .store(store) + .title(request.getTitle()) + .body(request.getBody()) + .rewardPoint(request.getRewardPoint()) + //.startDate(request.getStartDate()) + //.endDate(request.getEndDate()) + .build(); + } +} diff --git a/hajin/src/main/java/umc/study/domain/Mission.java b/hajin/src/main/java/umc/study/domain/Mission.java index bfe701b..3fa8672 100644 --- a/hajin/src/main/java/umc/study/domain/Mission.java +++ b/hajin/src/main/java/umc/study/domain/Mission.java @@ -6,6 +6,7 @@ import umc.study.domain.mapping.UserMission; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; @@ -28,20 +29,18 @@ public class Mission extends BaseEntity { @Column(nullable = false, length = 40) private String body; - private LocalDate startDate; + private LocalDateTime startDate; - private LocalDate endDate; - - private LocalDate deadline; + private LocalDateTime endDate; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "store_id") private Store store; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "region_id") - private Region region; - @OneToMany(mappedBy ="mission", cascade = CascadeType.ALL) private List UserMissionList = new ArrayList<>(); + + public void setStore(Store store) { + this.store = store; + } } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/Region.java b/hajin/src/main/java/umc/study/domain/Region.java index c90afad..d6a5224 100644 --- a/hajin/src/main/java/umc/study/domain/Region.java +++ b/hajin/src/main/java/umc/study/domain/Region.java @@ -22,5 +22,5 @@ public class Region extends BaseEntity { private String name; @OneToMany(mappedBy ="region", cascade = CascadeType.ALL) - private List MissionList = new ArrayList<>(); + private List StoreList = new ArrayList<>(); } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/Store.java b/hajin/src/main/java/umc/study/domain/Store.java index 38e1993..6be25f9 100644 --- a/hajin/src/main/java/umc/study/domain/Store.java +++ b/hajin/src/main/java/umc/study/domain/Store.java @@ -31,6 +31,10 @@ public class Store extends BaseEntity { @JoinColumn(name = "food_id") private Food food; + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "region_id") + private Region region; + @OneToMany(mappedBy ="store", cascade = CascadeType.ALL) private List ReviewList = new ArrayList<>(); @@ -55,4 +59,9 @@ public void addReview(Review review) { review.setStore(this); // 연관 관계 동기화 } + public void addMission(Mission mission) { + this.MissionList.add(mission); + mission.setStore(this); // 연관 관계 동기화 + } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/common/BaseEntity.java b/hajin/src/main/java/umc/study/domain/common/BaseEntity.java index a4ded44..e92f9b1 100644 --- a/hajin/src/main/java/umc/study/domain/common/BaseEntity.java +++ b/hajin/src/main/java/umc/study/domain/common/BaseEntity.java @@ -1,5 +1,6 @@ package umc.study.domain.common; +import jakarta.persistence.Column; import jakarta.persistence.EntityListeners; import jakarta.persistence.MappedSuperclass; import lombok.Getter; @@ -15,6 +16,7 @@ public abstract class BaseEntity { @CreatedDate + @Column(updatable = false) private LocalDateTime createdAt; @LastModifiedDate diff --git a/hajin/src/main/java/umc/study/domain/mapping/UserMission.java b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java index 669880b..3ff633b 100644 --- a/hajin/src/main/java/umc/study/domain/mapping/UserMission.java +++ b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java @@ -8,6 +8,8 @@ import umc.study.domain.common.BaseEntity; import umc.study.domain.enums.MissionStatus; +import java.time.LocalDate; + @Entity @Getter @Builder @@ -21,6 +23,8 @@ public class UserMission extends BaseEntity { private int confirmNumber; + private LocalDate deadline; + @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "user_id") private Users user; diff --git a/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepository.java b/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepository.java new file mode 100644 index 0000000..c03e253 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepository.java @@ -0,0 +1,7 @@ +package umc.study.repository.AdminRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import umc.study.domain.Mission; + +public interface AdminRepository extends JpaRepository { +} diff --git a/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepositoryImpl.java b/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepositoryImpl.java new file mode 100644 index 0000000..8146d71 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/AdminRepository/AdminRepositoryImpl.java @@ -0,0 +1,4 @@ +package umc.study.repository.AdminRepository; + +public abstract class AdminRepositoryImpl implements AdminRepository { +} diff --git a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepositoryImpl.java b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepositoryImpl.java index 54af7c6..27bd03b 100644 --- a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepositoryImpl.java +++ b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepositoryImpl.java @@ -57,17 +57,15 @@ public List dynamicQueryWithBooleanBuilder(Long userId) { public List missionsByRegion(Long regionId) { QMission mission = QMission.mission; QStore store = QStore.store; - QRegion region = QRegion.region; + return queryFactory .select(mission) .from(mission) .join(mission.store, store).fetchJoin() - .join(mission.region, region).fetchJoin() .where( - region.id.eq(regionId), - mission.startDate.loe(LocalDate.now()), - mission.endDate.goe(LocalDate.now()) + mission.startDate.loe(LocalDate.now().atStartOfDay()), + mission.endDate.goe(LocalDate.now().atTime(23, 59, 59)) ) .orderBy( mission.endDate.asc(), diff --git a/hajin/src/main/java/umc/study/service/AdminService/AdminService.java b/hajin/src/main/java/umc/study/service/AdminService/AdminService.java new file mode 100644 index 0000000..0c51a8d --- /dev/null +++ b/hajin/src/main/java/umc/study/service/AdminService/AdminService.java @@ -0,0 +1,8 @@ +package umc.study.service.AdminService; + +import umc.study.domain.Mission; +import umc.study.web.dto.MissionRequestDTO; + +public interface AdminService { + public Mission saveMission(Long storeId, MissionRequestDTO.MissionDto request); +} diff --git a/hajin/src/main/java/umc/study/service/AdminService/AdminServiceImpl.java b/hajin/src/main/java/umc/study/service/AdminService/AdminServiceImpl.java new file mode 100644 index 0000000..644cb62 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/AdminService/AdminServiceImpl.java @@ -0,0 +1,32 @@ +package umc.study.service.AdminService; + +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.study.converter.MissionConverter; +import umc.study.domain.Mission; +import umc.study.domain.Store; +import umc.study.repository.AdminRepository.AdminRepository; +import umc.study.repository.MissionRepository.MissionRepositoryImpl; +import umc.study.repository.StoreRepository.StoreRepository; +import umc.study.web.dto.MissionRequestDTO; + +@Service +@RequiredArgsConstructor +public class AdminServiceImpl implements AdminService { + + private final AdminRepository adminRepository; + private final StoreRepository storeRepository; + + @Override + public Mission saveMission(Long storeId, MissionRequestDTO.MissionDto request){ + // 관리자 인증을 여기서 하는건가..? + // 일단 패스 + + Store store = storeRepository.getReferenceById(storeId); + + Mission mission = MissionConverter.toMission(store, request); + //mission.setStore(store); + store.addMission(mission); + return adminRepository.save(mission); + } +} diff --git a/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java index fcc6ff9..ed8f8e3 100644 --- a/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java @@ -28,7 +28,8 @@ public Review save(Long userId, Long storeId, ReviewRequestDTO.ReviewDto request Store store = storeRepository.getReferenceById(storeId); // 리뷰 생성 Review review = ReviewConverter.toReview(user,request); - review.setUser(user); // 사용자 정보 매핑 + review.setUser(user); // 사용자 정보 매핑 , 근데 위에서 user을 한번 넣어서 필요 없는거 같기도 한데 + // 물어보기 review.setStore(store); store.addReview(review); diff --git a/hajin/src/main/java/umc/study/web/controller/AdminRestController.java b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java new file mode 100644 index 0000000..6915fdc --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java @@ -0,0 +1,30 @@ +package umc.study.web.controller; + + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import umc.study.apiPayload.ApiResponse; +import umc.study.converter.MissionConverter; +import umc.study.converter.ReviewConverter; +import umc.study.domain.Mission; +import umc.study.service.AdminService.AdminService; +import umc.study.web.dto.MissionRequestDTO; +import umc.study.web.dto.MissionResponseDTO; + +@Slf4j +@RestController +@RequiredArgsConstructor +@RequestMapping("/admin") +public class AdminRestController { + + private final AdminService adminService; + + @PostMapping("/store/{storeId}/mission") + public ApiResponse addMission(@PathVariable Long storeId, @RequestBody @Valid MissionRequestDTO.MissionDto request){ + //log.info("Request Body: {}", request.getBody()); + Mission mission = adminService.saveMission(storeId, request); + return ApiResponse.onSuccess(MissionConverter.toDto(mission)); + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/MissionRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/MissionRequestDTO.java new file mode 100644 index 0000000..bfcf002 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/MissionRequestDTO.java @@ -0,0 +1,35 @@ +package umc.study.web.dto; + +import com.fasterxml.jackson.annotation.JsonFormat; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.LocalDate; +import java.time.LocalDateTime; + +public class MissionRequestDTO { + + @Getter + @Setter + public static class MissionDto{ + @NotBlank(message = "Title cannot be blank") + private String title; + + @NotBlank(message = "Body cannot be blank") + private String body; + + @NotNull(message = "Reward points cannot be null") + private Integer rewardPoint; + +// @NotNull(message = "End date cannot be null") +// @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", shape = JsonFormat.Shape.STRING) +// private LocalDateTime startDate; + +// @NotNull(message = "Start date cannot be null") +// @JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss", shape = JsonFormat.Shape.STRING) +// private LocalDateTime endDate; + } +} diff --git a/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java new file mode 100644 index 0000000..da959e9 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java @@ -0,0 +1,22 @@ +package umc.study.web.dto; + +import lombok.Builder; +import lombok.Getter; + +import java.time.LocalDateTime; + +public class MissionResponseDTO { + @Builder + @Getter + public static class MissionResultDTO { + Long missionId; + Long storeId; + String title; + String body; + Integer rewardPoint; +// LocalDateTime start_date; +// LocalDateTime end_date; + LocalDateTime created_at; + LocalDateTime updated_at; + } +} From aa842c395b7d468add4551d8f5fe57cc84cca6d4 Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Tue, 20 May 2025 00:47:17 +0900 Subject: [PATCH 05/12] =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EB=8F=84=EC=A0=84?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../study/converter/UserMissionConverter.java | 32 +++++++++++++++ .../main/java/umc/study/domain/Mission.java | 5 +++ .../src/main/java/umc/study/domain/Users.java | 5 +++ .../umc/study/domain/mapping/UserMission.java | 1 + .../MissionRepository/MissionRepository.java | 2 +- .../UserMissionRepository.java | 10 +++++ .../UserRepository/UserRepository.java | 2 +- .../UserService/UserMissionService.java | 10 +++++ .../UserService/UserMissionServiceImpl.java | 33 +++++++++++++++ .../annotation/AlreadyJoinedMission.java | 18 +++++++++ .../validation/annotation/ExistMission.java | 17 ++++++++ .../AlreadyJoinedMissionValidator.java | 40 +++++++++++++++++++ .../validator/MissionExistValidator.java | 38 ++++++++++++++++++ .../web/controller/UserRestController.java | 22 ++++++++-- .../study/web/dto/UserMissionRequestDTO.java | 19 +++++++++ .../study/web/dto/UserMissionResponseDTO.java | 24 +++++++++++ 16 files changed, 272 insertions(+), 6 deletions(-) create mode 100644 hajin/src/main/java/umc/study/converter/UserMissionConverter.java create mode 100644 hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserMissionService.java create mode 100644 hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java create mode 100644 hajin/src/main/java/umc/study/validation/annotation/AlreadyJoinedMission.java create mode 100644 hajin/src/main/java/umc/study/validation/annotation/ExistMission.java create mode 100644 hajin/src/main/java/umc/study/validation/validator/AlreadyJoinedMissionValidator.java create mode 100644 hajin/src/main/java/umc/study/validation/validator/MissionExistValidator.java create mode 100644 hajin/src/main/java/umc/study/web/dto/UserMissionRequestDTO.java create mode 100644 hajin/src/main/java/umc/study/web/dto/UserMissionResponseDTO.java diff --git a/hajin/src/main/java/umc/study/converter/UserMissionConverter.java b/hajin/src/main/java/umc/study/converter/UserMissionConverter.java new file mode 100644 index 0000000..29a68f9 --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/UserMissionConverter.java @@ -0,0 +1,32 @@ +package umc.study.converter; + +import umc.study.domain.Mission; +import umc.study.domain.Users; +import umc.study.domain.enums.MissionStatus; +import umc.study.domain.mapping.UserMission; +import umc.study.web.dto.UserMissionRequestDTO; +import umc.study.web.dto.UserMissionResponseDTO; + +import java.time.LocalDateTime; + +public class UserMissionConverter { + public static UserMissionResponseDTO.UserMissionResultDto resultDTO(UserMission userMission) { + return UserMissionResponseDTO.UserMissionResultDto.builder() + .userId(userMission.getUser().getId()) + .missionId(userMission.getMission().getId()) + .missionStatus(userMission.getMissionStatus()) + .deadLine(LocalDateTime.now().plusDays(7)) + .createdAt(LocalDateTime.now()) + .updatedAt(LocalDateTime.now()) + .build(); + } + + public static UserMission toUserMission(Users user, Mission mission, UserMissionRequestDTO.UserMissionDto request) { + return UserMission.builder() + .user(user) + .mission(mission) + .missionStatus(request.getMissionStatus()) + .confirmNumber(0) + .build(); + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/Mission.java b/hajin/src/main/java/umc/study/domain/Mission.java index 3fa8672..d486d39 100644 --- a/hajin/src/main/java/umc/study/domain/Mission.java +++ b/hajin/src/main/java/umc/study/domain/Mission.java @@ -43,4 +43,9 @@ public class Mission extends BaseEntity { public void setStore(Store store) { this.store = store; } + public void addUserMission(UserMission userMission) { + this.UserMissionList.add(userMission); + userMission.setMission(this); // 연관 관계 동기화 + } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/domain/Users.java b/hajin/src/main/java/umc/study/domain/Users.java index 18d6117..24e3701 100644 --- a/hajin/src/main/java/umc/study/domain/Users.java +++ b/hajin/src/main/java/umc/study/domain/Users.java @@ -82,4 +82,9 @@ public class Users extends BaseEntity { private List userMissionList = new ArrayList<>(); //CascadeType.All -> User의 변화에 따라 Review, FoodPreference 등의 entity가 영향을 받는다. + + public void addUserMission(UserMission usermission) { + this.userMissionList.add(usermission); + usermission.setUser(this); // 연관 관계 동기화 + } } diff --git a/hajin/src/main/java/umc/study/domain/mapping/UserMission.java b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java index 3ff633b..dfc80ab 100644 --- a/hajin/src/main/java/umc/study/domain/mapping/UserMission.java +++ b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java @@ -12,6 +12,7 @@ @Entity @Getter +@Setter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor diff --git a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java index 9101b46..50779a8 100644 --- a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java +++ b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java @@ -3,6 +3,6 @@ import org.springframework.data.jpa.repository.JpaRepository; import umc.study.domain.Mission; -public interface MissionRepository extends JpaRepository, MissionRepositoryCustom{ +public interface MissionRepository extends JpaRepository, MissionRepositoryCustom{ } diff --git a/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java new file mode 100644 index 0000000..3efddcb --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java @@ -0,0 +1,10 @@ +package umc.study.repository.UserMissionRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import umc.study.domain.mapping.UserMission; + + +public interface UserMissionRepository extends JpaRepository { + boolean existsByUserIdAndMissionId(Integer userId, Long missionId); +} diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java index a613976..cb086a6 100644 --- a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -5,5 +5,5 @@ import umc.study.domain.Users; @Repository -public interface UserRepository extends JpaRepository { +public interface UserRepository extends JpaRepository { } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java new file mode 100644 index 0000000..346ffa5 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java @@ -0,0 +1,10 @@ +package umc.study.service.UserService; + +import org.springframework.stereotype.Service; +import umc.study.domain.mapping.UserMission; +import umc.study.web.dto.UserMissionRequestDTO; + +@Service +public interface UserMissionService { + public UserMission addMission(Integer userId, Long missionId, UserMissionRequestDTO.UserMissionDto request); +} diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java new file mode 100644 index 0000000..a989221 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java @@ -0,0 +1,33 @@ +package umc.study.service.UserService; + +import jakarta.transaction.Transactional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import umc.study.converter.UserMissionConverter; +import umc.study.domain.Mission; +import umc.study.domain.Users; +import umc.study.domain.mapping.UserMission; +import umc.study.repository.MissionRepository.MissionRepository; +import umc.study.repository.UserMissionRepository.UserMissionRepository; +import umc.study.repository.UserRepository.UserRepository; +import umc.study.web.dto.UserMissionRequestDTO; + +@Service +@RequiredArgsConstructor +public class UserMissionServiceImpl implements UserMissionService{ + + private final UserRepository userRepository; + private final MissionRepository missionRepository; + private final UserMissionRepository userMissionRepository; + + @Override + @Transactional //jakarta + public UserMission addMission(Integer userId, Long missionId, UserMissionRequestDTO.UserMissionDto request) { + Users user = userRepository.getReferenceById(userId); + Mission mission = missionRepository.getReferenceById(missionId); + UserMission userMission = UserMissionConverter.toUserMission(user,mission, request); + mission.addUserMission(userMission); + user.addUserMission(userMission); + return userMissionRepository.save(userMission); + } +} diff --git a/hajin/src/main/java/umc/study/validation/annotation/AlreadyJoinedMission.java b/hajin/src/main/java/umc/study/validation/annotation/AlreadyJoinedMission.java new file mode 100644 index 0000000..cc97681 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/annotation/AlreadyJoinedMission.java @@ -0,0 +1,18 @@ +package umc.study.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.study.validation.validator.AlreadyJoinedMissionValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = AlreadyJoinedMissionValidator.class) +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface AlreadyJoinedMission { + String message() default "이미 해당 미션을 등록한 사용자입니다."; + Class[] groups() default {}; + Class[] payload() default {}; + String userIdParameterName(); // 사용자 ID 파라미터 이름 +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/validation/annotation/ExistMission.java b/hajin/src/main/java/umc/study/validation/annotation/ExistMission.java new file mode 100644 index 0000000..35615d1 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/annotation/ExistMission.java @@ -0,0 +1,17 @@ +package umc.study.validation.annotation; + +import jakarta.validation.Constraint; +import jakarta.validation.Payload; +import umc.study.validation.validator.MissionExistValidator; + +import java.lang.annotation.*; + +@Documented +@Constraint(validatedBy = MissionExistValidator.class) +@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface ExistMission { + String message() default "사용자는 이미 이 미션을 등록했습니다."; + Class[] groups() default {}; + Class[] payload() default {}; +} diff --git a/hajin/src/main/java/umc/study/validation/validator/AlreadyJoinedMissionValidator.java b/hajin/src/main/java/umc/study/validation/validator/AlreadyJoinedMissionValidator.java new file mode 100644 index 0000000..8ec3edc --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/validator/AlreadyJoinedMissionValidator.java @@ -0,0 +1,40 @@ +package umc.study.validation.validator; + +import jakarta.servlet.http.HttpServletRequest; +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; +import umc.study.repository.UserMissionRepository.UserMissionRepository; +import umc.study.validation.annotation.AlreadyJoinedMission; + +// 새로운 검증기 +@Component +@RequiredArgsConstructor +public class AlreadyJoinedMissionValidator implements ConstraintValidator { + + private final UserMissionRepository userMissionRepository; + private String userIdParameterName; + + @Override + public void initialize(AlreadyJoinedMission constraintAnnotation) { + this.userIdParameterName = constraintAnnotation.userIdParameterName(); + } + + @Override + public boolean isValid(Long missionId, ConstraintValidatorContext context) { + HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest(); + Integer userId = Integer.parseInt(request.getAttribute(userIdParameterName).toString()); // RequestContext에서 userId 가져오기 (주의: Interceptor 설정 필요) + + boolean alreadyJoined = userMissionRepository.existsByUserIdAndMissionId(userId, missionId); + if (alreadyJoined) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate("이미 해당 미션을 등록한 사용자입니다.") + .addConstraintViolation(); + return false; + } + return true; + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/validation/validator/MissionExistValidator.java b/hajin/src/main/java/umc/study/validation/validator/MissionExistValidator.java new file mode 100644 index 0000000..f311b7a --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/validator/MissionExistValidator.java @@ -0,0 +1,38 @@ +package umc.study.validation.validator; + +import jakarta.validation.ConstraintValidator; +import jakarta.validation.ConstraintValidatorContext; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.repository.UserMissionRepository.UserMissionRepository; +import umc.study.validation.annotation.ExistMission; + +@Component +@RequiredArgsConstructor +public class MissionExistValidator implements ConstraintValidator { + + private final UserMissionRepository userMissionRepository; + + @Override + public boolean isValid(Long missionId, ConstraintValidatorContext context) { + // 사용자 ID와 미션 ID로 미션이 존재하는지 확인 + boolean exists = userMissionRepository.existsByUserIdAndMissionId(getUserIdFromContext(), missionId); + if (exists) { + context.disableDefaultConstraintViolation(); + context.buildConstraintViolationWithTemplate("해당 사용자는 이미 이 미션을 등록했습니다.") + .addConstraintViolation(); + return false; + + } + return true; + } + + // 사용자 ID를 RequestContext에서 가져오기 + private Integer getUserIdFromContext() { + return (Integer) RequestContextHolder.currentRequestAttributes() + .getAttribute("userId", RequestAttributes.SCOPE_REQUEST); + } +} diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index ad73b7b..9f81da0 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -2,14 +2,18 @@ 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; +import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; import umc.study.converter.UserConverter; +import umc.study.converter.UserMissionConverter; import umc.study.domain.Users; +import umc.study.domain.mapping.UserMission; import umc.study.service.UserService.UserCommandService; +import umc.study.service.UserService.UserMissionService; +import umc.study.validation.annotation.AlreadyJoinedMission; +import umc.study.validation.annotation.ExistMission; +import umc.study.web.dto.UserMissionRequestDTO; +import umc.study.web.dto.UserMissionResponseDTO; import umc.study.web.dto.UserRequestDTO; import umc.study.web.dto.UserResponseDTO; @@ -19,6 +23,7 @@ public class UserRestController { private final UserCommandService userCommandService; + private final UserMissionService userMissionService; // Valid앞에 @RequestBody가 있었는데 없어도 되는거 아닌가?(의문) @PostMapping("/") @@ -26,4 +31,13 @@ public ApiResponse join(@RequestBody @Valid UserR Users user = userCommandService.joinMember(request); return ApiResponse.onSuccess(UserConverter.toJoinResultDTO(user)); } + + // 가게의 미션 도전하기(도전중 미션으로 추가) + @PostMapping("/{userId}/mission/{missionId}") + //@PostMapping("/mission/join") + public ApiResponse join(@PathVariable Integer userId, @PathVariable @AlreadyJoinedMission(userIdParameterName = "userId") Long missionId, @RequestBody @Valid UserMissionRequestDTO.UserMissionDto request){ + UserMission userMission = userMissionService.addMission(userId, missionId, request); + return ApiResponse.onSuccess(UserMissionConverter.resultDTO(userMission)); + } } + diff --git a/hajin/src/main/java/umc/study/web/dto/UserMissionRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/UserMissionRequestDTO.java new file mode 100644 index 0000000..e090e86 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserMissionRequestDTO.java @@ -0,0 +1,19 @@ +package umc.study.web.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.Getter; +import lombok.Setter; +import umc.study.domain.enums.MissionStatus; + +import java.time.LocalDateTime; + +public class UserMissionRequestDTO { + + @Getter + @Setter + public static class UserMissionDto{ + private MissionStatus missionStatus; + private LocalDateTime deadLine; + } + +} diff --git a/hajin/src/main/java/umc/study/web/dto/UserMissionResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/UserMissionResponseDTO.java new file mode 100644 index 0000000..e114561 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserMissionResponseDTO.java @@ -0,0 +1,24 @@ +package umc.study.web.dto; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; +import umc.study.domain.enums.MissionStatus; + +import java.time.LocalDateTime; + + +public class UserMissionResponseDTO { + + @Builder + @Getter + @Setter + public static class UserMissionResultDto { + private Integer userId; + private Long missionId; + private MissionStatus missionStatus; + private LocalDateTime deadLine; + private LocalDateTime createdAt; + private LocalDateTime updatedAt; + } +} From 46cf2e0bfd4fe5f226fcf085e5c86d14a6898336 Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Tue, 20 May 2025 01:13:21 +0900 Subject: [PATCH 06/12] =?UTF-8?q?=EB=AF=B8=EC=85=98=20=EB=8F=84=EC=A0=84?= =?UTF-8?q?=ED=95=98=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/umc/study/web/controller/AdminRestController.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hajin/src/main/java/umc/study/web/controller/AdminRestController.java b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java index 6915fdc..29de6c8 100644 --- a/hajin/src/main/java/umc/study/web/controller/AdminRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java @@ -4,6 +4,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; import umc.study.converter.MissionConverter; @@ -16,6 +17,7 @@ @Slf4j @RestController @RequiredArgsConstructor +@Validated @RequestMapping("/admin") public class AdminRestController { From 4062ff8ca962212659705c9b78ea09b3ab3f0bb4 Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Tue, 20 May 2025 06:41:29 +0900 Subject: [PATCH 07/12] =?UTF-8?q?9=EC=A3=BC=EC=B0=A8=20=EC=8B=A4=EC=8A=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../umc/study/converter/ReviewConverter.java | 1 + .../umc/study/converter/StoreConverter.java | 34 ++++++++++++++ .../ReviewRepository/ReviewRepository.java | 4 ++ .../StoreService/StoreQueryService.java | 3 ++ .../StoreService/StoreQueryServiceImpl.java | 15 ++++++ .../web/controller/AdminRestController.java | 1 - .../web/controller/StoreRestController.java | 46 +++++++++++++++++++ .../web/controller/UserRestController.java | 3 +- .../umc/study/web/dto/ReviewResponseDTO.java | 29 ++++++++++++ 9 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 hajin/src/main/java/umc/study/converter/StoreConverter.java create mode 100644 hajin/src/main/java/umc/study/web/controller/StoreRestController.java diff --git a/hajin/src/main/java/umc/study/converter/ReviewConverter.java b/hajin/src/main/java/umc/study/converter/ReviewConverter.java index 1e08111..78d10a4 100644 --- a/hajin/src/main/java/umc/study/converter/ReviewConverter.java +++ b/hajin/src/main/java/umc/study/converter/ReviewConverter.java @@ -24,4 +24,5 @@ public static Review toReview(Users user, ReviewRequestDTO.ReviewDto request) { .build(); } + } diff --git a/hajin/src/main/java/umc/study/converter/StoreConverter.java b/hajin/src/main/java/umc/study/converter/StoreConverter.java new file mode 100644 index 0000000..362617c --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/StoreConverter.java @@ -0,0 +1,34 @@ +package umc.study.converter; + +import org.springframework.data.domain.Page; +import umc.study.domain.Review; +import umc.study.web.dto.ReviewResponseDTO; + +import java.util.List; +import java.util.stream.Collectors; + +public class StoreConverter { + + public static ReviewResponseDTO.ReviewPreViewDTO reviewPreViewDTO(Review review){ + return ReviewResponseDTO.ReviewPreViewDTO.builder() + .ownerNickname(review.getUser().getName()) + .score(review.getScore()) + .createdAt(review.getCreatedAt().toLocalDate()) + .body(review.getContent()) + .build(); + } + public static ReviewResponseDTO.ReviewPreViewListDTO reviewPreViewListDTO(Page reviewList){ + + List reviewPreViewDTOList = reviewList.stream() + .map(StoreConverter::reviewPreViewDTO).collect(Collectors.toList()); + + return ReviewResponseDTO.ReviewPreViewListDTO.builder() + .isLast(reviewList.isLast()) + .isFirst(reviewList.isFirst()) + .totalPage(reviewList.getTotalPages()) + .totalElements(reviewList.getTotalElements()) + .listSize(reviewPreViewDTOList.size()) + .reviewList(reviewPreViewDTOList) + .build(); + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java index cd85269..f7d8bd4 100644 --- a/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java +++ b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java @@ -1,7 +1,11 @@ package umc.study.repository.ReviewRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import umc.study.domain.Review; +import umc.study.domain.Store; public interface ReviewRepository extends JpaRepository { + Page findAllByStore(Store store, PageRequest pageRequest); } diff --git a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java index e927e6b..ae96354 100644 --- a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java +++ b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java @@ -1,5 +1,7 @@ package umc.study.service.StoreService; +import org.springframework.data.domain.Page; +import umc.study.domain.Review; import umc.study.domain.Store; import java.util.List; @@ -9,4 +11,5 @@ public interface StoreQueryService { Optional findStore(Long id); List findStoresByNameAndScore(String name, Float score); + Page getReviewList(Long StoreId, Integer page); } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java index f34b2e5..654062e 100644 --- a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java @@ -1,9 +1,13 @@ package umc.study.service.StoreService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import umc.study.domain.Review; import umc.study.domain.Store; +import umc.study.repository.ReviewRepository.ReviewRepository; import umc.study.repository.StoreRepository.StoreRepository; import java.util.List; @@ -15,6 +19,7 @@ public class StoreQueryServiceImpl implements StoreQueryService{ private final StoreRepository storeRepository; + private final ReviewRepository reviewRepository; @Override public Optional findStore(Long id) { @@ -29,4 +34,14 @@ public List findStoresByNameAndScore(String name, Float score) { return filteredStores; } + + @Override + public Page getReviewList(Long StoreId, Integer page) { + + Store store = storeRepository.findById(StoreId).get(); + + Page StorePage = reviewRepository.findAllByStore(store, PageRequest.of(page, 10)); + return StorePage; + } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/web/controller/AdminRestController.java b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java index 29de6c8..960cf5b 100644 --- a/hajin/src/main/java/umc/study/web/controller/AdminRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java @@ -17,7 +17,6 @@ @Slf4j @RestController @RequiredArgsConstructor -@Validated @RequestMapping("/admin") public class AdminRestController { diff --git a/hajin/src/main/java/umc/study/web/controller/StoreRestController.java b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java new file mode 100644 index 0000000..cecb77f --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java @@ -0,0 +1,46 @@ +package umc.study.web.controller; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Controller; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.*; +import umc.study.apiPayload.ApiResponse; +import umc.study.converter.StoreConverter; +import umc.study.domain.Review; +import umc.study.service.StoreService.StoreQueryService; +import umc.study.validation.annotation.ExistStore; +import umc.study.web.dto.ReviewResponseDTO; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/stores") +public class StoreRestController { + + private final StoreQueryService storeQueryService; + + @GetMapping("/{storeId}/reviews") + @Operation(summary = "특정 가게의 리뷰 목록 조회 API",description = "특정 가게의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") + }) + public ApiResponse getReviewList(@PathVariable(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){ + Page reviewList = storeQueryService.getReviewList(storeId,page); + return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); + } +} diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index 9f81da0..d4f911c 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -2,6 +2,7 @@ import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; import umc.study.converter.UserConverter; @@ -11,7 +12,6 @@ import umc.study.service.UserService.UserCommandService; import umc.study.service.UserService.UserMissionService; import umc.study.validation.annotation.AlreadyJoinedMission; -import umc.study.validation.annotation.ExistMission; import umc.study.web.dto.UserMissionRequestDTO; import umc.study.web.dto.UserMissionResponseDTO; import umc.study.web.dto.UserRequestDTO; @@ -19,6 +19,7 @@ @RestController @RequiredArgsConstructor +@Validated @RequestMapping("/users") public class UserRestController { diff --git a/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java index a8f663f..da608d5 100644 --- a/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java @@ -1,15 +1,44 @@ package umc.study.web.dto; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; public class ReviewResponseDTO { + @Builder @Getter public static class ReviewResultDto{ Long reviewId; LocalDateTime createdAt; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ReviewPreViewListDTO { + List reviewList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class ReviewPreViewDTO { + String ownerNickname; + Float score; + String body; + LocalDate createdAt; + } } From 5bf9e3d5d2f2b6bfb610146007d7fdcd2ffe501d Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Tue, 20 May 2025 08:17:21 +0900 Subject: [PATCH 08/12] =?UTF-8?q?9=EC=A3=BC=EC=B0=A8:=20=EB=82=B4=EA=B0=80?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=ED=95=9C=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReviewRepository/ReviewRepository.java | 2 ++ .../ReviewService/ReviewServiceImpl.java | 5 ++- .../service/UserService/UserQueryService.java | 4 +++ .../UserService/UserQueryServiceImpl.java | 25 +++++++++++++- .../web/controller/UserRestController.java | 34 ++++++++++++++++--- 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java index f7d8bd4..fd88973 100644 --- a/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java +++ b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java @@ -5,7 +5,9 @@ import org.springframework.data.jpa.repository.JpaRepository; import umc.study.domain.Review; import umc.study.domain.Store; +import umc.study.domain.Users; public interface ReviewRepository extends JpaRepository { Page findAllByStore(Store store, PageRequest pageRequest); + Page findAllByUser(Users User, PageRequest pageRequest); } diff --git a/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java index ed8f8e3..9d8d69b 100644 --- a/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/ReviewService/ReviewServiceImpl.java @@ -21,9 +21,8 @@ public class ReviewServiceImpl implements ReviewService{ @Override public Review save(Long userId, Long storeId, ReviewRequestDTO.ReviewDto request) { - // 사용자 조회, 1번 사용자만 리뷰 작성 - Users user = userRepository.findAll().stream().findFirst() - .orElseThrow(() -> new IllegalArgumentException("No users available")); + + Users user = userRepository.getReferenceById(Math.toIntExact(userId)); Store store = storeRepository.getReferenceById(storeId); // 리뷰 생성 diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java index ad41fac..0beef56 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java @@ -1,4 +1,8 @@ package umc.study.service.UserService; +import org.springframework.data.domain.Page; +import umc.study.domain.Review; + public interface UserQueryService { + Page getReviewList(Integer userId, Integer page); } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java index ab8492d..af5a6b9 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -1,4 +1,27 @@ package umc.study.service.UserService; -public class UserQueryServiceImpl { +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.stereotype.Repository; +import org.springframework.stereotype.Service; +import umc.study.domain.Review; +import umc.study.domain.Users; +import umc.study.repository.ReviewRepository.ReviewRepository; +import umc.study.repository.UserRepository.UserRepository; + +@Service +@RequiredArgsConstructor +public class UserQueryServiceImpl implements UserQueryService { + private final UserRepository userRepository; + private final ReviewRepository reviewRepository; + + @Override + public Page getReviewList(Integer userId, Integer page){ + //return null; + Users user = userRepository.findById(userId).get(); + + Page UserPage = reviewRepository.findAllByUser(user, PageRequest.of(page, 10)); + return UserPage; + } } diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index d4f911c..641f925 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -1,21 +1,29 @@ package umc.study.web.controller; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.Parameters; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponses; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; +import umc.study.converter.StoreConverter; import umc.study.converter.UserConverter; import umc.study.converter.UserMissionConverter; +import umc.study.domain.Review; import umc.study.domain.Users; import umc.study.domain.mapping.UserMission; +import umc.study.service.StoreService.StoreQueryService; import umc.study.service.UserService.UserCommandService; import umc.study.service.UserService.UserMissionService; +import umc.study.service.UserService.UserQueryService; import umc.study.validation.annotation.AlreadyJoinedMission; -import umc.study.web.dto.UserMissionRequestDTO; -import umc.study.web.dto.UserMissionResponseDTO; -import umc.study.web.dto.UserRequestDTO; -import umc.study.web.dto.UserResponseDTO; +import umc.study.web.dto.*; @RestController @RequiredArgsConstructor @@ -40,5 +48,23 @@ public ApiResponse join(@PathVariab UserMission userMission = userMissionService.addMission(userId, missionId, request); return ApiResponse.onSuccess(UserMissionConverter.resultDTO(userMission)); } + + private final UserQueryService userQueryService; + + @GetMapping("/{userId}/reviews") + @Operation(summary = "사용자의 리뷰 목록 조회 API",description = "사용자의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "userId", description = "유저의 아이디, path variable 입니다!") + }) + public ApiResponse getReviewList(@PathVariable(name = "userId") Integer userId, @RequestParam(name = "page") Integer page){ + Page reviewList = userQueryService.getReviewList(userId,page); + return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); + } } From 0f0c782f71d2e7e879749d68457bea03f1868d9e Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Tue, 20 May 2025 08:23:17 +0900 Subject: [PATCH 09/12] =?UTF-8?q?9=EC=A3=BC=EC=B0=A8:=20=EB=82=B4=EA=B0=80?= =?UTF-8?q?=20=EC=9E=91=EC=84=B1=ED=95=9C=20=EB=A6=AC=EB=B7=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hajin/.DS_Store | Bin 0 -> 6148 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 hajin/.DS_Store diff --git a/hajin/.DS_Store b/hajin/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e8b377f875f66b2d8a3786a56f24001e00fe765f GIT binary patch literal 6148 zcmeHK%SyvQ6rE|SO({Ya3SADkEm&W{;wHrU14eYAQWH}&7&9el&7u^t)*tdq{2uR} znSjNtMeMyWbMA8{b0G6zjB#%s?K5UG#w=)v9F-bDcWtO)k`XzMkx#=!hG2b!Qxp5^ zfZyI^Im_5XSbYEfB+h4p!6$DtTN|5gt8I0yJO4=*{w$cyb1%ql(7Kc|2`fDaui{BD zwztn@It$`-lBt3?nn23!b(}`B@Z>y=a#ic=fYq@&W4pIlIL@#y4u;OMFBZqclfF1Q zbe7AGwX?f-dNF=ZUQ+p{>Eyt+k}ZQ3yn|v^^Xg}5BGX6kRM}M)Au&J<5Cg=(dNW|o z1*^N>G|=ja0b-zr0o)%1G(^{6sZnhm(BbtN<1IuK(D5ySC=9v=ON|f#;kp!1mvZyO z;JO_A!sNLIOO3jmaWylHV`i=%FI>$IexcGCcQsN^3=jjW3^cWA Date: Fri, 23 May 2025 22:09:35 +0900 Subject: [PATCH 10/12] =?UTF-8?q?9=EC=A3=BC=EC=B0=A8=20=EC=8B=A4=EC=8A=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 6 +--- .../umc/study/converter/MissionConverter.java | 26 +++++++++++++++ .../umc/study/converter/StoreConverter.java | 8 ++--- .../MissionRepository/MissionRepository.java | 4 +++ .../UserMissionRepository.java | 4 +++ .../UserRepository/UserRepository.java | 1 + .../StoreService/StoreQueryService.java | 4 ++- .../StoreService/StoreQueryServiceImpl.java | 19 +++++++++-- .../UserService/UserMissionService.java | 3 ++ .../UserService/UserMissionServiceImpl.java | 32 +++++++++++++++++++ .../UserService/UserQueryServiceImpl.java | 9 +++--- .../validation/annotation/ValidPage.java | 0 .../validation/validator/PageValidator.java | 0 .../web/controller/StoreRestController.java | 20 ++++++++++++ .../web/controller/UserRestController.java | 29 +++++++++++++++-- .../umc/study/web/dto/MissionResponseDTO.java | 29 +++++++++++++++++ .../umc/study/web/dto/ReviewResponseDTO.java | 5 +-- 17 files changed, 178 insertions(+), 21 deletions(-) create mode 100644 hajin/src/main/java/umc/study/validation/annotation/ValidPage.java create mode 100644 hajin/src/main/java/umc/study/validation/validator/PageValidator.java diff --git a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java index 1d9a3d3..1615cfc 100644 --- a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java +++ b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -18,11 +18,7 @@ public enum ErrorStatus implements BaseErrorCode { FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), // 멤버 관려 에러 - MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), - NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."), - - // 예시 - ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."); + MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."); private final HttpStatus httpStatus; private final String code; diff --git a/hajin/src/main/java/umc/study/converter/MissionConverter.java b/hajin/src/main/java/umc/study/converter/MissionConverter.java index b076e8f..e3fd99f 100644 --- a/hajin/src/main/java/umc/study/converter/MissionConverter.java +++ b/hajin/src/main/java/umc/study/converter/MissionConverter.java @@ -1,12 +1,16 @@ package umc.study.converter; +import org.springframework.data.domain.Page; import umc.study.domain.Mission; import umc.study.domain.Store; +import umc.study.domain.mapping.UserMission; import umc.study.web.dto.MissionRequestDTO; import umc.study.web.dto.MissionResponseDTO; import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; +import java.util.stream.Collectors; public class MissionConverter { @@ -32,4 +36,26 @@ public static Mission toMission(Store store, MissionRequestDTO.MissionDto reques //.endDate(request.getEndDate()) .build(); } + + public static MissionResponseDTO.MissionDTO missionDTO(Mission mission) { + return MissionResponseDTO.MissionDTO.builder() + .storeName(mission.getStore().getName()) // Mission 엔티티와 Store 연관 관계를 고려하여 수정 + .foodCategory(mission.getStore().getName()) + .rewardPoint(mission.getRewardPoint()) + .build(); + } + public static MissionResponseDTO.MissionListDTO missionListDTO(Page missionList) { + List missionDTOList = missionList.stream() + .map(MissionConverter::missionDTO) + .collect(Collectors.toList()); + + return MissionResponseDTO.MissionListDTO.builder() + .isLast(missionList.isLast()) + .isFirst(missionList.isFirst()) + .totalPage(missionList.getTotalPages()) + .totalElements(missionList.getTotalElements()) + .listSize(missionDTOList.size()) + .missionList(missionDTOList) + .build(); + } } diff --git a/hajin/src/main/java/umc/study/converter/StoreConverter.java b/hajin/src/main/java/umc/study/converter/StoreConverter.java index 362617c..0bf1b6a 100644 --- a/hajin/src/main/java/umc/study/converter/StoreConverter.java +++ b/hajin/src/main/java/umc/study/converter/StoreConverter.java @@ -9,8 +9,8 @@ public class StoreConverter { - public static ReviewResponseDTO.ReviewPreViewDTO reviewPreViewDTO(Review review){ - return ReviewResponseDTO.ReviewPreViewDTO.builder() + public static ReviewResponseDTO.ReviewDetailDTO reviewDetailDTO(Review review){ + return ReviewResponseDTO.ReviewDetailDTO.builder() .ownerNickname(review.getUser().getName()) .score(review.getScore()) .createdAt(review.getCreatedAt().toLocalDate()) @@ -19,8 +19,8 @@ public static ReviewResponseDTO.ReviewPreViewDTO reviewPreViewDTO(Review review) } public static ReviewResponseDTO.ReviewPreViewListDTO reviewPreViewListDTO(Page reviewList){ - List reviewPreViewDTOList = reviewList.stream() - .map(StoreConverter::reviewPreViewDTO).collect(Collectors.toList()); + List reviewPreViewDTOList = reviewList.stream() + .map(StoreConverter::reviewDetailDTO).collect(Collectors.toList()); return ReviewResponseDTO.ReviewPreViewListDTO.builder() .isLast(reviewList.isLast()) diff --git a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java index 50779a8..0af423e 100644 --- a/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java +++ b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java @@ -1,8 +1,12 @@ package umc.study.repository.MissionRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import umc.study.domain.Mission; +import umc.study.domain.Store; public interface MissionRepository extends JpaRepository, MissionRepositoryCustom{ + Page findByStore(Store store, PageRequest pageRequest); } diff --git a/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java index 3efddcb..7a1ca36 100644 --- a/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java +++ b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java @@ -1,10 +1,14 @@ package umc.study.repository.UserMissionRepository; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import umc.study.domain.Users; import umc.study.domain.mapping.UserMission; public interface UserMissionRepository extends JpaRepository { boolean existsByUserIdAndMissionId(Integer userId, Long missionId); + Page findAllByUser(Users user, PageRequest pageRequest); } diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java index cb086a6..57c955c 100644 --- a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -6,4 +6,5 @@ @Repository public interface UserRepository extends JpaRepository { + } diff --git a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java index ae96354..8c27c4a 100644 --- a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java +++ b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryService.java @@ -1,6 +1,7 @@ package umc.study.service.StoreService; import org.springframework.data.domain.Page; +import umc.study.domain.Mission; import umc.study.domain.Review; import umc.study.domain.Store; @@ -11,5 +12,6 @@ public interface StoreQueryService { Optional findStore(Long id); List findStoresByNameAndScore(String name, Float score); - Page getReviewList(Long StoreId, Integer page); + Page getReviewList(Long storeId, Integer page); + Page getMissionList(Long storeId, Integer page); } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java index 654062e..60e313e 100644 --- a/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/StoreService/StoreQueryServiceImpl.java @@ -5,8 +5,10 @@ import org.springframework.data.domain.PageRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import umc.study.domain.Mission; import umc.study.domain.Review; import umc.study.domain.Store; +import umc.study.repository.MissionRepository.MissionRepository; import umc.study.repository.ReviewRepository.ReviewRepository; import umc.study.repository.StoreRepository.StoreRepository; @@ -20,6 +22,7 @@ public class StoreQueryServiceImpl implements StoreQueryService{ private final StoreRepository storeRepository; private final ReviewRepository reviewRepository; + private final MissionRepository missionRepository; @Override public Optional findStore(Long id) { @@ -36,12 +39,24 @@ public List findStoresByNameAndScore(String name, Float score) { } @Override - public Page getReviewList(Long StoreId, Integer page) { + public Page getReviewList(Long storeId, Integer page) { - Store store = storeRepository.findById(StoreId).get(); + Store store = storeRepository.findById(storeId).get(); Page StorePage = reviewRepository.findAllByStore(store, PageRequest.of(page, 10)); return StorePage; } + @Override + public Page getMissionList(Long StoreId, Integer page) { + + Store store = storeRepository.findById(StoreId).get(); + + Page MissionPage = missionRepository.findByStore(store, PageRequest.of(page, 10)); + for (Mission mission : MissionPage) { + System.out.println(mission.getId()); + } + return MissionPage; + } + } \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java index 346ffa5..f713b3f 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java @@ -1,10 +1,13 @@ package umc.study.service.UserService; +import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; +import umc.study.domain.Mission; import umc.study.domain.mapping.UserMission; import umc.study.web.dto.UserMissionRequestDTO; @Service public interface UserMissionService { public UserMission addMission(Integer userId, Long missionId, UserMissionRequestDTO.UserMissionDto request); + public Page getMissionList(Integer userId, Integer page); } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java index a989221..3ba6f4a 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java @@ -2,6 +2,10 @@ import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.stereotype.Service; import umc.study.converter.UserMissionConverter; import umc.study.domain.Mission; @@ -12,6 +16,9 @@ import umc.study.repository.UserRepository.UserRepository; import umc.study.web.dto.UserMissionRequestDTO; +import java.util.List; +import java.util.stream.Collectors; + @Service @RequiredArgsConstructor public class UserMissionServiceImpl implements UserMissionService{ @@ -30,4 +37,29 @@ public UserMission addMission(Integer userId, Long missionId, UserMissionRequest user.addUserMission(userMission); return userMissionRepository.save(userMission); } + + @Override + @Transactional + public Page getMissionList(Integer userId, Integer page){ + + Users user = userRepository.findById(userId).get(); + + Page userMissionPage = userMissionRepository.findAllByUser(user, PageRequest.of(page, 10)); + + List missions = userMissionPage + .stream() + .map(UserMission::getMission) + .collect(Collectors.toList()); + + // 페이징 정보 가져오기 (원래의 Pageable 활용) + Pageable pageable = userMissionPage.getPageable(); + + // 전체 개수도 UserMission 기준 그대로 사용 + long total = userMissionPage.getTotalElements(); + + // List → Page + Page missionPage = new PageImpl<>(missions, pageable, total); + + return missionPage; + } } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java index af5a6b9..3ee6813 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -3,11 +3,12 @@ import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; -import org.springframework.stereotype.Repository; import org.springframework.stereotype.Service; import umc.study.domain.Review; import umc.study.domain.Users; +import umc.study.domain.mapping.UserMission; import umc.study.repository.ReviewRepository.ReviewRepository; +import umc.study.repository.UserMissionRepository.UserMissionRepository; import umc.study.repository.UserRepository.UserRepository; @Service @@ -15,13 +16,13 @@ public class UserQueryServiceImpl implements UserQueryService { private final UserRepository userRepository; private final ReviewRepository reviewRepository; + private final UserMissionRepository userMissionRepository; @Override public Page getReviewList(Integer userId, Integer page){ - //return null; Users user = userRepository.findById(userId).get(); - Page UserPage = reviewRepository.findAllByUser(user, PageRequest.of(page, 10)); - return UserPage; + Page ReviewPage = reviewRepository.findAllByUser(user, PageRequest.of(page, 10)); + return ReviewPage; } } diff --git a/hajin/src/main/java/umc/study/validation/annotation/ValidPage.java b/hajin/src/main/java/umc/study/validation/annotation/ValidPage.java new file mode 100644 index 0000000..e69de29 diff --git a/hajin/src/main/java/umc/study/validation/validator/PageValidator.java b/hajin/src/main/java/umc/study/validation/validator/PageValidator.java new file mode 100644 index 0000000..e69de29 diff --git a/hajin/src/main/java/umc/study/web/controller/StoreRestController.java b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java index cecb77f..ca4736d 100644 --- a/hajin/src/main/java/umc/study/web/controller/StoreRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java @@ -12,10 +12,13 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; +import umc.study.converter.MissionConverter; import umc.study.converter.StoreConverter; +import umc.study.domain.Mission; import umc.study.domain.Review; import umc.study.service.StoreService.StoreQueryService; import umc.study.validation.annotation.ExistStore; +import umc.study.web.dto.MissionResponseDTO; import umc.study.web.dto.ReviewResponseDTO; import java.util.List; @@ -43,4 +46,21 @@ public ApiResponse getReviewList(@PathVa Page reviewList = storeQueryService.getReviewList(storeId,page); return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); } + + //@GetMapping("/{storeId}/missions") + @GetMapping("/missions") + @Operation(summary = "특정 가게의 미션 목록 조회 API",description = "특정 가게의 미션들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") + }) + public ApiResponse getMissionList(@RequestParam(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){ + Page missionList = storeQueryService.getMissionList(storeId,page); + return ApiResponse.onSuccess(MissionConverter.missionListDTO(missionList)); + } } diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index 641f925..086f42b 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -12,9 +12,11 @@ import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import umc.study.apiPayload.ApiResponse; +import umc.study.converter.MissionConverter; import umc.study.converter.StoreConverter; import umc.study.converter.UserConverter; import umc.study.converter.UserMissionConverter; +import umc.study.domain.Mission; import umc.study.domain.Review; import umc.study.domain.Users; import umc.study.domain.mapping.UserMission; @@ -25,6 +27,8 @@ import umc.study.validation.annotation.AlreadyJoinedMission; import umc.study.web.dto.*; +import java.util.List; + @RestController @RequiredArgsConstructor @Validated @@ -32,6 +36,7 @@ public class UserRestController { private final UserCommandService userCommandService; + private final UserQueryService userQueryService; private final UserMissionService userMissionService; // Valid앞에 @RequestBody가 있었는데 없어도 되는거 아닌가?(의문) @@ -44,13 +49,12 @@ public ApiResponse join(@RequestBody @Valid UserR // 가게의 미션 도전하기(도전중 미션으로 추가) @PostMapping("/{userId}/mission/{missionId}") //@PostMapping("/mission/join") - public ApiResponse join(@PathVariable Integer userId, @PathVariable @AlreadyJoinedMission(userIdParameterName = "userId") Long missionId, @RequestBody @Valid UserMissionRequestDTO.UserMissionDto request){ + public ApiResponse joinMission(@PathVariable Integer userId, @PathVariable @AlreadyJoinedMission(userIdParameterName = "userId") Long missionId, @RequestBody @Valid UserMissionRequestDTO.UserMissionDto request){ UserMission userMission = userMissionService.addMission(userId, missionId, request); return ApiResponse.onSuccess(UserMissionConverter.resultDTO(userMission)); } - private final UserQueryService userQueryService; - + // 내가 작성한 리뷰 목록 조회 @GetMapping("/{userId}/reviews") @Operation(summary = "사용자의 리뷰 목록 조회 API",description = "사용자의 리뷰들의 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") @ApiResponses({ @@ -66,5 +70,24 @@ public ApiResponse getReviewList(@PathVa Page reviewList = userQueryService.getReviewList(userId,page); return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); } + + // 내가 진행중인 미션 목록 조회 + @GetMapping("/{userId}/missions") + @Operation(summary = "사용자의 미션 목록 조회 API",description = "사용자의 미션 목록을 조회하는 API이며, 페이징을 포함합니다. query String 으로 page 번호를 주세요") + @ApiResponses({ + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "COMMON200",description = "OK, 성공"), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH003", description = "access 토큰을 주세요!",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH004", description = "acess 토큰 만료",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "AUTH006", description = "acess 토큰 모양이 이상함",content = @Content(schema = @Schema(implementation = ApiResponse.class))), + }) + @Parameters({ + @Parameter(name = "userId", description = "유저의 아이디, path variable 입니다!") + }) + public ApiResponse getMissionList(@PathVariable(name = "userId") Integer userId, @RequestParam(name = "page") Integer page){ + // 방법 1. userId가 ?인 usermission을 페이징해서 id 값에따른 미션을 리스트로 바꾸고 또 그걸 페이징 해서 출력 + // 방법 2. jpql이나 queryDSL로 페이징 + Page missionList = userMissionService.getMissionList(userId,page); + return ApiResponse.onSuccess(MissionConverter.missionListDTO(missionList)); + } } diff --git a/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java index da959e9..231626d 100644 --- a/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java @@ -1,9 +1,13 @@ package umc.study.web.dto; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; public class MissionResponseDTO { @Builder @@ -19,4 +23,29 @@ public static class MissionResultDTO { LocalDateTime created_at; LocalDateTime updated_at; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MissionListDTO { + List missionList; + Integer listSize; + Integer totalPage; + Long totalElements; + Boolean isFirst; + Boolean isLast; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MissionDTO { + String storeName; + String foodCategory; + LocalDate deadline; + String body; // 미션 내용 (조건) + Integer rewardPoint; // 보상 + } } diff --git a/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java index da608d5..a092c67 100644 --- a/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java @@ -18,12 +18,13 @@ public static class ReviewResultDto{ LocalDateTime createdAt; } + //리뷰 조회 @Builder @Getter @NoArgsConstructor @AllArgsConstructor public static class ReviewPreViewListDTO { - List reviewList; + List reviewList; Integer listSize; Integer totalPage; Long totalElements; @@ -35,7 +36,7 @@ public static class ReviewPreViewListDTO { @Getter @NoArgsConstructor @AllArgsConstructor - public static class ReviewPreViewDTO { + public static class ReviewDetailDTO { String ownerNickname; Float score; String body; From 03e491b965256f15fc87d9873f73fd0edb4cbfd6 Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Fri, 23 May 2025 23:10:03 +0900 Subject: [PATCH 11/12] =?UTF-8?q?9=EC=A3=BC=EC=B0=A8=20=EC=8B=A4=EC=8A=B5?= =?UTF-8?q?=20+=20=ED=8E=98=EC=9D=B4=EC=A7=95=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../apiPayload/code/status/ErrorStatus.java | 4 ++- .../main/java/umc/study/config/WebConfig.java | 16 +++++++++ .../validation/annotation/PageParam.java | 10 ++++++ .../validation/annotation/ValidPage.java | 0 .../resolver/PageParamResolver.java | 36 +++++++++++++++++++ .../validation/validator/PageValidator.java | 0 .../web/controller/StoreRestController.java | 5 +-- .../web/controller/UserRestController.java | 5 +-- 8 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 hajin/src/main/java/umc/study/config/WebConfig.java create mode 100644 hajin/src/main/java/umc/study/validation/annotation/PageParam.java delete mode 100644 hajin/src/main/java/umc/study/validation/annotation/ValidPage.java create mode 100644 hajin/src/main/java/umc/study/validation/resolver/PageParamResolver.java delete mode 100644 hajin/src/main/java/umc/study/validation/validator/PageValidator.java diff --git a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java index 1615cfc..4b72e1e 100644 --- a/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java +++ b/hajin/src/main/java/umc/study/apiPayload/code/status/ErrorStatus.java @@ -18,7 +18,9 @@ public enum ErrorStatus implements BaseErrorCode { FOOD_CATEGORY_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), // 멤버 관려 에러 - MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."); + MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), + // 페이징 에러 + PAGE_INVALID(HttpStatus.BAD_REQUEST, "PAGE001", "page는 1 이상의 값이어야 합니다."); private final HttpStatus httpStatus; private final String code; diff --git a/hajin/src/main/java/umc/study/config/WebConfig.java b/hajin/src/main/java/umc/study/config/WebConfig.java new file mode 100644 index 0000000..7d70aa3 --- /dev/null +++ b/hajin/src/main/java/umc/study/config/WebConfig.java @@ -0,0 +1,16 @@ +package umc.study.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; +import umc.study.validation.resolver.PageParamResolver; + +import java.util.List; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + @Override + public void addArgumentResolvers(List resolvers) { + resolvers.add(new PageParamResolver()); + } +} diff --git a/hajin/src/main/java/umc/study/validation/annotation/PageParam.java b/hajin/src/main/java/umc/study/validation/annotation/PageParam.java new file mode 100644 index 0000000..b165642 --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/annotation/PageParam.java @@ -0,0 +1,10 @@ +package umc.study.validation.annotation; + +import java.lang.annotation.*; + +@Documented +@Target({ElementType.PARAMETER}) +@Retention(RetentionPolicy.RUNTIME) +public @interface PageParam { + int defaultValue() default 1; +} diff --git a/hajin/src/main/java/umc/study/validation/annotation/ValidPage.java b/hajin/src/main/java/umc/study/validation/annotation/ValidPage.java deleted file mode 100644 index e69de29..0000000 diff --git a/hajin/src/main/java/umc/study/validation/resolver/PageParamResolver.java b/hajin/src/main/java/umc/study/validation/resolver/PageParamResolver.java new file mode 100644 index 0000000..2745e8c --- /dev/null +++ b/hajin/src/main/java/umc/study/validation/resolver/PageParamResolver.java @@ -0,0 +1,36 @@ +package umc.study.validation.resolver; + +import org.springframework.core.MethodParameter; +import org.springframework.web.bind.support.WebDataBinderFactory; +import org.springframework.web.context.request.NativeWebRequest; +import org.springframework.web.method.support.HandlerMethodArgumentResolver; +import org.springframework.web.method.support.ModelAndViewContainer; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.GeneralException; +import umc.study.validation.annotation.PageParam; + +public class PageParamResolver implements HandlerMethodArgumentResolver { + + @Override + public boolean supportsParameter(MethodParameter parameter) { + return parameter.hasParameterAnnotation(PageParam.class) && + parameter.getParameterType().equals(Integer.class); + } + + @Override + public Object resolveArgument(MethodParameter parameter, + ModelAndViewContainer mavContainer, + NativeWebRequest webRequest, + WebDataBinderFactory binderFactory) { + + PageParam pageParam = parameter.getParameterAnnotation(PageParam.class); + String pageStr = webRequest.getParameter("page"); + int page = pageStr != null ? Integer.parseInt(pageStr) : pageParam.defaultValue(); + + if (page <= 0) { + throw new GeneralException(ErrorStatus.PAGE_INVALID); // 공통 예외로 처리 + } + + return page - 1; // 내부적으로 0부터 시작 + } +} diff --git a/hajin/src/main/java/umc/study/validation/validator/PageValidator.java b/hajin/src/main/java/umc/study/validation/validator/PageValidator.java deleted file mode 100644 index e69de29..0000000 diff --git a/hajin/src/main/java/umc/study/web/controller/StoreRestController.java b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java index ca4736d..189660b 100644 --- a/hajin/src/main/java/umc/study/web/controller/StoreRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java @@ -18,6 +18,7 @@ import umc.study.domain.Review; import umc.study.service.StoreService.StoreQueryService; import umc.study.validation.annotation.ExistStore; +import umc.study.validation.annotation.PageParam; import umc.study.web.dto.MissionResponseDTO; import umc.study.web.dto.ReviewResponseDTO; @@ -42,7 +43,7 @@ public class StoreRestController { @Parameters({ @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") }) - public ApiResponse getReviewList(@PathVariable(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){ + public ApiResponse getReviewList(@PathVariable(name = "storeId") Long storeId, @PageParam Integer page){ Page reviewList = storeQueryService.getReviewList(storeId,page); return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); } @@ -59,7 +60,7 @@ public ApiResponse getReviewList(@PathVa @Parameters({ @Parameter(name = "storeId", description = "가게의 아이디, path variable 입니다!") }) - public ApiResponse getMissionList(@RequestParam(name = "storeId") Long storeId, @RequestParam(name = "page") Integer page){ + public ApiResponse getMissionList(@RequestParam(name = "storeId") Long storeId, @PageParam Integer page){ Page missionList = storeQueryService.getMissionList(storeId,page); return ApiResponse.onSuccess(MissionConverter.missionListDTO(missionList)); } diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index 086f42b..9ce494a 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -25,6 +25,7 @@ import umc.study.service.UserService.UserMissionService; import umc.study.service.UserService.UserQueryService; import umc.study.validation.annotation.AlreadyJoinedMission; +import umc.study.validation.annotation.PageParam; import umc.study.web.dto.*; import java.util.List; @@ -66,7 +67,7 @@ public ApiResponse joinMission(@Pat @Parameters({ @Parameter(name = "userId", description = "유저의 아이디, path variable 입니다!") }) - public ApiResponse getReviewList(@PathVariable(name = "userId") Integer userId, @RequestParam(name = "page") Integer page){ + public ApiResponse getReviewList(@PathVariable(name = "userId") Integer userId, @PageParam Integer page){ Page reviewList = userQueryService.getReviewList(userId,page); return ApiResponse.onSuccess(StoreConverter.reviewPreViewListDTO(reviewList)); } @@ -83,7 +84,7 @@ public ApiResponse getReviewList(@PathVa @Parameters({ @Parameter(name = "userId", description = "유저의 아이디, path variable 입니다!") }) - public ApiResponse getMissionList(@PathVariable(name = "userId") Integer userId, @RequestParam(name = "page") Integer page){ + public ApiResponse getMissionList(@PathVariable(name = "userId") Integer userId, @PageParam Integer page){ // 방법 1. userId가 ?인 usermission을 페이징해서 id 값에따른 미션을 리스트로 바꾸고 또 그걸 페이징 해서 출력 // 방법 2. jpql이나 queryDSL로 페이징 Page missionList = userMissionService.getMissionList(userId,page); From aba1eaa889bc30ba70de5760b008893b400e2f3b Mon Sep 17 00:00:00 2001 From: Hajin99 Date: Mon, 9 Jun 2025 01:44:51 +0900 Subject: [PATCH 12/12] =?UTF-8?q?10=EC=A3=BC=EC=B0=A8=20=EA=B3=BC=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hajin/.DS_Store | Bin 6148 -> 6148 bytes hajin/build.gradle | 15 ++++ hajin/src/.DS_Store | Bin 0 -> 6148 bytes hajin/src/main/.DS_Store | Bin 0 -> 6148 bytes hajin/src/main/java/.DS_Store | Bin 0 -> 6148 bytes hajin/src/main/java/umc/.DS_Store | Bin 0 -> 6148 bytes hajin/src/main/java/umc/study/.DS_Store | Bin 0 -> 8196 bytes .../src/main/java/umc/study/Application.java | 3 + .../apiPayload/code/status/ErrorStatus.java | 6 +- .../exception/handler/UserHandler.java | 10 +++ .../study/config/properties/Constants.java | 6 ++ .../config/properties/JwtProperties.java | 31 +++++++ .../security/CustomUserDetailsService.java | 29 ++++++ .../study/config/security/SecurityConfig.java | 53 +++++++++++ .../security/jwt/JwtAuthenticationFilter.java | 43 +++++++++ .../config/security/jwt/JwtTokenProvider.java | 84 ++++++++++++++++++ .../umc/study/converter/UserConverter.java | 22 ++++- .../src/main/java/umc/study/domain/.DS_Store | Bin 0 -> 6148 bytes .../src/main/java/umc/study/domain/Users.java | 37 +++++--- .../java/umc/study/domain/enums/Role.java | 5 ++ .../main/java/umc/study/repository/.DS_Store | Bin 0 -> 6148 bytes .../UserRepository/UserRepository.java | 4 +- .../src/main/java/umc/study/service/.DS_Store | Bin 0 -> 6148 bytes .../UserService/UserCommandService.java | 5 +- .../UserService/UserCommandServiceImpl.java | 55 ++++++++++-- .../UserService/UserMissionService.java | 3 + .../UserService/UserMissionServiceImpl.java | 9 +- .../service/UserService/UserQueryService.java | 3 + .../UserService/UserQueryServiceImpl.java | 20 ++++- hajin/src/main/java/umc/study/web/.DS_Store | Bin 0 -> 6148 bytes .../web/controller/MemberViewController.java | 72 +++++++++++++++ .../web/controller/UserRestController.java | 19 +++- .../umc/study/web/dto/UserRequestDTO.java | 38 +++++++- .../umc/study/web/dto/UserResponseDTO.java | 19 ++++ hajin/src/main/resources/.DS_Store | Bin 0 -> 6148 bytes hajin/src/main/resources/templates/admin.html | 10 +++ hajin/src/main/resources/templates/home.html | 20 +++++ hajin/src/main/resources/templates/login.html | 26 ++++++ .../src/main/resources/templates/signup.html | 60 +++++++++++++ 39 files changed, 679 insertions(+), 28 deletions(-) create mode 100644 hajin/src/.DS_Store create mode 100644 hajin/src/main/.DS_Store create mode 100644 hajin/src/main/java/.DS_Store create mode 100644 hajin/src/main/java/umc/.DS_Store create mode 100644 hajin/src/main/java/umc/study/.DS_Store create mode 100644 hajin/src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java create mode 100644 hajin/src/main/java/umc/study/config/properties/Constants.java create mode 100644 hajin/src/main/java/umc/study/config/properties/JwtProperties.java create mode 100644 hajin/src/main/java/umc/study/config/security/CustomUserDetailsService.java create mode 100644 hajin/src/main/java/umc/study/config/security/SecurityConfig.java create mode 100644 hajin/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java create mode 100644 hajin/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java create mode 100644 hajin/src/main/java/umc/study/domain/.DS_Store create mode 100644 hajin/src/main/java/umc/study/domain/enums/Role.java create mode 100644 hajin/src/main/java/umc/study/repository/.DS_Store create mode 100644 hajin/src/main/java/umc/study/service/.DS_Store create mode 100644 hajin/src/main/java/umc/study/web/.DS_Store create mode 100644 hajin/src/main/java/umc/study/web/controller/MemberViewController.java create mode 100644 hajin/src/main/resources/.DS_Store create mode 100644 hajin/src/main/resources/templates/admin.html create mode 100644 hajin/src/main/resources/templates/home.html create mode 100644 hajin/src/main/resources/templates/login.html create mode 100644 hajin/src/main/resources/templates/signup.html diff --git a/hajin/.DS_Store b/hajin/.DS_Store index e8b377f875f66b2d8a3786a56f24001e00fe765f..e4b2d99115e29859866e11c09b63f21d954a5a5d 100644 GIT binary patch delta 21 ccmZoMXffC@jgiCBz(7aA*vxSAJjOIJ07iud1poj5 delta 21 ccmZoMXffC@jgiC5%u+|e$kJ@{JjOIJ07oS5Z<-XrW7Fug&r5Y7EGxYi<8H&#u)c!afdO7F=j(UbgvC7=SS9yfQ05L!e5Cfab zfH@QF=H^mBD<=ksfgc#a{XsxO^bD37)z$$WUY{{;A)<=_`4&ofwR)a8t;nPD6=bM<)PYIg7omCm@Qk$Pf)7+7VXrcDRW|8w|d);{uA zOUNPyh=G5`0JjFgz=uVdv-R8Z@T?Wk?xCSzUWp0_=qr~1FmNAfsi2Mv)FIC^SZc&k T(67n?=^~&Ap^g~%1qMCS5T0$TCWz33LXQhx3%005@e-=OfDt{Y)Wigh#;i2`Ig~=q`a(X5&*RMQ zw%8x9B4q|{Pc;Rn`lVEbi)rB#JTQ_BzQTHEgSX7UeqEHw%vIxSeWkG&*iH>TLg za!}Am>w)YQsd!0cbG1+SVPpoF0cL<0uzRSp$#gIS%m6bm#{lgQ5|z-iSQyk>2M)Rg zK%{G=7PP6Cpd4w@vsf6!6%=7o5lyPFEru}Z=$AImvsf53=^$+LA?%xl?NEe%J3e3P za1fqB9+?4VV3vWhnO5oiKmWb|pH1Q!Gr$b26a%7i=AX5(Bzv|l6h~*RM7>8Pp}4}} lGzATP6k{wM#Whqd=$B+5dKL?V=t1F&fTn>5X5dd5_y#ISimU(t literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/.DS_Store b/hajin/src/main/java/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..ad33f0e947693bf96ca3c76e093fd883990fe041 GIT binary patch literal 6148 zcmeHK-AcnS6i&A3GKSC#gwfY|wYIZc7j@AT_rbHugGIQ==6*Q4Me9nbBr5eVx{k;B z(Ahs%=^~8N@k|%QqcNo1-Nflg<-W?&(OlOCCLkK3F?3qXW#4t%vfJyd+H!f)KW)q7 zZf~_}h=aqUv&-R2GD+2&p_2pKTJ|iK@D7Sy&1*1A6O}%Jr_3(12#En=fEXYKwwnQa zE{NuK(?F{y28e+T4B-ACp&@z}3xj&=fDW(E7_T9sfR1koL}}5pSQvx|2sfpGrj*+! z1~=v4mp0F{SQs?rjO&?U96NLQc;R|>@JpS}xMz@hVt^P}XP|0K2haZt_+>Ue^4Ckq zA_j4T6CWi?V0ykLBT6E1^9=L&3ZP6%f$ZE&*WRKGM=b6PKt%o@cQzh@+ri Rmjlv8Kode8G4Klvd;u(fNreCa literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/umc/.DS_Store b/hajin/src/main/java/umc/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..b1f9f4ded3e0bcf349203b9e93f72c63f372b99b GIT binary patch literal 6148 zcmeHK%}T>S5Z>*N-BN@e6nb3nTCk;BEM7vaFJMFuDm5WRgE3p0)FP#jv%Zi|;`2DO zyMY#qM-e*%yWi~m>}Ed5{xHV4I}5vvH5p?98X`w!gP?h%t7L-_xt`-Sn5UB6j8 z{-z1Py~i2&HlQ>GVPUn-C%9ZL)P1Hm~+8#ebj6%7K2WwEqnd$vMm?K zgOj#A>h+h)y4XKBJiQn_#m|X)GemM=T+6P-0^UJcsp!RiP$a=z~g4nz#nDnUbU%q!jY3f5>0r z@A1sX$~xVB6|MKe+>_4Slk^;DCO0J_F<+-gL=z$kFf@)2k!}dRuE$OsxEak8;1lI^ zp0xEUDO9pk%@gbZJHQUG1MC1h@HaSsJzGa`%f4?{IkN-oz<=q0zCTC|jiJNNqPcY- znHB)p$FvC?qYp3~=`eKISu|9f({>NSQH5(Ugr#F$>T+P{u(N3CBrKhTyRvW{iZE9P zPnvQPp+(N@06VbjfZqEDv?N6(ef0YG@5!pj>t;6lAr5-|JA+{`3=V_$(I?eLP28;O zd0f5KpI#|dBv;3A@;cAj#rXbnRW@;6W|bl2X@-(_Z}Ku#?Od(Pv^LzxE(D`sv=~n| zo735BDvwW&wo|!za{6>CA0MA=x1->}!$;3vEd!DRszNWNZ774zi zuKP{wWmJ`gD$g;mPd1p8;On{{G*m z@n?2`9oQoWME@*0n?umm1GTSB_u3NU1BMP=*I6_aB-4%qO*; new UsernameNotFoundException("해당 이메일을 가진 유저가 존재하지 않습니다: " + username)); + + return org.springframework.security.core.userdetails.User + .withUsername(user.getEmail()) + .password(user.getPassword()) + .roles(user.getRole().name()) + .build(); + } + +} diff --git a/hajin/src/main/java/umc/study/config/security/SecurityConfig.java b/hajin/src/main/java/umc/study/config/security/SecurityConfig.java new file mode 100644 index 0000000..d3efab2 --- /dev/null +++ b/hajin/src/main/java/umc/study/config/security/SecurityConfig.java @@ -0,0 +1,53 @@ +package umc.study.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; +import org.springframework.security.crypto.password.PasswordEncoder; +import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import umc.study.config.security.jwt.JwtAuthenticationFilter; +import umc.study.config.security.jwt.JwtTokenProvider; + +// 우리가 직접 작성한 보안 설정이 Spring Security의 기본 설정보다 우선 적용 +@EnableWebSecurity +@Configuration +@RequiredArgsConstructor +public class SecurityConfig { + + private final JwtTokenProvider jwtTokenProvider; + + @Bean + public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { + http + .authorizeHttpRequests((requests) -> requests + .requestMatchers("/", "/home", "/signup", "/css/**", "/users/**", "/members/signup", "/users/join", "/users/login", "/swagger-ui/**", "/v3/api-docs/**").permitAll() + .requestMatchers("/admin/**").hasRole("ADMIN") + .anyRequest().authenticated() + ) + + .formLogin((form) -> form + .loginPage("/login") + .defaultSuccessUrl("/home", true) + .permitAll() + ) + .logout((logout) -> logout + .logoutUrl("/logout") + .logoutSuccessUrl("/login?logout") + .permitAll() + ) + .csrf() + .disable() + .addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider), UsernamePasswordAuthenticationFilter.class); + + return http.build(); + } + + @Bean + public PasswordEncoder passwordEncoder() { + return new BCryptPasswordEncoder(); + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java b/hajin/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java new file mode 100644 index 0000000..76c5d68 --- /dev/null +++ b/hajin/src/main/java/umc/study/config/security/jwt/JwtAuthenticationFilter.java @@ -0,0 +1,43 @@ +package umc.study.config.security.jwt; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.util.StringUtils; +import org.springframework.web.filter.OncePerRequestFilter; +import umc.study.config.properties.Constants; + +import java.io.IOException; + +@RequiredArgsConstructor +public class JwtAuthenticationFilter extends OncePerRequestFilter { + + private final JwtTokenProvider jwtTokenProvider; + + @Override + protected void doFilterInternal(HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain) + throws ServletException, IOException { + + String token = resolveToken(request); + + if(StringUtils.hasText(token) && jwtTokenProvider.validateToken(token)) { + Authentication authentication = jwtTokenProvider.getAuthentication(token); + SecurityContextHolder.getContext().setAuthentication(authentication); + } + filterChain.doFilter(request, response); + } + + private String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java b/hajin/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java new file mode 100644 index 0000000..8bd9cee --- /dev/null +++ b/hajin/src/main/java/umc/study/config/security/jwt/JwtTokenProvider.java @@ -0,0 +1,84 @@ +package umc.study.config.security.jwt; + +import io.jsonwebtoken.*; +import io.jsonwebtoken.security.Keys; +import jakarta.servlet.http.HttpServletRequest; +import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.User; +import org.springframework.stereotype.Component; +import org.springframework.util.StringUtils; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.properties.Constants; +import umc.study.config.properties.JwtProperties; + +import java.security.Key; +import java.util.Date; +import java.util.Collections; + +@Component +@RequiredArgsConstructor +public class JwtTokenProvider { + + private final JwtProperties jwtProperties; + + private Key getSigningKey() { + return Keys.hmacShaKeyFor(jwtProperties.getSecretKey().getBytes()); + } + + public String generateToken(Authentication authentication) { + String email = authentication.getName(); + + return Jwts.builder() + .setSubject(email) + .claim("role", authentication.getAuthorities().iterator().next().getAuthority()) + .setIssuedAt(new Date()) + .setExpiration(new Date(System.currentTimeMillis() + jwtProperties.getExpiration().getAccess())) + .signWith(getSigningKey(), SignatureAlgorithm.HS256) + .compact(); + } + + public boolean validateToken(String token) { + try { + Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token); + return true; + } catch (JwtException | IllegalArgumentException e) { + return false; + } + } + + public Authentication getAuthentication(String token) { + Claims claims = Jwts.parserBuilder() + .setSigningKey(getSigningKey()) + .build() + .parseClaimsJws(token) + .getBody(); + + String email = claims.getSubject(); + String role = claims.get("role", String.class); + + User principal = new User(email, "", Collections.singleton(() -> role)); + return new UsernamePasswordAuthenticationToken(principal, token, principal.getAuthorities()); + } + + public static String resolveToken(HttpServletRequest request) { + String bearerToken = request.getHeader(Constants.AUTH_HEADER); + if(StringUtils.hasText(bearerToken) && bearerToken.startsWith(Constants.TOKEN_PREFIX)) { + return bearerToken.substring(Constants.TOKEN_PREFIX.length()); + } + return null; + } + + public Authentication extractAuthentication(HttpServletRequest request){ + String accessToken = resolveToken(request); + if(accessToken == null || !validateToken(accessToken)) { + throw new UserHandler(ErrorStatus.INVALID_TOKEN); + } + return getAuthentication(accessToken); + } +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/converter/UserConverter.java b/hajin/src/main/java/umc/study/converter/UserConverter.java index 55958ad..4e52d91 100644 --- a/hajin/src/main/java/umc/study/converter/UserConverter.java +++ b/hajin/src/main/java/umc/study/converter/UserConverter.java @@ -17,11 +17,28 @@ public static UserResponseDTO.JoinResultDTO toJoinResultDTO(Users user) { .build(); } + public static UserResponseDTO.LoginResultDTO toLoginResultDTO(Long memberId, String accessToken) { + return UserResponseDTO.LoginResultDTO.builder() + .memberId(memberId) + .accessToken(accessToken) + .build(); + } + + public static UserResponseDTO.UserInfoDTO toUserInfoDTO(Users user){ + return UserResponseDTO.UserInfoDTO.builder() + .name(user.getName()) + .email(user.getEmail()) + .gender(user.getGender().name()) + .build(); + } + public static Users toUser(UserRequestDTO.JoinDto request) { Gender gender = null; - switch (request.getGender()) { + int genderCode = Integer.parseInt(request.getGender()); + + switch (genderCode) { case 1: gender = Gender.MALE; break; @@ -38,10 +55,13 @@ public static Users toUser(UserRequestDTO.JoinDto request) { .gender(gender) .age(request.getAge()) .name(request.getName()) + .email(request.getEmail()) // 추가된 코드 + .password(request.getPassword()) // 추가된 코드 .foodPreferenceList(new ArrayList<>()) .email(request.getEmail()) .birth(request.getBirth()) .point(request.getPoint()) + .role(request.getRole()) .build(); } } diff --git a/hajin/src/main/java/umc/study/domain/.DS_Store b/hajin/src/main/java/umc/study/domain/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f0a9cc8846f3ae4b672613af0d965457450a674a GIT binary patch literal 6148 zcmeHKOHRX33>-s&N^H7h`L4hXqN<#L3ji%bNTorly3dYlGyb#+O4+hNbW7V z@QNJ{T-YDCb^3;@M-fJ8E siGDQZMmj^ZVq&ynK6op>8|5{>=XzH-Bt|*;C@1Piz;%&Hfxl4T8$wecssI20 literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/umc/study/domain/Users.java b/hajin/src/main/java/umc/study/domain/Users.java index 24e3701..7d0c2b3 100644 --- a/hajin/src/main/java/umc/study/domain/Users.java +++ b/hajin/src/main/java/umc/study/domain/Users.java @@ -7,6 +7,7 @@ import org.hibernate.annotations.DynamicUpdate; import umc.study.domain.common.BaseEntity; import umc.study.domain.enums.Gender; +import umc.study.domain.enums.Role; import umc.study.domain.enums.SocialType; import umc.study.domain.enums.UserStatus; import umc.study.domain.mapping.FoodPreference; @@ -29,7 +30,7 @@ public class Users extends BaseEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) // jpa가 통신을 하는 dbms의 방식을 따른다는 의미. - private int id; + private Integer id; @Column(nullable = false, length = 20) private String name; @@ -40,51 +41,63 @@ public class Users extends BaseEntity { private LocalDateTime birth; - private int age; + private Integer age; @Column(nullable = false, length = 40) private String address; @ColumnDefault("0") - private int point; + @Builder.Default + private Integer point = 0; @Column(nullable = false, length = 40) private String email; - @Column(nullable = false, length = 40) + @Column(length = 40) private String phone_number; - private boolean phone_certification; + private Boolean phone_certification; @Enumerated(EnumType.STRING) @Column(columnDefinition = "VARCHAR(10) DEFAULT 'ACTIVE'") private UserStatus userStatus; @Enumerated(EnumType.STRING) //ORDINAL(순서저장)->STRING - @Column(nullable = false, length = 20) + @Column(nullable = true, length = 20) private SocialType socialType; private LocalDateTime inactiveDate; - @OneToMany(mappedBy ="user", cascade =CascadeType.ALL) + // 비밀번호, 권한 추가 + @Column(nullable = false) + private String password; + + @Enumerated(EnumType.STRING) + private Role role; + + @OneToMany(mappedBy ="user", cascade = CascadeType.ALL) private List memberAgreeList = new ArrayList<>(); - @OneToMany(mappedBy ="user", cascade =CascadeType.ALL) + @OneToMany(mappedBy ="user", cascade = CascadeType.ALL) private List foodPreferenceList = new ArrayList<>(); - @OneToMany(mappedBy ="user", cascade =CascadeType.ALL) + @OneToMany(mappedBy ="user", cascade = CascadeType.ALL) private List inquiryList = new ArrayList<>(); - @OneToMany(mappedBy ="user", cascade =CascadeType.ALL) + @OneToMany(mappedBy ="user", cascade = CascadeType.ALL) private List notificationList = new ArrayList<>(); - @OneToMany(mappedBy ="user", cascade =CascadeType.ALL) + @OneToMany(mappedBy ="user", cascade = CascadeType.ALL) private List userMissionList = new ArrayList<>(); - //CascadeType.All -> User의 변화에 따라 Review, FoodPreference 등의 entity가 영향을 받는다. + //CascadeType.All -> User의 변화에 따라 Review, FoodPreference 등의 entity가 영향을 받는다. public void addUserMission(UserMission usermission) { this.userMissionList.add(usermission); usermission.setUser(this); // 연관 관계 동기화 } + + public void encodePassword(String password) { + this.password = password; + } } diff --git a/hajin/src/main/java/umc/study/domain/enums/Role.java b/hajin/src/main/java/umc/study/domain/enums/Role.java new file mode 100644 index 0000000..7270f87 --- /dev/null +++ b/hajin/src/main/java/umc/study/domain/enums/Role.java @@ -0,0 +1,5 @@ +package umc.study.domain.enums; + +public enum Role { + ADMIN, USER +} diff --git a/hajin/src/main/java/umc/study/repository/.DS_Store b/hajin/src/main/java/umc/study/repository/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..29d5a13bf9b1c67524f444e5d650fca5f011ae22 GIT binary patch literal 6148 zcmeHK%}T>S5T0$TO%Npq1&<3}3s(F=@e*o%0prg>r8YEZFlI}VTBH&_Z zw_>S!5k$%i%zm@8GrR1!VJ8a!qBZDk0eAr5pb}PGY`zd0C!LXs^$-d@#|cEx2L(CA zGtun$j||Y+t-u+cSOgh-KEDFbF@vXYnD@g(g&6l5_LDd-n$0&+sn!-2>rUNSa;}3r zH4KVwG00op?2>w?N+sdM?uO@aH0*e*M=CA4aT;YhA?`&Oa&-}>JvD5pLE6i8ZeRu+ z*Ks@E`e<~}Y&PWf-qyGwN4p364Y{+uHy*pr+Q#PLN#{O!NY%3;R^T_NW!d5cUeNe) zkB>o?CMvzfxXB!oTt;Sq8DIwHkpX+xIZN}nIo=a9zzqC~0lFU~Dxq(&G^no*Z0Prq z#%qKmXwzGQP+IgYmIiSIMVM4XlPc^JLzr~*OB?4~EDf4;5PD^t$F3~w3q|PF(Jys4 z2;U&L%m6bm%|O+3J*xl5-@pH-leotWFavYNfT*>Db_=&;YwOJBsMbo<8&ndCD-C|6 jprK1K#!@L>N7aIUNd}^Cu{4Mt6#fy=G;qTV{3!$PNBL1W literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java index 57c955c..5e4baa1 100644 --- a/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -4,7 +4,9 @@ import org.springframework.stereotype.Repository; import umc.study.domain.Users; +import java.util.Optional; + @Repository public interface UserRepository extends JpaRepository { - + Optional findByEmail(String email); } diff --git a/hajin/src/main/java/umc/study/service/.DS_Store b/hajin/src/main/java/umc/study/service/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..e7f39e32b966da6a8092cc608d3bf680bd36c487 GIT binary patch literal 6148 zcmeHK%}&BV5S|5;Vq?NV6ONmBB@u!WiI)cP1uW`84N}`^W9bsg&w-HitS{t~_&m<+ zE{O;nych#J$;>yqJJV*qWPb(#Saal80g3=%p%Uiy(5w(zCtZ?^p2;CH`3)BW7{C}p zcv^}!6NUl9z<*MZ&{~53e7E$*@xSWSMMkXE$%6c88;+*&W=`s4Ya~&Hb)-69$t`@!&$l!)_P{1DO!|0fyY)hOsXuO)-l7MCQ6` z!75mVPO&tdHY$}euhpxwGM}C{&dU6xR-erZ*5T3d`BmpN>c!$s(aG@ik+QFG4)5r! zsc!5J;z-2L7(v-KTtgyHug&COWpzI|n6qYZI~dTr#XGBiUF2uxveIar0>?H;^?mB ssE?>56qhQLDQNU{EFHRvOQ=$Cjgy1uXiOF242pRONE(bW4E!krUj>7Y&j0`b literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java b/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java index 56febb8..698fce9 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java @@ -3,8 +3,11 @@ import org.springframework.stereotype.Service; import umc.study.domain.Users; import umc.study.web.dto.UserRequestDTO; +import umc.study.web.dto.UserResponseDTO; @Service public interface UserCommandService { - public Users joinMember(UserRequestDTO.JoinDto request); + public Users joinUser(UserRequestDTO.JoinDto request); + UserResponseDTO.LoginResultDTO loginUser(UserRequestDTO.LoginRequestDTO request); + } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java index 8171852..59df2c0 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java @@ -1,10 +1,15 @@ package umc.study.service.UserService; import lombok.RequiredArgsConstructor; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import umc.study.apiPayload.code.status.ErrorStatus; import umc.study.apiPayload.exception.handler.FoodCategoryHandler; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.security.jwt.JwtTokenProvider; import umc.study.converter.UserConverter; import umc.study.converter.UserPreferConverter; import umc.study.domain.Food; @@ -13,7 +18,9 @@ import umc.study.repository.UserRepository.FoodCategoryRepository; import umc.study.repository.UserRepository.UserRepository; import umc.study.web.dto.UserRequestDTO; +import umc.study.web.dto.UserResponseDTO; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -23,19 +30,57 @@ public class UserCommandServiceImpl implements UserCommandService { private final UserRepository userRepository; private final FoodCategoryRepository foodCategoryRepository; + private final PasswordEncoder passwordEncoder; + private final JwtTokenProvider jwtTokenProvider; @Override @Transactional - public Users joinMember(UserRequestDTO.JoinDto request) { + public Users joinUser(UserRequestDTO.JoinDto request) { Users newUser = UserConverter.toUser(request); + newUser.encodePassword(passwordEncoder.encode(request.getPassword())); //userRepository.save(newUser); - List foodList = request.getPreferFood().stream() - .map( category -> { - return foodCategoryRepository.findById(category).orElseThrow(()-> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); - }).collect(Collectors.toList()); + +// List foodList = request.getPreferFood().stream() +// .map( category -> { +// return foodCategoryRepository.findById(category).orElseThrow(()-> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)); +// }).collect(Collectors.toList()); + + // "1" "2" 형식으로 받은 preferfood를 1, 2 형식으로 변환 + List categoryIds = request.getPreferFood().stream() + .map(Long::parseLong) + .toList(); + + // 카테고리 ID를 기반으로 Food 엔티티 조회 + List foodList = categoryIds.stream() + .map(categoryId -> + foodCategoryRepository.findById(categoryId) + .orElseThrow(() -> new FoodCategoryHandler(ErrorStatus.FOOD_CATEGORY_NOT_FOUND)) + ).toList(); + List foodPreferList = UserPreferConverter.toUserPreferList(foodList); foodPreferList.forEach(foodPreference->{foodPreference.setUser(newUser); }); return userRepository.save(newUser); } + @Override + public UserResponseDTO.LoginResultDTO loginUser(UserRequestDTO.LoginRequestDTO request) { + Users user = userRepository.findByEmail(request.getEmail()) + .orElseThrow(()-> new UserHandler(ErrorStatus.MEMBER_NOT_FOUND)); + + if(!passwordEncoder.matches(request.getPassword(), user.getPassword())) { + throw new UserHandler(ErrorStatus.INVALID_PASSWORD); + } + + Authentication authentication = new UsernamePasswordAuthenticationToken( + user.getEmail(), null, + Collections.singleton(() -> user.getRole().name()) + ); + + String accessToken = jwtTokenProvider.generateToken(authentication); + + return UserConverter.toLoginResultDTO( + Long.valueOf(user.getId()), + accessToken + ); + } } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java index f713b3f..972ecc4 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java @@ -1,13 +1,16 @@ package umc.study.service.UserService; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import umc.study.domain.Mission; import umc.study.domain.mapping.UserMission; import umc.study.web.dto.UserMissionRequestDTO; +import umc.study.web.dto.UserResponseDTO; @Service public interface UserMissionService { public UserMission addMission(Integer userId, Long missionId, UserMissionRequestDTO.UserMissionDto request); public Page getMissionList(Integer userId, Integer page); + } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java index 3ba6f4a..e0bacbb 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java @@ -1,12 +1,18 @@ package umc.study.service.UserService; -import jakarta.transaction.Transactional; +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.transaction.annotation.Transactional; // ✅ readOnly 가능 import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.security.jwt.JwtTokenProvider; +import umc.study.converter.UserConverter; import umc.study.converter.UserMissionConverter; import umc.study.domain.Mission; import umc.study.domain.Users; @@ -15,6 +21,7 @@ import umc.study.repository.UserMissionRepository.UserMissionRepository; import umc.study.repository.UserRepository.UserRepository; import umc.study.web.dto.UserMissionRequestDTO; +import umc.study.web.dto.UserResponseDTO; import java.util.List; import java.util.stream.Collectors; diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java index 0beef56..0ef3848 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java @@ -1,8 +1,11 @@ package umc.study.service.UserService; +import jakarta.servlet.http.HttpServletRequest; import org.springframework.data.domain.Page; import umc.study.domain.Review; +import umc.study.web.dto.UserResponseDTO; public interface UserQueryService { Page getReviewList(Integer userId, Integer page); + UserResponseDTO.UserInfoDTO getUserInfo(HttpServletRequest request); } diff --git a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java index 3ee6813..98ad851 100644 --- a/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -1,23 +1,41 @@ package umc.study.service.UserService; +import jakarta.servlet.http.HttpServletRequest; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import umc.study.apiPayload.code.status.ErrorStatus; +import umc.study.apiPayload.exception.handler.UserHandler; +import umc.study.config.security.jwt.JwtTokenProvider; +import umc.study.converter.UserConverter; import umc.study.domain.Review; import umc.study.domain.Users; import umc.study.domain.mapping.UserMission; import umc.study.repository.ReviewRepository.ReviewRepository; import umc.study.repository.UserMissionRepository.UserMissionRepository; import umc.study.repository.UserRepository.UserRepository; +import umc.study.web.dto.UserResponseDTO; @Service @RequiredArgsConstructor public class UserQueryServiceImpl implements UserQueryService { private final UserRepository userRepository; private final ReviewRepository reviewRepository; - private final UserMissionRepository userMissionRepository; + private final JwtTokenProvider jwtTokenProvider; + @Override + @Transactional(readOnly = true) + public UserResponseDTO.UserInfoDTO getUserInfo(HttpServletRequest request){ + Authentication authentication = jwtTokenProvider.extractAuthentication(request); + String email = authentication.getName(); + + Users user = userRepository.findByEmail(email) + .orElseThrow(()-> new UserHandler(ErrorStatus.MEMBER_NOT_FOUND)); + return UserConverter.toUserInfoDTO(user); + } @Override public Page getReviewList(Integer userId, Integer page){ Users user = userRepository.findById(userId).get(); diff --git a/hajin/src/main/java/umc/study/web/.DS_Store b/hajin/src/main/java/umc/study/web/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..6ed3ed3c71b749ab1e7819eee0b29285ee3afe13 GIT binary patch literal 6148 zcmeHK%}T>S5Z-O8O)NqV3Oz1(E!a{m7B8XJ7cim+m718K!I&*+Y7eE5v%Zi|;`2DO zyDjv`t4NtCv)}CQOu~Fyb~21H-W!K4#vI0&1&Ua*Vb~zJkGdu$vr+-%>KPf6EP-Fd zvNdm7_>TIJcDLaB7S53nrsskjMb`zk*>BkMd^oo0rO!o!zQcwQAOb_aY}= z?&srC&(H2~b}eNR%xyooiKEHDIk=E%?#F4Asem|)z~%lnPD45A$#EKvRIH~rEZedN zPJKGj1dN1gM~)e0pU6nP=|8UVsIS}>%#cC1`CZkoN?29aPwwvIux$n z4$l|LopDzqjl=*ku*^VNPaUlPm%sP_%Rw|F28e;JVt`lrUcUz^ncBLR9M)O^`Ur}G oeuc(G2pFmqgD;liI;av@7ia*w1`Ca_0zw}GiUt~pfj?#72h@s*MF0Q* literal 0 HcmV?d00001 diff --git a/hajin/src/main/java/umc/study/web/controller/MemberViewController.java b/hajin/src/main/java/umc/study/web/controller/MemberViewController.java new file mode 100644 index 0000000..a2eecd8 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/MemberViewController.java @@ -0,0 +1,72 @@ +//package umc.study.web.controller; +// +//import jakarta.validation.Valid; +//import lombok.RequiredArgsConstructor; +//import lombok.extern.slf4j.Slf4j; +//import org.springframework.stereotype.Controller; +//import org.springframework.ui.Model; +//import org.springframework.validation.BindingResult; +//import org.springframework.web.bind.annotation.*; +//import umc.study.apiPayload.ApiResponse; +//import umc.study.converter.UserConverter; +//import umc.study.domain.Users; +//import umc.study.service.UserService.UserCommandService; +//import umc.study.web.dto.UserRequestDTO; +//import umc.study.web.dto.UserResponseDTO; +// +//@Slf4j +//@Controller +//@RequiredArgsConstructor +//public class MemberViewController { +// +// private final UserCommandService userCommandService; +// +//// @PostMapping("/members/signup") +//// public ApiResponse join(@ModelAttribute @Valid UserRequestDTO.JoinDto request){ +//// Users user = userCommandService.joinMember(request); +//// return ApiResponse.onSuccess(UserConverter.toJoinResultDTO(user)); +//// } +// @PostMapping("/members/signup") +// public String joinMember(@ModelAttribute("memberJoinDto") UserRequestDTO.JoinDto request, // 협업시에는 기존 RequestBody 어노테이션을 붙여주시면 됩니다! +// BindingResult bindingResult, +// Model model) { +// log.info("gender={}", request.getGender()); +// log.info("role={}", request.getRole()); +// log.info("name={}", request.getName()); +// log.info("age={}", request.getAge()); +// if (bindingResult.hasErrors()) { +// // 뷰에 데이터 바인딩이 실패할 경우 signup 페이지를 유지합니다. +// return "signup"; +// } +// +// try { +// userCommandService.joinUser(request); +// return "redirect:/login"; +// } catch (Exception e) { +// // 회원가입 과정에서 에러가 발생할 경우 에러 메시지를 보내고, signup 페이디를 유지합니다. +// model.addAttribute("error", e.getMessage()); +// return "signup"; +// } +// } +// +// @GetMapping("/login") +// public String loginPage() { +// return "login"; +// } +// +// @GetMapping("/signup") +// public String signupPage(Model model) { +// model.addAttribute("memberJoinDto", new UserRequestDTO.JoinDto()); +// return "signup"; +// } +// +// @GetMapping("/home") +// public String home() { +// return "home"; +// } +// +// @GetMapping("/admin") +// public String admin() { +// return "admin"; +// } +//} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/web/controller/UserRestController.java b/hajin/src/main/java/umc/study/web/controller/UserRestController.java index 9ce494a..a4bc308 100644 --- a/hajin/src/main/java/umc/study/web/controller/UserRestController.java +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -6,6 +6,8 @@ import io.swagger.v3.oas.annotations.media.Content; import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.SecurityRequirement; +import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Page; @@ -43,10 +45,25 @@ public class UserRestController { // Valid앞에 @RequestBody가 있었는데 없어도 되는거 아닌가?(의문) @PostMapping("/") public ApiResponse join(@RequestBody @Valid UserRequestDTO.JoinDto request){ - Users user = userCommandService.joinMember(request); + Users user = userCommandService.joinUser(request); return ApiResponse.onSuccess(UserConverter.toJoinResultDTO(user)); } + @PostMapping("/login") + @Operation(summary = "유저 로그인 API",description = "유저가 로그인하는 API입니다.") + public ApiResponse login(@RequestBody @Valid UserRequestDTO.LoginRequestDTO request) { + return ApiResponse.onSuccess(userCommandService.loginUser(request)); + } + + @GetMapping("/info") + @Operation(summary = "유저 내 정보 조회 API - 인증 필요", + description = "유저가 내 정보를 조회하는 API입니다.", + security = { @SecurityRequirement(name = "JWT TOKEN") } + ) + public ApiResponse getMyInfo(HttpServletRequest request) { + return ApiResponse.onSuccess(userQueryService.getUserInfo(request)); + } + // 가게의 미션 도전하기(도전중 미션으로 추가) @PostMapping("/{userId}/mission/{missionId}") //@PostMapping("/mission/join") diff --git a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java index 4d798c3..63b5fc8 100644 --- a/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java @@ -1,6 +1,11 @@ package umc.study.web.dto; +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import lombok.Getter; +import lombok.Setter; +import umc.study.domain.enums.Role; import umc.study.validation.annotation.ExistCategories; //import umc.study.domain.enums.Gender; @@ -10,18 +15,43 @@ public class UserRequestDTO { @Getter + @Setter public static class JoinDto{ String name; - Integer gender; // 받을 때는 정수 형태로 + @NotBlank + @Email + String email; // 이메일 필드 추가 + @NotBlank + String password; // 비밀번호 필드 추가 + @NotNull + String gender; // 받을 때는 정수 형태로 +// @NotNull +// Integer gender; // 받을 때는 정수 형태로 LocalDateTime birth; Integer age; String address; Integer point; String phone_number; Boolean phone_certification; - String email; - @ExistCategories - List preferFood; + @NotNull + Role role; // 역할 필드 추가 + + //@ExistCategories + //List preferFood; + // form에서 데이터가 문자 "1" "2"로 오기 때문에 String 타입으로 받음 + List preferFood; + } + + // token 기반 로그인 위한 dto + @Getter + @Setter + public static class LoginRequestDTO{ + @NotBlank(message = "이메일은 필수입니다.") + @Email(message = "올바른 이메일 형식이어야 합니다.") + private String email; + + @NotBlank(message = "패스워드는 필수입니다.") + private String password; } } diff --git a/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java b/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java index 02cbaae..dbf3860 100644 --- a/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java +++ b/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java @@ -17,4 +17,23 @@ public static class JoinResultDTO{ Long userId; LocalDateTime createdAt; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class LoginResultDTO { + Long memberId; + String accessToken; + } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class UserInfoDTO{ + String name; + String email; + String gender; + } } diff --git a/hajin/src/main/resources/.DS_Store b/hajin/src/main/resources/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..4670db585c35d1eb5ebe86065d4f364b79070b0d GIT binary patch literal 6148 zcmeHKPfNov6i>G4T87Yr!j1v213RX};ic611+3^nWwxwnv1`WKxx*OrtY64a;^*k9x-~aoFiD7Phswe{ga>cuYr`eAaYw`16$P7+k^&bT+K)DVSub%ga$5uaD@jA)-JZ-x7$z zpl7hu2p$lwQvr18YYyF__!;(yGU0WR1S^;_oih^;a#*Y-}s8S5ESc;pV aO296#1LzqnHG&0%E&_@MYKVa!W#AoYHcgHI literal 0 HcmV?d00001 diff --git a/hajin/src/main/resources/templates/admin.html b/hajin/src/main/resources/templates/admin.html new file mode 100644 index 0000000..e1f3436 --- /dev/null +++ b/hajin/src/main/resources/templates/admin.html @@ -0,0 +1,10 @@ +Add commentMore actions + + + Admin Page + + +

Admin Page

+

관리자만 접근할 수 있는 페이지입니다.

+ + \ No newline at end of file diff --git a/hajin/src/main/resources/templates/home.html b/hajin/src/main/resources/templates/home.html new file mode 100644 index 0000000..7e92afd --- /dev/null +++ b/hajin/src/main/resources/templates/home.html @@ -0,0 +1,20 @@ + +Add commentMore actions + + Home + + +

Welcome to Home Page!

+ +

+ + + + + +
+ +
+ \ No newline at end of file diff --git a/hajin/src/main/resources/templates/login.html b/hajin/src/main/resources/templates/login.html new file mode 100644 index 0000000..64d7285 --- /dev/null +++ b/hajin/src/main/resources/templates/login.html @@ -0,0 +1,26 @@ + + + + Login + + +

Login

+
+
+ + +
+
+ + +
+ +
+ +

사용자 이름 또는 비밀번호가 잘못되었습니다.

+

로그아웃되었습니다.

+ + +

계정이 없나요? Sign up

+ + \ No newline at end of file diff --git a/hajin/src/main/resources/templates/signup.html b/hajin/src/main/resources/templates/signup.html new file mode 100644 index 0000000..4d4fc76 --- /dev/null +++ b/hajin/src/main/resources/templates/signup.html @@ -0,0 +1,60 @@ +Add commentMore actions + + + 회원가입 + + + +

회원가입

+
+
+ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+ + + +
+
+
+ + +
+ +
+ + \ No newline at end of file