diff --git a/customer-apigateway-service/src/main/java/com/justpickup/customerapigatewayservice/handler/GlobalExceptionHandler.java b/customer-apigateway-service/src/main/java/com/justpickup/customerapigatewayservice/handler/GlobalExceptionHandler.java index b40813e..8c8b633 100644 --- a/customer-apigateway-service/src/main/java/com/justpickup/customerapigatewayservice/handler/GlobalExceptionHandler.java +++ b/customer-apigateway-service/src/main/java/com/justpickup/customerapigatewayservice/handler/GlobalExceptionHandler.java @@ -44,10 +44,10 @@ public class GlobalExceptionHandler implements ErrorWebExceptionHandler { exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON); responseBody.put("code", "INVALID"); responseBody.put("message", "Invalid Access Token"); - }else{ - exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); + } else { + exchange.getResponse().setStatusCode(exchange.getResponse().getStatusCode()); exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON); - responseBody.put("code", "INVALID"); + responseBody.put("code", ex.getMessage()); } DataBuffer wrap = null; diff --git a/customer-apigateway-service/src/main/resources/application.yml b/customer-apigateway-service/src/main/resources/application.yml index ba88654..c3cb663 100644 --- a/customer-apigateway-service/src/main/resources/application.yml +++ b/customer-apigateway-service/src/main/resources/application.yml @@ -38,6 +38,7 @@ spring: - PUT - OPTIONS - DELETE + - PATCH allowedHeaders: '*' allow-credentials: true routes: @@ -57,6 +58,14 @@ spring: - AuthorizationHeaderFilter - RewritePath=/store-service/(?.*),/$\{segment} + - id: notification-service + uri: lb://NOTIFICATION-SERVICE + predicates: + - Path=/notification-service/** + filters: + - AuthorizationHeaderFilter + - RewritePath=/notification-service/(?.*),/$\{segment} + - id: user-service uri: lb://USER-SERVICE predicates: diff --git a/customer-vue/src/api/notification.js b/customer-vue/src/api/notification.js new file mode 100644 index 0000000..f0c753b --- /dev/null +++ b/customer-vue/src/api/notification.js @@ -0,0 +1,19 @@ +import axios from "axios"; + +const url = process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/notification-service"; + +export default { + requestNotification() { + return axios.get(url + "/notifications"); + }, + patchNotification(id, isRead) { + const body = { + read: isRead + } + + return axios.patch(url + "/notification/" + id, body) + }, + countsNotification() { + return axios.get(url + "/api/notification/counts"); + } +} \ No newline at end of file diff --git a/customer-vue/src/api/store.js b/customer-vue/src/api/store.js index fedeb42..90062da 100644 --- a/customer-vue/src/api/store.js +++ b/customer-vue/src/api/store.js @@ -1,32 +1,45 @@ import axios from "axios"; export default { - requestNearbyStore(latitude, longitude, storeName, page, size) { - const options = { - params: { - latitude: latitude, - longitude: longitude, - storeName: storeName, - page: page, - size: size - } + requestNearbyStore(latitude, longitude, storeName, page, size) { + const options = { + params: { + latitude: latitude, + longitude: longitude, + storeName: storeName, + page: page, + size: size } - return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', options); - }, - getCategoryList(){ - return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/'); - }, - fetchItem(itemId){ - return axios.get(process.env.VUE_APP_STORE_API_URL+'/item/'+itemId) - }, - getFavoriteStore(latitude, longitude,){ - const options = { - params: { - latitude: latitude, - longitude: longitude, - } - } - return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options) - }, - + } + return axios.get(process.env.VUE_APP_STORE_API_URL + '/store/search', options); + }, + getItemById(itemId){ + return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/item/'+itemId) + }, + getFavoriteStore(latitude, longitude,){ + const options = { + params: { + latitude: latitude, + longitude: longitude, + } + } + return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/api/customer/store/favorite',options) + }, + getCategoryList(){ + return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL+'/store-service/category/'); + }, + fetchItem(itemId){ + return axios.get(process.env.VUE_APP_STORE_API_URL+'/item/'+itemId) + }, + requestCategoriesWithItem(storeId) { + const options = { + params: { + "storeId": storeId + } + } + return axios.get(process.env.VUE_APP_STORE_API_URL + "/categories", options); + }, + requestStore(storeId) { + return axios.get(process.env.VUE_APP_CUSTOMER_SERVICE_BASEURL + "/store-service/store/" + storeId); + } } \ No newline at end of file diff --git a/customer-vue/src/components/AppNavigation.vue b/customer-vue/src/components/AppNavigation.vue index fba55f2..85e7734 100644 --- a/customer-vue/src/components/AppNavigation.vue +++ b/customer-vue/src/components/AppNavigation.vue @@ -6,29 +6,43 @@ elevation="1" > + + mdi-arrow-left + + + + + + - - - mdi-arrow-left - - - - - - - - mdi-magnify + + + mdi-bell-outline + - \ No newline at end of file diff --git a/customer-vue/src/components/StoreNavigation.vue b/customer-vue/src/components/StoreNavigation.vue new file mode 100644 index 0000000..b6e22a7 --- /dev/null +++ b/customer-vue/src/components/StoreNavigation.vue @@ -0,0 +1,32 @@ + + + + + \ No newline at end of file diff --git a/customer-vue/src/router/router.js b/customer-vue/src/router/router.js index 9a54e2f..a3afd88 100644 --- a/customer-vue/src/router/router.js +++ b/customer-vue/src/router/router.js @@ -4,6 +4,7 @@ import jwt from "@/common/jwt"; import auth from "@/api/auth"; import HomeLayout from '../views/Layout/HomeLayout.vue'; +import StoreLayout from "@/views/Layout/StoreLayout"; const ACCESS_TOKEN_NAME = "accessToken"; const EXPIRED_TIME_NAME = "expiredTime"; @@ -54,6 +55,11 @@ const routes = [ name: 'favorite-store', component: () => import('../views/FavoriteStore') }, + { + path: "/notification", + name: 'notification', + component: () => import('../views/NotificationView') + }, { path: '/login', name: 'login', @@ -71,8 +77,20 @@ const routes = [ }, ] }, - - + { + path: '/store', + redirect: 'store', + beforeEnter: authCheck, + component: StoreLayout, + children: [ + { + path: "/store/:storeId", + name: "store", + component: () => import('../views/StoreView'), + props: true + }, + ] + }, { path: '/auth', name: 'auth', diff --git a/customer-vue/src/views/Layout/HomeLayout.vue b/customer-vue/src/views/Layout/HomeLayout.vue index e0d8b4d..9d48fa3 100644 --- a/customer-vue/src/views/Layout/HomeLayout.vue +++ b/customer-vue/src/views/Layout/HomeLayout.vue @@ -1,9 +1,12 @@ + + + + \ No newline at end of file diff --git a/notification-service/build.gradle b/notification-service/build.gradle index ec207f2..3e1fa5f 100644 --- a/notification-service/build.gradle +++ b/notification-service/build.gradle @@ -34,7 +34,6 @@ dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign' /*implementation 'org.springframework.boot:spring-boot-starter-amqp'*/ - /*implementation 'org.springframework.boot:spring-boot-starter-security'*/ /*implementation 'org.springframework.cloud:spring-cloud-starter-config'*/ /*implementation 'org.springframework.kafka:spring-kafka'*/ // https://mvnrepository.com/artifact/com.github.gavlyukovskiy/p6spy-spring-boot-starter diff --git a/notification-service/src/docs/asciidoc/api-docs.adoc b/notification-service/src/docs/asciidoc/api-docs.adoc new file mode 100644 index 0000000..ef87720 --- /dev/null +++ b/notification-service/src/docs/asciidoc/api-docs.adoc @@ -0,0 +1,75 @@ +:doctype: book +:icons: font +:source-highlighter: highlightjs +:toc: left +:toclevels: 2 +:sectlinks: + + +[[overview]] += 개요 + +[[overview-http-verbs]] +== HTTP 동사 + +본 REST API에서 사용하는 HTTP 동사(verbs)는 가능한한 표준 HTTP와 REST 규약을 따릅니다. + +|=== +| 동사 | 용례 + +| `GET` +| 리소스를 가져올 때 사용 + +| `POST` +| 새 리소스를 만들 때 사용 + +| `PUT` +| 기존 리소스를 수정할 때 사용 + +| `PATCH` +| 기존 리소스의 일부를 수정할 때 사용 + +| `DELETE` +| 기존 리소스를 삭제할 떄 사용 +|=== + +[[overview-http-status-codes]] +== HTTP 상태 코드 + +본 REST API에서 사용하는 HTTP 상태 코드는 가능한 표준 HTTP와 REST 규약을 따릅니다. + +|=== +| 상태 코드 | 용례 + +| `200 OK` +| 요청을 성공적으로 처리함 + +| `201 Created` +| 새 리소스를 성공적으로 생성함. 응답의 `Location` 헤더에 해당 리소스의 URI가 담겨있다. + +| `204 No Content` +| 기존 리소스를 성공적으로 수정함. + +| `400 Bad Request` +| 잘못된 요청을 보낸 경우. 응답 본문에 더 오류에 대한 정보가 담겨있다. + +| `404 Not Found` +| 요청한 리소스가 없음. + +| `409 Conflict` +| 클라이언트의 요청이 서버의 상태와 충돌이 발생한 경우. +|=== + +[[snippets-write-convention]] +== snippets 작성 컨벤션 +domain-httpRequestCode-etc + +== 알림 +=== 알림 조회 - 회원 고유번호 +operation::notification-get[snippets='curl-request,http-request,http-response,request-headers,response-fields'] +=== 알림 수정 +operation::notification-patch[snippets='curl-request,http-request,http-response,path-parameters,request-fields,response-fields'] + +== API +=== 읽지 않은 알림 개수 조회 +operation::api-get-notification-counts[snippets='curl-request,http-request,http-response,request-headers,response-fields'] diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/FindNotificationDto.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/FindNotificationDto.java new file mode 100644 index 0000000..1b382f6 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/FindNotificationDto.java @@ -0,0 +1,28 @@ +package com.justpickup.notificationservice.domain.notification.dto; + +import com.justpickup.notificationservice.domain.notification.entity.Notification; +import com.justpickup.notificationservice.global.dto.Yn; +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.time.LocalDateTime; + +@Getter +@AllArgsConstructor(staticName = "of") +public class FindNotificationDto { + private Long id; + private Long userId; + private String message; + private String title; + private Yn readYn; + private LocalDateTime createdAt; + + public FindNotificationDto(Notification entity) { + this.id = entity.getId(); + this.userId = entity.getUserId(); + this.message = entity.getMessage(); + this.title = entity.getTitle(); + this.readYn = entity.getReadYn(); + this.createdAt = entity.getCreatedAt(); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/UpdateNotificationDto.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/UpdateNotificationDto.java new file mode 100644 index 0000000..04c4a67 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/dto/UpdateNotificationDto.java @@ -0,0 +1,11 @@ +package com.justpickup.notificationservice.domain.notification.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor(staticName = "of") +public class UpdateNotificationDto { + private Long id; + private boolean read; +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/entity/Notification.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/entity/Notification.java index e9757d7..96d159f 100644 --- a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/entity/Notification.java +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/entity/Notification.java @@ -1,5 +1,6 @@ package com.justpickup.notificationservice.domain.notification.entity; +import com.justpickup.notificationservice.global.dto.Yn; import com.justpickup.notificationservice.global.entity.BaseEntity; import lombok.AccessLevel; import lombok.Getter; @@ -13,7 +14,7 @@ import javax.persistence.*; @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Notification extends BaseEntity { - @Id @GeneratedValue + @Id @GeneratedValue @Column(name = "notification_id") private Long id; @@ -22,4 +23,24 @@ public class Notification extends BaseEntity { */ private Long userId; + private String title; + + private String message; + + @Enumerated(EnumType.STRING) + private Yn readYn; + + // == 생성 메소드 == // + public static Notification of(Long userId, String message, String title) { + Notification notification = new Notification(); + notification.userId = userId; + notification.message = message; + notification.title = title; + notification.readYn = Yn.N; + return notification; + } + + public void modifyReadYn(Yn readYn) { + this.readYn = readYn; + } } diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/exception/NotExistNotification.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/exception/NotExistNotification.java new file mode 100644 index 0000000..b3530b5 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/exception/NotExistNotification.java @@ -0,0 +1,11 @@ +package com.justpickup.notificationservice.domain.notification.exception; + +import com.justpickup.notificationservice.global.exception.CustomException; +import org.springframework.http.HttpStatus; + +public class NotExistNotification extends CustomException { + + public NotExistNotification(String message) { + super(HttpStatus.CONFLICT, message); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/repository/NotificationRepository.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/repository/NotificationRepository.java new file mode 100644 index 0000000..ae319c1 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/repository/NotificationRepository.java @@ -0,0 +1,13 @@ +package com.justpickup.notificationservice.domain.notification.repository; + +import com.justpickup.notificationservice.domain.notification.entity.Notification; +import com.justpickup.notificationservice.global.dto.Yn; +import org.springframework.data.domain.Sort; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.List; + +public interface NotificationRepository extends JpaRepository { + List findByUserId(Long userId, Sort sort); + long countByUserIdAndReadYn(Long userId, Yn readYn); +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationService.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationService.java new file mode 100644 index 0000000..3d35ecb --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationService.java @@ -0,0 +1,13 @@ +package com.justpickup.notificationservice.domain.notification.service; + +import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto; +import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto; +import com.justpickup.notificationservice.global.dto.Yn; + +import java.util.List; + +public interface NotificationService { + List findNotificationByUserId(Long id); + void updateNotification(UpdateNotificationDto dto); + Long findNotificationCounts(Long userId, Yn readYn); +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationServiceImpl.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationServiceImpl.java new file mode 100644 index 0000000..5c7d6de --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/service/NotificationServiceImpl.java @@ -0,0 +1,52 @@ +package com.justpickup.notificationservice.domain.notification.service; + +import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto; +import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto; +import com.justpickup.notificationservice.domain.notification.entity.Notification; +import com.justpickup.notificationservice.domain.notification.exception.NotExistNotification; +import com.justpickup.notificationservice.domain.notification.repository.NotificationRepository; +import com.justpickup.notificationservice.global.dto.Yn; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Sort; +import org.springframework.data.domain.Sort.Order; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) +public class NotificationServiceImpl implements NotificationService { + + private final NotificationRepository notificationRepository; + + @Override + public List findNotificationByUserId(Long userId) { + Order readYnAsc = Order.asc("readYn"); + Order createdAtDesc = Order.desc("createdAt"); + + Sort sort = Sort.by(List.of(readYnAsc, createdAtDesc)); + return notificationRepository.findByUserId(userId, sort) + .stream() + .map(FindNotificationDto::new) + .collect(Collectors.toList()); + } + + @Transactional + @Override + public void updateNotification(UpdateNotificationDto dto) { + Long id = dto.getId(); + Notification notification = notificationRepository.findById(id) + .orElseThrow(() -> new NotExistNotification(id + "는 없는 알림 고유번호입니다.")); + + Yn readYn = dto.isRead() ? Yn.Y : Yn.N; + notification.modifyReadYn(readYn); + } + + @Override + public Long findNotificationCounts(Long userId, Yn readYn) { + return notificationRepository.countByUserIdAndReadYn(userId, readYn); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiController.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiController.java new file mode 100644 index 0000000..ba2e6d5 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiController.java @@ -0,0 +1,29 @@ +package com.justpickup.notificationservice.domain.notification.web; + +import com.justpickup.notificationservice.domain.notification.service.NotificationService; +import com.justpickup.notificationservice.global.dto.Result; +import com.justpickup.notificationservice.global.dto.Yn; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestHeader; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api") +@RequiredArgsConstructor +public class NotificationApiController { + + private final NotificationService notificationService; + + @GetMapping("/notification/counts") + public ResponseEntity getNotificationCounts(@RequestHeader("user-id") String userIdHeader) { + + Long userId = Long.valueOf(userIdHeader); + Yn readYn = Yn.N; + Long counts = notificationService.findNotificationCounts(userId, readYn); + + return ResponseEntity.ok(Result.createSuccessResult(counts)); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationController.java b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationController.java new file mode 100644 index 0000000..2d3cbc1 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/domain/notification/web/NotificationController.java @@ -0,0 +1,76 @@ +package com.justpickup.notificationservice.domain.notification.web; + +import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto; +import com.justpickup.notificationservice.domain.notification.dto.UpdateNotificationDto; +import com.justpickup.notificationservice.domain.notification.service.NotificationService; +import com.justpickup.notificationservice.global.dto.Result; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequiredArgsConstructor +public class NotificationController { + + private final NotificationService notificationService; + + @GetMapping("/notifications") + public ResponseEntity getNotificationByUserId(@RequestHeader("user-id") String userIdHeader) { + Long userId = Long.valueOf(userIdHeader); + + List notifications = notificationService.findNotificationByUserId(userId); + + GetNotificationResponse response = new GetNotificationResponse(notifications); + return ResponseEntity.ok(Result.createSuccessResult(response)); + } + + @Data @NoArgsConstructor + static class GetNotificationResponse { + private List<_Notification> notifications; + + public GetNotificationResponse(List notifications) { + this.notifications = + notifications.stream().map(_Notification::new).collect(Collectors.toList()); + } + + @Data + static class _Notification { + private Long id; + private String message; + private String title; + private boolean read; + private String time; + + public _Notification(FindNotificationDto dto) { + this.id = dto.getId(); + this.message = dto.getMessage(); + this.title = dto.getTitle(); + this.read = dto.getReadYn().isY(); + this.time = dto.getCreatedAt().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")); + } + } + } + + @PatchMapping("/notification/{notificationId}") + public ResponseEntity patchNotification(@PathVariable("notificationId") Long notificationId, + @RequestBody PatchNotificationRequest notificationRequest) { + + UpdateNotificationDto dto = UpdateNotificationDto.of(notificationId, notificationRequest.isRead()); + + notificationService.updateNotification(dto); + + return ResponseEntity.ok(Result.createSuccessResult(null)); + } + + @Data @NoArgsConstructor @AllArgsConstructor + static class PatchNotificationRequest { + private boolean read; + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/SqlCommandLineRunner.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/SqlCommandLineRunner.java new file mode 100644 index 0000000..147ecaf --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/SqlCommandLineRunner.java @@ -0,0 +1,37 @@ +package com.justpickup.notificationservice.global; + +import com.justpickup.notificationservice.domain.notification.entity.Notification; +import com.justpickup.notificationservice.domain.notification.repository.NotificationRepository; +import com.justpickup.notificationservice.global.dto.Yn; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class SqlCommandLineRunner implements CommandLineRunner { + + private final NotificationRepository notificationRepository; + + @Override + @Transactional + public void run(String... args) throws Exception { + for (long userId = 1; userId < 10; userId++) { + List notifications = new ArrayList<>(); + for (int notification = 1; notification <= 5; notification++) { + Notification of = Notification.of(userId, notification + "번 매장의 주문이 수락되었습니다.", "주문이 수락되었어요"); + notifications.add(of); + } + for (int notification = 6; notification <= 10; notification++) { + Notification of = Notification.of(userId, notification + "번 매장의 주문이 수락되었습니다.", "주문이 수락되었어요"); + of.modifyReadYn(Yn.Y); + notifications.add(of); + } + notificationRepository.saveAll(notifications); + } + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Code.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Code.java new file mode 100644 index 0000000..eea0092 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Code.java @@ -0,0 +1,5 @@ +package com.justpickup.notificationservice.global.dto; + +public enum Code { + SUCCESS, ERROR +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Result.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Result.java new file mode 100644 index 0000000..3a6a7b7 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Result.java @@ -0,0 +1,44 @@ +package com.justpickup.notificationservice.global.dto; + +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data @NoArgsConstructor +public class Result { + private Code code; + private String message; + private T data; + + @Builder + public Result(Code code, String message, T data) { + this.code = code; + this.message = message; + this.data = data; + } + + public static Result createErrorResult(String message) { + return Result.builder() + .code(Code.ERROR) + .message(message) + .data(null) + .build(); + } + + // 해당 는 클래스의 T와 다름 + public static Result createSuccessResult(T data) { + return Result.builder() + .code(Code.SUCCESS) + .message("") + .data(data) + .build(); + } + + public static Result success(){ + return Result.builder() + .code(Code.SUCCESS) + .message("성공") + .data(null) + .build(); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Yn.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Yn.java new file mode 100644 index 0000000..d3ad242 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/dto/Yn.java @@ -0,0 +1,14 @@ +package com.justpickup.notificationservice.global.dto; + +import lombok.Getter; + +@Getter +public enum Yn { + Y(true), N(false); + + private boolean y; + + Yn(boolean y) { + this.y = y; + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/entity/BaseEntity.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/entity/BaseEntity.java index 613ae6e..f27ba47 100644 --- a/notification-service/src/main/java/com/justpickup/notificationservice/global/entity/BaseEntity.java +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/entity/BaseEntity.java @@ -1,14 +1,21 @@ package com.justpickup.notificationservice.global.entity; +import lombok.Getter; +import org.hibernate.annotations.CreationTimestamp; +import org.hibernate.annotations.UpdateTimestamp; + import javax.persistence.MappedSuperclass; import java.time.LocalDateTime; @MappedSuperclass +@Getter public abstract class BaseEntity { + @CreationTimestamp private LocalDateTime createdAt; private Long createdBy; + @UpdateTimestamp private LocalDateTime lastModifiedAt; private Long lastModifiedBy; diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/CustomException.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/CustomException.java new file mode 100644 index 0000000..e27cf67 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/CustomException.java @@ -0,0 +1,17 @@ +package com.justpickup.notificationservice.global.exception; + +import com.justpickup.notificationservice.global.dto.Result; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +@Getter +public class CustomException extends RuntimeException { + + private HttpStatus status; + private Result errorResult; + + protected CustomException(HttpStatus status, String message) { + this.status = status; + this.errorResult = Result.createErrorResult(message); + } +} diff --git a/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/GlobalExceptionHandler.java b/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..a763569 --- /dev/null +++ b/notification-service/src/main/java/com/justpickup/notificationservice/global/exception/GlobalExceptionHandler.java @@ -0,0 +1,55 @@ +package com.justpickup.notificationservice.global.exception; + +import com.justpickup.notificationservice.global.dto.Result; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.BindException; +import org.springframework.validation.BindingResult; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +@Slf4j +public class GlobalExceptionHandler { + + @ExceptionHandler(CustomException.class) + public ResponseEntity customExceptionHandler(CustomException ce) { + HttpStatus status = ce.getStatus(); + Result errorResult = ce.getErrorResult(); + + return ResponseEntity.status(status) + .body(errorResult); + } + + @ExceptionHandler(BindException.class) + public ResponseEntity bindExceptionHandler(BindException exception) { + return getValidationErrorBody(exception); + } + + @ExceptionHandler(MethodArgumentNotValidException.class) + public ResponseEntity methodArgumentNotValidExceptionHandler(MethodArgumentNotValidException exception) { + return getValidationErrorBody(exception); + } + + private ResponseEntity getValidationErrorBody(BindException exception) { + BindingResult bindingResult = exception.getBindingResult(); + + StringBuilder builder = new StringBuilder(); + bindingResult.getFieldErrors() + .forEach(fieldError -> { + builder.append("["); + builder.append(fieldError.getField()); + builder.append("](은)는 "); + builder.append(fieldError.getDefaultMessage()); + builder.append(" 입력된 값: ["); + builder.append(fieldError.getRejectedValue()); + builder.append("]"); + }); + + return ResponseEntity.status(HttpStatus.BAD_REQUEST) + .body(Result.createErrorResult(builder.toString())); + } + +} diff --git a/notification-service/src/test/java/com/justpickup/notificationservice/config/TestConfig.java b/notification-service/src/test/java/com/justpickup/notificationservice/config/TestConfig.java new file mode 100644 index 0000000..d4f484a --- /dev/null +++ b/notification-service/src/test/java/com/justpickup/notificationservice/config/TestConfig.java @@ -0,0 +1,18 @@ +package com.justpickup.notificationservice.config; + +import org.springframework.boot.test.autoconfigure.restdocs.RestDocsMockMvcConfigurationCustomizer; +import org.springframework.boot.test.context.TestConfiguration; +import org.springframework.context.annotation.Bean; + +import static org.springframework.restdocs.operation.preprocess.Preprocessors.prettyPrint; + +@TestConfiguration +public class TestConfig { + + @Bean + public RestDocsMockMvcConfigurationCustomizer restDocsMockMvcConfigurationCustomizer() { + return configurer -> configurer.operationPreprocessors() + .withRequestDefaults(prettyPrint()) + .withResponseDefaults(prettyPrint()); + } +} diff --git a/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiControllerTest.java b/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiControllerTest.java new file mode 100644 index 0000000..4e2242f --- /dev/null +++ b/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationApiControllerTest.java @@ -0,0 +1,69 @@ +package com.justpickup.notificationservice.domain.notification.web; + +import com.justpickup.notificationservice.config.TestConfig; +import com.justpickup.notificationservice.domain.notification.service.NotificationService; +import com.justpickup.notificationservice.global.dto.Yn; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(NotificationApiController.class) +@Import(TestConfig.class) +@AutoConfigureRestDocs(uriHost = "https://just-pickup.com", uriPort = 8000) +class NotificationApiControllerTest { + + @Autowired + MockMvc mockMvc; + + @MockBean + NotificationService notificationService; + + private final String url = "/api/"; + + @Test + @DisplayName("[API] [GET] 읽지 않은 알림 개수 가져오기") + void getNotificationCounts() throws Exception { + // GIVEN + Long userId = 1L; + given(notificationService.findNotificationCounts(userId, Yn.N)) + .willReturn(10L); + + // THEN + ResultActions actions = mockMvc.perform(get(url + "/notification/counts") + .header("user-id", String.valueOf(userId))); + + // WHEN + actions.andExpect(status().isOk()) + .andDo(print()) + .andDo(document("api-get-notification-counts", + requestHeaders( + headerWithName("user-id").description("회원 고유번호") + ), + responseFields( + fieldWithPath("code").description("결과코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data").description("알림 개수") + ) + ) + ) + ; + } + +} \ No newline at end of file diff --git a/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationControllerTest.java b/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationControllerTest.java new file mode 100644 index 0000000..d2c29b3 --- /dev/null +++ b/notification-service/src/test/java/com/justpickup/notificationservice/domain/notification/web/NotificationControllerTest.java @@ -0,0 +1,136 @@ +package com.justpickup.notificationservice.domain.notification.web; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.justpickup.notificationservice.config.TestConfig; +import com.justpickup.notificationservice.domain.notification.dto.FindNotificationDto; +import com.justpickup.notificationservice.domain.notification.service.NotificationService; +import com.justpickup.notificationservice.domain.notification.web.NotificationController.PatchNotificationRequest; +import com.justpickup.notificationservice.global.dto.Code; +import com.justpickup.notificationservice.global.dto.Yn; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.http.MediaType; +import org.springframework.restdocs.request.RequestDocumentation; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; + +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.patch; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(NotificationController.class) +@Import(TestConfig.class) +@AutoConfigureRestDocs(uriHost = "https://just-pickup.com", uriPort = 8000) +class NotificationControllerTest { + + @Autowired + MockMvc mockMvc; + + @Autowired + ObjectMapper objectMapper; + + @MockBean + NotificationService notificationService; + + private final String url = "/notification"; + + @Test + @DisplayName("[GET] 회원 고유번호로 알림 가져오기") + void getNotificationByUserId() throws Exception { + // GIVEN + long userId = 1L; + given(notificationService.findNotificationByUserId(userId)) + .willReturn(getNotificationByUserIdWillReturn(userId)); + + // THEN + ResultActions actions + = mockMvc.perform(get(url).header("user-id", String.valueOf(userId))); + + // WHEN + actions.andExpect(status().isOk()) + .andExpect(jsonPath("code").value(Code.SUCCESS.name())) + .andExpect(jsonPath("message").value("")) + .andExpect(jsonPath("data").isNotEmpty()) + .andDo(print()) + .andDo(document("notification-get", + requestHeaders( + headerWithName("user-id").description("회원 고유번호") + ), + responseFields( + fieldWithPath("code").description("결과코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data.notifications[*].id").description("알림 고유번호"), + fieldWithPath("data.notifications[*].message").description("알림 내용"), + fieldWithPath("data.notifications[*].title").description("알림 제목"), + fieldWithPath("data.notifications[*].read").description("알림 읽음 여부"), + fieldWithPath("data.notifications[*].time").description("알림 생성 시간 [YYYY-MM-DD HH:MM]") + ) + ) + ) + ; + } + + private List getNotificationByUserIdWillReturn(long userId) { + List returnList = new ArrayList<>(); + for (long id = 1; id <= 5; id++) { + returnList.add(FindNotificationDto.of(id, userId, id + "번 메시지 예시입니다.", "제목" + id, Yn.Y, LocalDateTime.now())); + } + for (long id = 6; id <= 10; id++) { + returnList.add(FindNotificationDto.of(id, userId, id + "번 메시지 예시입니다.", "제목" + id, Yn.N, LocalDateTime.now())); + } + return returnList; + } + + @Test + @DisplayName("[API] 알림 수정") + void patchNotification() throws Exception { + // GIVEN + long notificationId = 1L; + PatchNotificationRequest request = new PatchNotificationRequest(true); + String requestBody = objectMapper.writeValueAsString(request); + + // WHEN + ResultActions actions = mockMvc.perform( + patch(url + "/{notificationId}", String.valueOf(notificationId)) + .content(requestBody) + .contentType(MediaType.APPLICATION_JSON) + .accept(MediaType.APPLICATION_JSON) + ); + + // THEN + actions.andExpect(status().isOk()) + .andDo(print()) + .andDo(document("notification-patch", + pathParameters( + parameterWithName("notificationId").description("알림 고유번호") + ), + requestFields( + fieldWithPath("read").description("읽음 여부") + ), + responseFields( + fieldWithPath("code").description("결과코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data").description("데이터") + ) + )) + ; + } +} diff --git a/store-service/src/docs/asciidoc/api-docs.adoc b/store-service/src/docs/asciidoc/api-docs.adoc index ea28906..cdb3467 100644 --- a/store-service/src/docs/asciidoc/api-docs.adoc +++ b/store-service/src/docs/asciidoc/api-docs.adoc @@ -95,8 +95,8 @@ operation::put-categoryList[snippets='curl-request,http-request,http-response,re == 매장 === 매장 검색 조회 operation::api-customer-store-search[snippets='curl-request,http-request,http-response,request-parameters,response-fields'] - === 자주찾는 매장 operation::favoriteStore-get[snippets='curl-request,http-request,http-response,request-headers,request-parameters,response-fields'] - +=== 매장 조회 +operation::store-get[snippets='curl-request,http-request,http-response,path-parameters,response-fields'] diff --git a/store-service/src/main/java/com/justpickup/storeservice/StoreServiceApplication.java b/store-service/src/main/java/com/justpickup/storeservice/StoreServiceApplication.java index 42b2047..e0e9bd9 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/StoreServiceApplication.java +++ b/store-service/src/main/java/com/justpickup/storeservice/StoreServiceApplication.java @@ -1,19 +1,8 @@ package com.justpickup.storeservice; -import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore; -import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreRepository; -import com.justpickup.storeservice.domain.map.entity.Map; -import com.justpickup.storeservice.domain.store.entity.Store; -import com.justpickup.storeservice.domain.store.repository.StoreRepository; -import com.justpickup.storeservice.global.entity.Address; -import org.springframework.boot.CommandLineRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; -import org.springframework.context.annotation.Bean; - -import java.util.ArrayList; -import java.util.List; @SpringBootApplication @EnableEurekaClient @@ -23,56 +12,4 @@ public class StoreServiceApplication { SpringApplication.run(StoreServiceApplication.class, args); } - @Bean - CommandLineRunner run(StoreRepository storeRepository, FavoriteStoreRepository favoriteStoreRepository) { - return args -> { - List stores = new ArrayList<>(); - - stores.add( - Store.of( - new Address("서울시", "마포구 도화동", "201-20"), - Map.of(37.5398271003404, 126.94769672415691), - 1L, - "커피온리 마포역점" - ) - ); - - stores.add( - Store.of( - new Address("서울시", "마포구 도화동", "50-10"), - Map.of(37.54010719003089, 126.94556661330861), - 2L, - "만랩커피 마포점" - ) - ); - - stores.add( - Store.of( - new Address("서울시", "마포구 도화동", "555"), - Map.of(37.539797393793755, 126.9453578838543), - 3L, - "이디야커피 마포오벨리스크점" - ) - ); - - stores.add( - Store.of( - new Address("서울시", "영등포구 도림로", "31길 2"), - Map.of(37.493033141569505, 126.89593667847592), - 4L, - "이디야커피 대림역점" - ) - ); - - storeRepository.saveAll(stores); - - List userList = List.of(1L,2L,3L,4L,5L,6L,7L); - userList.forEach(userId -> { - stores.forEach(store -> { - favoriteStoreRepository.save(FavoriteStore.of(userId, store)); - }); - }); - - }; - } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/category/entity/Category.java b/store-service/src/main/java/com/justpickup/storeservice/domain/category/entity/Category.java index 7fd7607..d78dd7c 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/category/entity/Category.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/category/entity/Category.java @@ -60,4 +60,11 @@ public class Category extends BaseEntity { return new Category(id,name,order,store); } + public static Category of(String name, Integer order, Store store) { + Category category = new Category(); + category.name = name; + category.order = order; + category.store = store; + return category; + } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/category/service/CategoryService.java b/store-service/src/main/java/com/justpickup/storeservice/domain/category/service/CategoryService.java index 25dd471..1ef04de 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/category/service/CategoryService.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/category/service/CategoryService.java @@ -7,7 +7,6 @@ import com.justpickup.storeservice.domain.category.repository.CategoryRepository import com.justpickup.storeservice.domain.category.repository.CategoryRepositoryCustom; import com.justpickup.storeservice.domain.store.entity.Store; import com.justpickup.storeservice.domain.store.repository.StoreRepository; -import com.justpickup.storeservice.global.exception.CustomException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; @@ -27,7 +26,7 @@ public class CategoryService { private final CategoryRepositoryCustom categoryRepositoryCustom; private final StoreRepository storeRepository; - public List getCategoryList(Long storeId){ + public List getCategoriesWithItem(Long storeId){ return categoryRepositoryCustom.getCategoryList(storeId) .stream() @@ -74,6 +73,6 @@ public class CategoryService { .orElseThrow(() -> new NotFoundStoreException(HttpStatus.BAD_REQUEST,"존재하지않는 Category"))); } ); - } + } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryCustomerApiController.java b/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryCustomerApiController.java new file mode 100644 index 0000000..882f3a0 --- /dev/null +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryCustomerApiController.java @@ -0,0 +1,78 @@ +package com.justpickup.storeservice.domain.category.web; + +import com.justpickup.storeservice.domain.category.dto.CategoryDto; +import com.justpickup.storeservice.domain.category.service.CategoryService; +import com.justpickup.storeservice.global.dto.Result; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.stream.Collectors; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/customer/") +public class CategoryCustomerApiController { + + private final CategoryService categoryService; + + @GetMapping("/categories") + public ResponseEntity getCategories(@RequestParam("storeId") Long storeId) { + List categoryList = categoryService.getCategoriesWithItem(storeId); + + GetCategoriesResponse getCategoriesResponse = new GetCategoriesResponse(categoryList); + + return ResponseEntity.ok(Result.createSuccessResult(getCategoriesResponse)); + } + + @Data @NoArgsConstructor + static class GetCategoriesResponse { + private List<_Category> categories; + + public GetCategoriesResponse(List categoryDtoList) { + this. categories = categoryDtoList + .stream() + .map(_Category::new) + .collect(Collectors.toList()); + } + + @Data + static class _Category { + private Long id; + private String name; + private Integer order; + private List<_Item> items; + + public _Category(CategoryDto categoryDto) { + List<_Item> items = categoryDto.getItems() + .stream() + .map(itemDto -> new _Item(itemDto.getId(), itemDto.getName(), itemDto.getPrice())) + .collect(Collectors.toList()); + + this.id = categoryDto.getId(); + this.name = categoryDto.getName(); + this.order = categoryDto.getOrder(); + this.items = items; + } + } + + @Data + static class _Item { + private Long id; + private String name; + private Long price; + + public _Item(Long id, String name, Long price) { + this.id = id; + this.name = name; + this.price = price; + } + } + } +} diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiController.java b/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiController.java index a444af3..1da4a2e 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiController.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiController.java @@ -24,7 +24,7 @@ public class CategoryOwnerApiController { @GetMapping("/category") public ResponseEntity getCategoryList(@RequestHeader(value = "user-id") String userId ){ Long storeId = Long.parseLong(userId); - List categoryList = categoryService.getCategoryList(storeId); + List categoryList = categoryService.getCategoriesWithItem(storeId); List categoryResponseList = categoryList.stream() .map(CategoryResponse::new) diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/item/dto/ItemDto.java b/store-service/src/main/java/com/justpickup/storeservice/domain/item/dto/ItemDto.java index 06bdcba..1174460 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/item/dto/ItemDto.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/item/dto/ItemDto.java @@ -52,7 +52,7 @@ public class ItemDto { .build(); } - public static ItemDto createWithCategoryItemDto(Item item) { + public static ItemDto createWithCategory(Item item) { return ItemDto.builder() .id(item.getId()) .name(item.getName()) diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/item/entity/Item.java b/store-service/src/main/java/com/justpickup/storeservice/domain/item/entity/Item.java index 3747090..4d2f177 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/item/entity/Item.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/item/entity/Item.java @@ -11,11 +11,9 @@ import lombok.Getter; import lombok.NoArgsConstructor; import javax.persistence.*; - +import java.util.ArrayList; import java.util.List; -import static javax.persistence.CascadeType.PERSIST; -import static javax.persistence.CascadeType.REMOVE; import static javax.persistence.FetchType.LAZY; @Entity @@ -45,8 +43,8 @@ public class Item extends BaseEntity { @JoinColumn(name = "store_id") private Store store; - @OneToMany(mappedBy = "item" ,cascade = {REMOVE,PERSIST} ) - private List itemOptions; + @OneToMany(mappedBy = "item", cascade = CascadeType.ALL) + private List itemOptions = new ArrayList<>(); // == 연관관계 편의 메소드 ==// public void addItemOption(ItemOption itemOption) { @@ -71,19 +69,15 @@ public class Item extends BaseEntity { } // == 생성 메소드 == // - public static Item createdItem(Category category, Store store, List itemOptions) { + public static Item of(String name, Long price, Category category, Store store, List itemOptions) { Item item = new Item(); - item.setCategory(category); - item.setStore(store); - itemOptions.forEach(item::addItemOption); - return item; - } - - public static Item createdFullItem(Category category, Store store, List itemOptions, String name , Long price) { - Item item = new Item(); - item.setItemNameAndPriceAndCategory( name, price ,category); - item.setStore(store); - itemOptions.forEach(item::addItemOption); + item.name = name; + item.price = price; + item.category = category; + item.store = store; + for (ItemOption itemOption : itemOptions) { + item.addItemOption(itemOption); + } return item; } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/item/repository/ItemRepository.java b/store-service/src/main/java/com/justpickup/storeservice/domain/item/repository/ItemRepository.java index 3e6b1d9..dc6a081 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/item/repository/ItemRepository.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/item/repository/ItemRepository.java @@ -1,12 +1,8 @@ package com.justpickup.storeservice.domain.item.repository; import com.justpickup.storeservice.domain.item.entity.Item; -import com.justpickup.storeservice.domain.store.entity.Store; import org.springframework.data.jpa.repository.JpaRepository; -import java.util.List; - public interface ItemRepository extends JpaRepository { - List findByStore(Store store); } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/item/service/ItemServiceImpl.java b/store-service/src/main/java/com/justpickup/storeservice/domain/item/service/ItemServiceImpl.java index 43823b6..d8b2198 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/item/service/ItemServiceImpl.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/item/service/ItemServiceImpl.java @@ -20,7 +20,6 @@ import org.springframework.data.support.PageableExecutionUtils; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; @@ -60,7 +59,7 @@ public class ItemServiceImpl implements ItemService { Page itemList = itemRepositoryCustom.findItem(storeId,word,pageable); return PageableExecutionUtils.getPage(itemList.stream() - .map(ItemDto::createWithCategoryItemDto) + .map(ItemDto::createWithCategory) .collect(Collectors.toList()),pageable,itemList::getTotalElements); } @@ -95,7 +94,6 @@ public class ItemServiceImpl implements ItemService { Long itemPrice, Long categoryId, List itemOptionDtos) { - //find Store Store store = storeRepository.findById(storeId) .orElseThrow(() -> new NotExistItemException("존재하지 않는 매장 입니다.")); @@ -104,11 +102,13 @@ public class ItemServiceImpl implements ItemService { Category category = categoryRepository.findById(categoryId) .orElseThrow(() -> new NotExistItemException("존재하지 않는 카테고리 입니다.")); - //create Item - Item item = Item.createdFullItem(category,store,new ArrayList<>() ,itemName, itemPrice); + List itemOptions = itemOptionDtos + .stream() + .map(itemOptionDto -> ItemOption.of(itemOptionDto.getOptionType(), itemOptionDto.getName())) + .collect(Collectors.toList()); - //add ItemOption - itemOptionDtos.forEach((itemOptionDto -> - itemOptionRepository.save(ItemOptionDto.createItemOption(itemOptionDto, item)))); + Item createdItem = Item.of(itemName, itemPrice, category, store, itemOptions); + + itemRepository.save(createdItem); } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiController.java b/store-service/src/main/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiController.java index 918940f..59840e5 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiController.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiController.java @@ -130,10 +130,8 @@ public class ItemOwnerApiController { private String name; public ItemOptionResponse(ItemOptionDto itemOptionDto) { - this.id = itemOptionDto.getId(); this.optionType = itemOptionDto.getOptionType(); - this.price = itemOptionDto.getPrice(); this.name = itemOptionDto.getName(); } } @@ -183,13 +181,11 @@ public class ItemOwnerApiController { private Long id; private String name; private OptionType optionType; - private Long price; public static ItemOptionDto createItemOptionDto(ItemOptionRequest itemOptionRequest){ return ItemOptionDto.builder() .id(itemOptionRequest.getId()) .name(itemOptionRequest.getName()) - .price(itemOptionRequest.getPrice()) .optionType(itemOptionRequest.getOptionType()) .build(); diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/dto/ItemOptionDto.java b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/dto/ItemOptionDto.java index 0ec7b5b..5231920 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/dto/ItemOptionDto.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/dto/ItemOptionDto.java @@ -20,20 +20,15 @@ public class ItemOptionDto { private OptionType optionType; - private Long price; - private String name; public ItemOptionDto (ItemOption itemOption){ this.id = itemOption.getId(); this.optionType = itemOption.getOptionType(); - this.price = itemOption.getPrice(); this.name = itemOption.getName(); } public static ItemOption createItemOption (ItemOptionDto itemOptionDto, Item item){ - - return new ItemOption(itemOptionDto.getOptionType(),itemOptionDto.getPrice(),itemOptionDto.getName(),item); - + return new ItemOption(itemOptionDto.getOptionType(), itemOptionDto.getName(),item); } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/entity/ItemOption.java b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/entity/ItemOption.java index d3bb75a..d674b96 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/entity/ItemOption.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/entity/ItemOption.java @@ -22,8 +22,6 @@ public class ItemOption extends BaseEntity { @Enumerated(EnumType.STRING) private OptionType optionType; - private Long price; - private String name; @ManyToOne(fetch = LAZY , cascade = CascadeType.ALL) @@ -36,10 +34,16 @@ public class ItemOption extends BaseEntity { item.getItemOptions().add(this); } - public ItemOption(OptionType optionType, Long price, String name, Item item) { + public ItemOption(OptionType optionType, String name, Item item) { this.optionType = optionType; - this.price = price; this.name = name; this.item = item; } + + public static ItemOption of(OptionType type, String name) { + ItemOption itemOption = new ItemOption(); + itemOption.optionType = type; + itemOption.name = name; + return itemOption; + } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/repository/ItemOptionRepositoryCustom.java b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/repository/ItemOptionRepositoryCustom.java index 3cc1316..b211a40 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/repository/ItemOptionRepositoryCustom.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/itemoption/repository/ItemOptionRepositoryCustom.java @@ -1,16 +1,15 @@ package com.justpickup.storeservice.domain.itemoption.repository; -import com.justpickup.storeservice.domain.item.entity.Item; -import com.justpickup.storeservice.domain.item.entity.QItem; import com.justpickup.storeservice.domain.itemoption.entity.ItemOption; import com.justpickup.storeservice.domain.itemoption.entity.QItemOption; import com.querydsl.jpa.impl.JPAQueryFactory; import lombok.RequiredArgsConstructor; -import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; import java.util.List; +import static com.justpickup.storeservice.domain.item.entity.QItem.item; + @Repository @RequiredArgsConstructor public class ItemOptionRepositoryCustom { @@ -21,7 +20,9 @@ public class ItemOptionRepositoryCustom { return queryFactory.selectFrom(QItemOption.itemOption) .join(QItemOption.itemOption.item) - .on(QItem.item.id.eq(itemId)) + .on(item.id.eq(itemId)) .fetch(); } + + } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/dto/StoreDto.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/dto/StoreDto.java index fcc49b7..2dd6c27 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/dto/StoreDto.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/dto/StoreDto.java @@ -1,13 +1,21 @@ package com.justpickup.storeservice.domain.store.dto; +import com.justpickup.storeservice.domain.store.entity.Store; import lombok.AllArgsConstructor; +import lombok.Builder; import lombok.Getter; -import lombok.NoArgsConstructor; @Getter -@NoArgsConstructor @AllArgsConstructor +@Builder public class StoreDto { - private Long storeId; - private String storeName; + private Long id; + private String name; + private String phoneNumber; + + public StoreDto(Store store) { + this.id = store.getId(); + this.name = store.getName(); + this.phoneNumber = store.getPhoneNumber(); + } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/entity/Store.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/entity/Store.java index e50396f..58fc886 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/entity/Store.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/entity/Store.java @@ -13,6 +13,7 @@ import lombok.NoArgsConstructor; import javax.persistence.*; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import static javax.persistence.FetchType.LAZY; @@ -48,13 +49,14 @@ public class Store extends BaseEntity { private Long userId; @OneToMany(mappedBy = "store") - private List categories; + private List categories = new ArrayList<>(); @OneToMany(mappedBy = "store") - private List items; + private List items = new ArrayList<>(); @OneToMany(mappedBy = "store") - private List favoriteStores; + private List favoriteStores = new ArrayList<>(); + // == 연관관계 편의 메소드 == // public void addCategory(Category category) { categories.add(category); diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/exception/NotExistStoreException.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/exception/NotExistStoreException.java new file mode 100644 index 0000000..c8a4bcc --- /dev/null +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/exception/NotExistStoreException.java @@ -0,0 +1,11 @@ +package com.justpickup.storeservice.domain.store.exception; + +import com.justpickup.storeservice.global.exception.CustomException; +import org.springframework.http.HttpStatus; + +public class NotExistStoreException extends CustomException { + + public NotExistStoreException(String message) { + super(HttpStatus.CONFLICT, message); + } +} diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/repository/StoreRepositoryCustom.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/repository/StoreRepositoryCustom.java index 55093ed..4c45d7c 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/repository/StoreRepositoryCustom.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/repository/StoreRepositoryCustom.java @@ -1,6 +1,5 @@ package com.justpickup.storeservice.domain.store.repository; -import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore; import com.justpickup.storeservice.domain.favoritestore.entity.QFavoriteStore; import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition; import com.justpickup.storeservice.domain.store.dto.SearchStoreResult; @@ -83,7 +82,7 @@ public class StoreRepositoryCustom { return content; } - private NumberExpression getHaversineDistance (double SearchLatitude, double SearchLongitude){ + private NumberExpression getHaversineDistance(double SearchLatitude, double SearchLongitude){ Expression latitude = Expressions.constant(SearchLatitude); Expression longitude = Expressions.constant(SearchLongitude); diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreService.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreService.java index afb2099..1787b50 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreService.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreService.java @@ -2,6 +2,7 @@ package com.justpickup.storeservice.domain.store.service; import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition; import com.justpickup.storeservice.domain.store.dto.SearchStoreResult; +import com.justpickup.storeservice.domain.store.dto.StoreDto; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.SliceImpl; @@ -10,4 +11,5 @@ import java.util.List; public interface StoreService { SliceImpl findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable); List findFavoriteStore(SearchStoreCondition condition, Long userId); + StoreDto findStore(Long storeId); } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreServiceImpl.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreServiceImpl.java index c535f7e..3caf57e 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreServiceImpl.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/service/StoreServiceImpl.java @@ -3,6 +3,10 @@ package com.justpickup.storeservice.domain.store.service; import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreCustom; import com.justpickup.storeservice.domain.store.dto.SearchStoreCondition; import com.justpickup.storeservice.domain.store.dto.SearchStoreResult; +import com.justpickup.storeservice.domain.store.dto.StoreDto; +import com.justpickup.storeservice.domain.store.entity.Store; +import com.justpickup.storeservice.domain.store.exception.NotExistStoreException; +import com.justpickup.storeservice.domain.store.repository.StoreRepository; import com.justpickup.storeservice.domain.store.repository.StoreRepositoryCustom; import lombok.RequiredArgsConstructor; import org.springframework.data.domain.Pageable; @@ -10,7 +14,6 @@ import org.springframework.data.domain.SliceImpl; import org.springframework.stereotype.Service; import java.util.List; -import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -18,6 +21,7 @@ public class StoreServiceImpl implements StoreService { private final StoreRepositoryCustom storeRepositoryCustom; private final FavoriteStoreCustom favoriteStoreCustom; + private final StoreRepository storeRepository; @Override public SliceImpl findSearchStoreScroll(SearchStoreCondition condition, Pageable pageable) { @@ -41,4 +45,12 @@ public class StoreServiceImpl implements StoreService { }); return favoriteStores; } + + @Override + public StoreDto findStore(Long storeId) { + Store store = storeRepository.findById(storeId) + .orElseThrow(() -> new NotExistStoreException(storeId + "는 없는 매장 고유번호입니다.")); + + return new StoreDto(store); + } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/domain/store/web/StoreController.java b/store-service/src/main/java/com/justpickup/storeservice/domain/store/web/StoreController.java index 11854d5..3bbe6f2 100644 --- a/store-service/src/main/java/com/justpickup/storeservice/domain/store/web/StoreController.java +++ b/store-service/src/main/java/com/justpickup/storeservice/domain/store/web/StoreController.java @@ -1,6 +1,14 @@ package com.justpickup.storeservice.domain.store.web; +import com.justpickup.storeservice.domain.store.dto.StoreDto; +import com.justpickup.storeservice.domain.store.service.StoreService; +import com.justpickup.storeservice.global.dto.Result; +import lombok.Data; +import lombok.NoArgsConstructor; import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @@ -9,4 +17,26 @@ import org.springframework.web.bind.annotation.RestController; @RequestMapping("/store") public class StoreController { + private final StoreService storeService; + + @GetMapping("/{storeId}") + public ResponseEntity getStore(@PathVariable("storeId") Long storeId) { + StoreDto storeDto = storeService.findStore(storeId); + + GetStoreResponse getStoreResponse = new GetStoreResponse(storeDto); + return ResponseEntity.ok(Result.createSuccessResult(getStoreResponse)); + } + + @Data @NoArgsConstructor + static class GetStoreResponse { + private Long id; + private String name; + private String phoneNumber; + + public GetStoreResponse(StoreDto storeDto) { + this.id = storeDto.getId(); + this.name = storeDto.getName(); + this.phoneNumber = storeDto.getPhoneNumber(); + } + } } diff --git a/store-service/src/main/java/com/justpickup/storeservice/global/SqlCommandLineRunner.java b/store-service/src/main/java/com/justpickup/storeservice/global/SqlCommandLineRunner.java new file mode 100644 index 0000000..9440a00 --- /dev/null +++ b/store-service/src/main/java/com/justpickup/storeservice/global/SqlCommandLineRunner.java @@ -0,0 +1,108 @@ +package com.justpickup.storeservice.global; + +import com.justpickup.storeservice.domain.category.entity.Category; +import com.justpickup.storeservice.domain.category.repository.CategoryRepository; +import com.justpickup.storeservice.domain.favoritestore.entity.FavoriteStore; +import com.justpickup.storeservice.domain.favoritestore.repository.FavoriteStoreRepository; +import com.justpickup.storeservice.domain.item.entity.Item; +import com.justpickup.storeservice.domain.item.repository.ItemRepository; +import com.justpickup.storeservice.domain.itemoption.entity.ItemOption; +import com.justpickup.storeservice.domain.itemoption.entity.OptionType; +import com.justpickup.storeservice.domain.map.entity.Map; +import com.justpickup.storeservice.domain.store.entity.Store; +import com.justpickup.storeservice.domain.store.repository.StoreRepository; +import com.justpickup.storeservice.global.entity.Address; +import lombok.RequiredArgsConstructor; +import org.springframework.boot.CommandLineRunner; +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.List; + +@Component +@RequiredArgsConstructor +public class SqlCommandLineRunner implements CommandLineRunner { + + private final StoreRepository storeRepository; + private final FavoriteStoreRepository favoriteStoreRepository; + private final ItemRepository itemRepository; + private final CategoryRepository categoryRepository; + + @Override + public void run(String... args) throws Exception { + List stores = new ArrayList<>(); + + createStores(storeRepository, stores); + + createFavoriteStore(favoriteStoreRepository, stores); + + createItemAndCategories(itemRepository, categoryRepository, stores); + } + + private void createItemAndCategories(ItemRepository itemRepository, CategoryRepository categoryRepository, List stores) { + stores.forEach(store -> { + Category 카페인 = categoryRepository.save(Category.of("카페인", 0, store)); + Category 디카페인 = categoryRepository.save(Category.of("디카페인", 1, store)); + Category 티 = categoryRepository.save(Category.of("티", 2, store)); + + ItemOption ice = ItemOption.of(OptionType.REQUIRED, "ICE"); + ItemOption hot = ItemOption.of(OptionType.REQUIRED, "HOT"); + + Item 아메리카노 = Item.of("아메리카노", 1500L, 카페인, store, List.of(ice, hot)); + Item 카페라떼 = Item.of("카페라떼", 2000L, 카페인, store, List.of(ice, hot)); + Item 카페모카 = Item.of("카페모카", 3900L, 카페인, store, List.of(ice, hot)); + Item 콜드브루 = Item.of("콜드브루", 2500L, 카페인, store, List.of(ice)); + Item 녹차라떼 = Item.of("녹차라떼", 3000L, 디카페인, store, List.of(ice, hot)); + Item 딸기라떼 = Item.of("딸기라떼", 3000L, 디카페인, store, List.of(ice, hot)); + Item 녹차 = Item.of("녹차", 3000L, 티, store, List.of(hot)); + Item 히비스커스 = Item.of("히비스커스 티", 3000L, 티, store, List.of(hot)); + itemRepository.saveAll(List.of(아메리카노, 카페라떼, 콜드브루, 녹차라떼, 딸기라떼, 녹차, 히비스커스)); + }); + } + + + private void createFavoriteStore(FavoriteStoreRepository favoriteStoreRepository, List stores) { + List userList = List.of(1L,2L,3L,4L,5L,6L,7L); + userList.forEach(userId -> stores.forEach(store -> favoriteStoreRepository.save(FavoriteStore.of(userId, store)))); + } + + private void createStores(StoreRepository storeRepository, List stores) { + stores.add( + Store.of( + new Address("서울시", "마포구 도화동", "201-20"), + Map.of(37.5398271003404, 126.94769672415691), + 1L, + "커피온리 마포역점" + ) + ); + + stores.add( + Store.of( + new Address("서울시", "마포구 도화동", "50-10"), + Map.of(37.54010719003089, 126.94556661330861), + 2L, + "만랩커피 마포점" + ) + ); + + stores.add( + Store.of( + new Address("서울시", "마포구 도화동", "555"), + Map.of(37.539797393793755, 126.9453578838543), + 3L, + "이디야커피 마포오벨리스크점" + ) + ); + + stores.add( + Store.of( + new Address("서울시", "영등포구 도림로", "31길 2"), + Map.of(37.493033141569505, 126.89593667847592), + 4L, + "이디야커피 대림역점" + ) + ); + + storeRepository.saveAll(stores); + } +} diff --git a/store-service/src/test/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiControllerTest.java b/store-service/src/test/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiControllerTest.java index c809974..63bdf20 100644 --- a/store-service/src/test/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiControllerTest.java +++ b/store-service/src/test/java/com/justpickup/storeservice/domain/category/web/CategoryOwnerApiControllerTest.java @@ -45,17 +45,9 @@ class CategoryOwnerApiControllerTest { @MockBean private CategoryService categoryService; - @MockBean - private StoreRepository storeRepository; - - @MockBean - private FavoriteStoreRepository favoriteStoreRepository; - - @Test @DisplayName("카테고리리스트_가져오기_성공") void getCategoryList_success() throws Exception { - //given Long storeId = 1L; List categoryDtoList = new ArrayList<>(); @@ -75,7 +67,7 @@ class CategoryOwnerApiControllerTest { .order(2) .build()); - given(categoryService.getCategoryList(any())).willReturn(categoryDtoList); + given(categoryService.getCategoriesWithItem(any())).willReturn(categoryDtoList); //when ResultActions actions = mockMvc.perform(MockMvcRequestBuilders.get("/api/owner/category") @@ -108,7 +100,6 @@ class CategoryOwnerApiControllerTest { } - @Test @DisplayName("카테고리리스트_수정_성공") void putCategoryList_success() throws Exception { diff --git a/store-service/src/test/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiControllerTest.java b/store-service/src/test/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiControllerTest.java index 0b4a5e7..db657c5 100644 --- a/store-service/src/test/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiControllerTest.java +++ b/store-service/src/test/java/com/justpickup/storeservice/domain/item/web/ItemOwnerApiControllerTest.java @@ -53,12 +53,6 @@ class ItemOwnerApiControllerTest { @MockBean ItemService itemService; - @MockBean - private StoreRepository storeRepository; - - @MockBean - private FavoriteStoreRepository favoriteStoreRepository; - @Test @DisplayName("상품 리스트 조회") void getItemList() throws Exception { @@ -174,9 +168,9 @@ class ItemOwnerApiControllerTest { Long categoryId = 1L; List requiredOption = - List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED,null)); + List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED)); List otherOption = - List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER,null)); + List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가", OptionType.OTHER)); ItemOwnerApiController.ItemRequest itemRequest = @@ -205,12 +199,10 @@ class ItemOwnerApiControllerTest { fieldWithPath("requiredOption[*].id").description("옵션 고유번호"), fieldWithPath("requiredOption[*].name").description("옵션 이름"), fieldWithPath("requiredOption[*].optionType").description("옵션 타입"), - fieldWithPath("requiredOption[*].price").description("옵션 가격"), fieldWithPath("otherOption").description("추가옵션"), fieldWithPath("otherOption[*].id").description("옵션 고유번호"), fieldWithPath("otherOption[*].name").description("옵션 이름"), - fieldWithPath("otherOption[*].optionType").description("옵션 타입"), - fieldWithPath("otherOption[*].price").description("옵션 가격") + fieldWithPath("otherOption[*].optionType").description("옵션 타입") )) ); } @@ -224,9 +216,9 @@ class ItemOwnerApiControllerTest { Long categoryId = 1L; List requiredOption = - List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED,null)); + List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "HOT",OptionType.REQUIRED)); List otherOption = - List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER,null)); + List.of(new ItemOwnerApiController.ItemRequest.ItemOptionRequest(null, "샷 추가",OptionType.OTHER)); ItemOwnerApiController.ItemRequest itemRequest = @@ -254,12 +246,10 @@ class ItemOwnerApiControllerTest { fieldWithPath("requiredOption[*].id").description("옵션 고유번호"), fieldWithPath("requiredOption[*].name").description("옵션 이름"), fieldWithPath("requiredOption[*].optionType").description("옵션 타입"), - fieldWithPath("requiredOption[*].price").description("옵션 가격"), fieldWithPath("otherOption").description("추가옵션"), fieldWithPath("otherOption[*].id").description("옵션 고유번호"), fieldWithPath("otherOption[*].name").description("옵션 이름"), - fieldWithPath("otherOption[*].optionType").description("옵션 타입"), - fieldWithPath("otherOption[*].price").description("옵션 가격") + fieldWithPath("otherOption[*].optionType").description("옵션 타입") )) ); } diff --git a/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreControllerTest.java b/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreControllerTest.java new file mode 100644 index 0000000..09737d0 --- /dev/null +++ b/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreControllerTest.java @@ -0,0 +1,69 @@ +package com.justpickup.storeservice.domain.store.web; + +import com.justpickup.storeservice.config.TestConfig; +import com.justpickup.storeservice.domain.store.dto.StoreDto; +import com.justpickup.storeservice.domain.store.service.StoreService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.context.annotation.Import; +import org.springframework.test.web.servlet.MockMvc; +import org.springframework.test.web.servlet.ResultActions; + +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; +import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.get; +import static org.springframework.restdocs.payload.PayloadDocumentation.fieldWithPath; +import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; +import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + +@WebMvcTest(StoreController.class) +@Import(TestConfig.class) +@AutoConfigureRestDocs(uriHost = "just-pickup.com", uriPort = 8000) +class StoreControllerTest { + + private final String url = "/store"; + + @Autowired + MockMvc mockMvc; + + @MockBean + StoreService storeService; + + @Test + @DisplayName("[store] 매장 정보 가져오기") + void getStore() throws Exception { + //given + String storeId = "1"; + given(storeService.findStore(1L)).willReturn(getWillReturnStore()); + + //when + ResultActions actions = mockMvc.perform(get("/store/{storeId}", storeId)); + + //then + actions.andExpect(status().isOk()) + .andDo(print()) + .andDo(document("store-get", + pathParameters( + parameterWithName("storeId").description("매장 고유번호") + ), + responseFields( + fieldWithPath("code").description("결과 코드 SUCCESS/ERROR"), + fieldWithPath("message").description("메시지"), + fieldWithPath("data.id").description("매장 고유번호"), + fieldWithPath("data.name").description("매장 이름"), + fieldWithPath("data.phoneNumber").description("매장 번호") + ) + )); + } + + private StoreDto getWillReturnStore() { + return StoreDto.builder().id(1L).name("이디야커피 대림역점").phoneNumber("010-1234-5678").build(); + } +} \ No newline at end of file diff --git a/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreCustomerApiControllerTest.java b/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreCustomerApiControllerTest.java index 9946375..f9eefc1 100644 --- a/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreCustomerApiControllerTest.java +++ b/store-service/src/test/java/com/justpickup/storeservice/domain/store/web/StoreCustomerApiControllerTest.java @@ -93,16 +93,17 @@ class StoreCustomerApiControllerTest { fieldWithPath("message").description("메시지"), fieldWithPath("data[*].id").description("매장 고유번호"), fieldWithPath("data[*].name").description("매장 이름"), - fieldWithPath("data[*].distance").description("고객과의 거리차이 m/km") + fieldWithPath("data[*].distance").description("고객과의 거리차이 m/km"), + fieldWithPath("data[*].favoriteCounts").description("즐겨찾기 회수") ) )); } private List getWillReturnSearchStore(){ - SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007); - SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466); - SearchStoreResult result_3 = new SearchStoreResult(3L, "커피온리 마포역점", 341.25696860337655); + SearchStoreResult result_1 = new SearchStoreResult(1L, "이디야커피 마포오벨리스크점", 145.11980562222007, 5L); + SearchStoreResult result_2 = new SearchStoreResult(2L, "만랩커피 마포점", 150.97181089895466, 5L); + SearchStoreResult result_3 = new SearchStoreResult(3L, "커피온리 마포역점", 341.25696860337655, 5L); return List.of(result_1,result_2,result_3); }