diff --git a/hajin/.DS_Store b/hajin/.DS_Store new file mode 100644 index 0000000..e4b2d99 Binary files /dev/null and b/hajin/.DS_Store differ diff --git a/hajin/build.gradle b/hajin/build.gradle index 7e151dd..1c5035f 100644 --- a/hajin/build.gradle +++ b/hajin/build.gradle @@ -29,8 +29,14 @@ 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' + + // 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' @@ -54,6 +60,21 @@ dependencies { implementation 'org.springdoc:springdoc-openapi-ui:1.6.9' implementation 'org.springdoc:springdoc-openapi-data-rest:1.6.9' + // SoringBoot Security + implementation 'org.springframework.boot:spring-boot-starter-security' + testImplementation 'org.springframework.security:spring-security-test' + + // thymeleaf + implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' + + // thymeleaf 에서도 security 동작 + implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE' + + // jwt + implementation 'io.jsonwebtoken:jjwt-api:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5' + runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.11.5' + testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation "com.querydsl:querydsl-jpa" } diff --git a/hajin/src/.DS_Store b/hajin/src/.DS_Store new file mode 100644 index 0000000..8b1cc91 Binary files /dev/null and b/hajin/src/.DS_Store differ diff --git a/hajin/src/main/.DS_Store b/hajin/src/main/.DS_Store new file mode 100644 index 0000000..f2e0b7e Binary files /dev/null and b/hajin/src/main/.DS_Store differ diff --git a/hajin/src/main/java/.DS_Store b/hajin/src/main/java/.DS_Store new file mode 100644 index 0000000..ad33f0e Binary files /dev/null and b/hajin/src/main/java/.DS_Store differ diff --git a/hajin/src/main/java/umc/.DS_Store b/hajin/src/main/java/umc/.DS_Store new file mode 100644 index 0000000..b1f9f4d Binary files /dev/null and b/hajin/src/main/java/umc/.DS_Store differ diff --git a/hajin/src/main/java/umc/study/.DS_Store b/hajin/src/main/java/umc/study/.DS_Store new file mode 100644 index 0000000..c2534d2 Binary files /dev/null and b/hajin/src/main/java/umc/study/.DS_Store differ diff --git a/hajin/src/main/java/umc/study/Application.java b/hajin/src/main/java/umc/study/Application.java index dd4de3f..625213f 100644 --- a/hajin/src/main/java/umc/study/Application.java +++ b/hajin/src/main/java/umc/study/Application.java @@ -4,14 +4,17 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import umc.study.config.properties.JwtProperties; import umc.study.service.StoreService.StoreQueryService; @SpringBootApplication @EntityScan(basePackages = "umc.study.domain") @EnableJpaAuditing // 데이터베이스 엔티티의 생성과 수정 정보 자동 기록, 관리 +@EnableConfigurationProperties(JwtProperties.class) public class Application { public static void main(String[] args) { 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..e92d228 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,13 +15,16 @@ 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", "잘못된 요청입니다."), + STORE_NOT_FOUND(HttpStatus.NOT_FOUND, "COMMON400", "잘못된 요청입니다."), // 멤버 관려 에러 MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER4001", "사용자가 없습니다."), - NICKNAME_NOT_EXIST(HttpStatus.BAD_REQUEST, "MEMBER4002", "닉네임은 필수 입니다."), + INVALID_PASSWORD(HttpStatus.BAD_REQUEST, "MEMBER4003", "패스워드가 불일치합니다."), + // 페이징 에러 + PAGE_INVALID(HttpStatus.BAD_REQUEST, "PAGE001", "page는 1 이상의 값이어야 합니다."), - // 예시 - ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다."); + // 인증 에러 + INVALID_TOKEN(HttpStatus.UNAUTHORIZED, "AUTH001", "유효하지 않은 토큰입니다."); private final HttpStatus httpStatus; private final String code; 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..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() @@ -69,7 +67,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/apiPayload/exception/handler/UserHandler.java b/hajin/src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java new file mode 100644 index 0000000..5c626c2 --- /dev/null +++ b/hajin/src/main/java/umc/study/apiPayload/exception/handler/UserHandler.java @@ -0,0 +1,10 @@ +package umc.study.apiPayload.exception.handler; + +import umc.study.apiPayload.code.BaseErrorCode; +import umc.study.apiPayload.exception.GeneralException; + +public class UserHandler extends GeneralException { + public UserHandler(BaseErrorCode errorCode) { + super(errorCode); + } +} 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/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/config/properties/Constants.java b/hajin/src/main/java/umc/study/config/properties/Constants.java new file mode 100644 index 0000000..660c31f --- /dev/null +++ b/hajin/src/main/java/umc/study/config/properties/Constants.java @@ -0,0 +1,6 @@ +package umc.study.config.properties; + +public final class Constants { + public static final String AUTH_HEADER = "Authorization"; + public static final String TOKEN_PREFIX = "Bearer "; +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/config/properties/JwtProperties.java b/hajin/src/main/java/umc/study/config/properties/JwtProperties.java new file mode 100644 index 0000000..dcabc16 --- /dev/null +++ b/hajin/src/main/java/umc/study/config/properties/JwtProperties.java @@ -0,0 +1,31 @@ +package umc.study.config.properties; + +import jakarta.annotation.PostConstruct; +import lombok.Getter; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Component +@Getter +@Setter +@ConfigurationProperties("jwt.token") +public class JwtProperties { + private String secretKey=""; + private Expiration expiration; + + @Getter + @Setter + public static class Expiration{ + private Long access; + // TODO: refreshToken + } + + // 애플리케이션 시작 시 값 제대로 들어왔는지 확인 + @PostConstruct + public void testInit() { + System.out.println("jwt.secretKey = " + secretKey); + System.out.println("jwt.expiration.access = " + (expiration != null ? expiration.getAccess() : "expiration is null")); + } + +} \ No newline at end of file diff --git a/hajin/src/main/java/umc/study/config/security/CustomUserDetailsService.java b/hajin/src/main/java/umc/study/config/security/CustomUserDetailsService.java new file mode 100644 index 0000000..f369930 --- /dev/null +++ b/hajin/src/main/java/umc/study/config/security/CustomUserDetailsService.java @@ -0,0 +1,29 @@ +package umc.study.config.security; + +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.stereotype.Service; +import umc.study.domain.Users; +import umc.study.repository.UserRepository.UserRepository; + +@Service +@RequiredArgsConstructor +public class CustomUserDetailsService implements UserDetailsService { + + private final UserRepository userRepository; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + Users user = userRepository.findByEmail(username) + .orElseThrow(() -> 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/MissionConverter.java b/hajin/src/main/java/umc/study/converter/MissionConverter.java new file mode 100644 index 0000000..e3fd99f --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/MissionConverter.java @@ -0,0 +1,61 @@ +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 { + + 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(); + } + + 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/ReviewConverter.java b/hajin/src/main/java/umc/study/converter/ReviewConverter.java new file mode 100644 index 0000000..78d10a4 --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/ReviewConverter.java @@ -0,0 +1,28 @@ +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/converter/StoreConverter.java b/hajin/src/main/java/umc/study/converter/StoreConverter.java new file mode 100644 index 0000000..0bf1b6a --- /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.ReviewDetailDTO reviewDetailDTO(Review review){ + return ReviewResponseDTO.ReviewDetailDTO.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::reviewDetailDTO).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/converter/UserConverter.java b/hajin/src/main/java/umc/study/converter/UserConverter.java new file mode 100644 index 0000000..4e52d91 --- /dev/null +++ b/hajin/src/main/java/umc/study/converter/UserConverter.java @@ -0,0 +1,67 @@ +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 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; + + int genderCode = Integer.parseInt(request.getGender()); + + switch (genderCode) { + 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()) + .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/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/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/.DS_Store b/hajin/src/main/java/umc/study/domain/.DS_Store new file mode 100644 index 0000000..f0a9cc8 Binary files /dev/null and b/hajin/src/main/java/umc/study/domain/.DS_Store differ diff --git a/hajin/src/main/java/umc/study/domain/Mission.java b/hajin/src/main/java/umc/study/domain/Mission.java index bfe701b..d486d39 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,23 @@ 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; + } + 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/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/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..6be25f9 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; @@ -30,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<>(); @@ -49,4 +54,14 @@ public String toString() { '}'; } + public void addReview(Review review) { + this.ReviewList.add(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/Users.java b/hajin/src/main/java/umc/study/domain/Users.java index 05ce7b5..7d0c2b3 100644 --- a/hajin/src/main/java/umc/study/domain/Users.java +++ b/hajin/src/main/java/umc/study/domain/Users.java @@ -2,8 +2,12 @@ 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.Role; import umc.study.domain.enums.SocialType; import umc.study.domain.enums.UserStatus; import umc.study.domain.mapping.FoodPreference; @@ -17,6 +21,8 @@ @Entity @Getter +@DynamicUpdate // update시 null인경우 쿼리를 보내지 않음 +@DynamicInsert // insert시 null인경우 쿼리를 보내지 않음 @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) // 기본 생성자 @AllArgsConstructor // 모든 매개변수 생성자 @@ -24,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; @@ -35,43 +41,63 @@ public class Users extends BaseEntity { private LocalDateTime birth; + private Integer age; + @Column(nullable = false, length = 40) private String address; - private int point; + @ColumnDefault("0") + @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 phoneCertification; + 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가 영향을 받는다. + 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/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/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/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/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/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/domain/mapping/UserMission.java b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java index 669880b..dfc80ab 100644 --- a/hajin/src/main/java/umc/study/domain/mapping/UserMission.java +++ b/hajin/src/main/java/umc/study/domain/mapping/UserMission.java @@ -8,8 +8,11 @@ import umc.study.domain.common.BaseEntity; import umc.study.domain.enums.MissionStatus; +import java.time.LocalDate; + @Entity @Getter +@Setter @Builder @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -21,6 +24,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/.DS_Store b/hajin/src/main/java/umc/study/repository/.DS_Store new file mode 100644 index 0000000..29d5a13 Binary files /dev/null and b/hajin/src/main/java/umc/study/repository/.DS_Store differ 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/MissionRepository.java b/hajin/src/main/java/umc/study/repository/MissionRepository/MissionRepository.java index 9101b46..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{ +public interface MissionRepository extends JpaRepository, MissionRepositoryCustom{ + Page findByStore(Store store, PageRequest pageRequest); } 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/repository/ReviewRepository/ReviewRepository.java b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java new file mode 100644 index 0000000..fd88973 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/ReviewRepository/ReviewRepository.java @@ -0,0 +1,13 @@ +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; +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/repository/UserMissionRepository/UserMissionRepository.java b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java new file mode 100644 index 0000000..7a1ca36 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/UserMissionRepository/UserMissionRepository.java @@ -0,0 +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/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..5e4baa1 --- /dev/null +++ b/hajin/src/main/java/umc/study/repository/UserRepository/UserRepository.java @@ -0,0 +1,12 @@ +package umc.study.repository.UserRepository; + +import org.springframework.data.jpa.repository.JpaRepository; +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 0000000..e7f39e3 Binary files /dev/null and b/hajin/src/main/java/umc/study/service/.DS_Store differ 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/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..9d8d69b --- /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) { + + Users user = userRepository.getReferenceById(Math.toIntExact(userId)); + + Store store = storeRepository.getReferenceById(storeId); + // 리뷰 생성 + Review review = ReviewConverter.toReview(user,request); + review.setUser(user); // 사용자 정보 매핑 , 근데 위에서 user을 한번 넣어서 필요 없는거 같기도 한데 + // 물어보기 + + review.setStore(store); + store.addReview(review); + return reviewRepository.save(review); + } +} 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..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,5 +1,8 @@ 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; import java.util.List; @@ -9,4 +12,6 @@ public interface StoreQueryService { Optional findStore(Long id); List findStoresByNameAndScore(String name, Float score); + 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 f34b2e5..60e313e 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,15 @@ 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.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; import java.util.List; @@ -15,6 +21,8 @@ public class StoreQueryServiceImpl implements StoreQueryService{ private final StoreRepository storeRepository; + private final ReviewRepository reviewRepository; + private final MissionRepository missionRepository; @Override public Optional findStore(Long id) { @@ -29,4 +37,26 @@ 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; + } + + @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/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..698fce9 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandService.java @@ -0,0 +1,13 @@ +package umc.study.service.UserService; + +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 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 new file mode 100644 index 0000000..59df2c0 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserCommandServiceImpl.java @@ -0,0 +1,86 @@ +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; +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 umc.study.web.dto.UserResponseDTO; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +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 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()); + + // "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 new file mode 100644 index 0000000..972ecc4 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionService.java @@ -0,0 +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 new file mode 100644 index 0000000..e0bacbb --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserMissionServiceImpl.java @@ -0,0 +1,72 @@ +package umc.study.service.UserService; + +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; +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; +import umc.study.web.dto.UserResponseDTO; + +import java.util.List; +import java.util.stream.Collectors; + +@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); + } + + @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/UserQueryService.java b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java new file mode 100644 index 0000000..0ef3848 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryService.java @@ -0,0 +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 new file mode 100644 index 0000000..98ad851 --- /dev/null +++ b/hajin/src/main/java/umc/study/service/UserService/UserQueryServiceImpl.java @@ -0,0 +1,46 @@ +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 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(); + + Page ReviewPage = reviewRepository.findAllByUser(user, PageRequest.of(page, 10)); + return ReviewPage; + } +} 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/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/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/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/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/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/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/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/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/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/.DS_Store b/hajin/src/main/java/umc/study/web/.DS_Store new file mode 100644 index 0000000..6ed3ed3 Binary files /dev/null and b/hajin/src/main/java/umc/study/web/.DS_Store differ 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..960cf5b --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/AdminRestController.java @@ -0,0 +1,31 @@ +package umc.study.web.controller; + + +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; +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/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/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/controller/StoreRestController.java b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java new file mode 100644 index 0000000..189660b --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/StoreRestController.java @@ -0,0 +1,67 @@ +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.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.validation.annotation.PageParam; +import umc.study.web.dto.MissionResponseDTO; +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, @PageParam Integer page){ + 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, @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/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..a4bc308 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/controller/UserRestController.java @@ -0,0 +1,111 @@ +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 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; +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; +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.validation.annotation.PageParam; +import umc.study.web.dto.*; + +import java.util.List; + +@RestController +@RequiredArgsConstructor +@Validated +@RequestMapping("/users") +public class UserRestController { + + private final UserCommandService userCommandService; + private final UserQueryService userQueryService; + private final UserMissionService userMissionService; + + // Valid앞에 @RequestBody가 있었는데 없어도 되는거 아닌가?(의문) + @PostMapping("/") + public ApiResponse join(@RequestBody @Valid UserRequestDTO.JoinDto 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") + 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)); + } + + // 내가 작성한 리뷰 목록 조회 + @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, @PageParam Integer page){ + 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, @PageParam 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/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..231626d --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/MissionResponseDTO.java @@ -0,0 +1,51 @@ +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 + @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; + } + + @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/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..a092c67 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/ReviewResponseDTO.java @@ -0,0 +1,45 @@ +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 ReviewDetailDTO { + String ownerNickname; + Float score; + String body; + LocalDate createdAt; + } +} 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/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; + } +} 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..63b5fc8 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserRequestDTO.java @@ -0,0 +1,57 @@ +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; + +import java.time.LocalDateTime; +import java.util.List; + +public class UserRequestDTO { + + @Getter + @Setter + public static class JoinDto{ + String name; + @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; + @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 new file mode 100644 index 0000000..dbf3860 --- /dev/null +++ b/hajin/src/main/java/umc/study/web/dto/UserResponseDTO.java @@ -0,0 +1,39 @@ +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; + } + + @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 0000000..4670db5 Binary files /dev/null and b/hajin/src/main/resources/.DS_Store differ 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