store register add
This commit is contained in:
parent
b25c2edcc0
commit
17a68d3cdb
@ -44,6 +44,8 @@ spring:
|
|||||||
order_updates: true
|
order_updates: true
|
||||||
open-in-view: false
|
open-in-view: false
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Redis 설정 (올바른 구조)
|
# Redis 설정 (올바른 구조)
|
||||||
data:
|
data:
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@ -1,4 +1,342 @@
|
|||||||
|
// store/src/main/java/com/ktds/hi/store/biz/service/StoreService.java
|
||||||
package com.ktds.hi.store.biz.service;
|
package com.ktds.hi.store.biz.service;
|
||||||
|
|
||||||
public class StoreService {
|
import com.ktds.hi.store.biz.usecase.in.StoreUseCase;
|
||||||
|
import com.ktds.hi.store.biz.usecase.out.*;
|
||||||
|
import com.ktds.hi.store.biz.domain.Store;
|
||||||
|
import com.ktds.hi.store.biz.domain.StoreStatus;
|
||||||
|
import com.ktds.hi.store.infra.dto.*;
|
||||||
|
import com.ktds.hi.common.exception.BusinessException;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 서비스 구현체
|
||||||
|
* Clean Architecture의 Application Service Layer
|
||||||
|
*
|
||||||
|
* @author 하이오더 개발팀
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public class StoreService implements StoreUseCase {
|
||||||
|
|
||||||
|
private final StoreRepositoryPort storeRepositoryPort;
|
||||||
|
private final MenuRepositoryPort menuRepositoryPort;
|
||||||
|
private final StoreTagRepositoryPort storeTagRepositoryPort;
|
||||||
|
private final GeocodingPort geocodingPort;
|
||||||
|
private final CachePort cachePort;
|
||||||
|
private final EventPort eventPort;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public StoreCreateResponse createStore(Long ownerId, StoreCreateRequest request) {
|
||||||
|
log.info("매장 등록 시작: ownerId={}, storeName={}", ownerId, request.getStoreName());
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 입력값 검증
|
||||||
|
validateStoreCreateRequest(request);
|
||||||
|
|
||||||
|
// 2. 점주 매장 개수 제한 확인 (예: 최대 10개)
|
||||||
|
validateOwnerStoreLimit(ownerId);
|
||||||
|
|
||||||
|
// 3. 주소 지오코딩 (좌표 변환)
|
||||||
|
Coordinates coordinates = geocodingPort.getCoordinates(request.getAddress());
|
||||||
|
|
||||||
|
// 4. Store 도메인 객체 생성
|
||||||
|
Store store = Store.builder()
|
||||||
|
.ownerId(ownerId)
|
||||||
|
.storeName(request.getStoreName())
|
||||||
|
.address(request.getAddress())
|
||||||
|
.latitude(coordinates.getLatitude())
|
||||||
|
.longitude(coordinates.getLongitude())
|
||||||
|
.description(request.getDescription())
|
||||||
|
.phone(request.getPhone())
|
||||||
|
.operatingHours(request.getOperatingHours())
|
||||||
|
.category(request.getCategory())
|
||||||
|
.status(StoreStatus.ACTIVE)
|
||||||
|
.rating(0.0)
|
||||||
|
.reviewCount(0)
|
||||||
|
.createdAt(LocalDateTime.now())
|
||||||
|
.updatedAt(LocalDateTime.now())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 5. 매장 저장
|
||||||
|
Store savedStore = storeRepositoryPort.saveStore(store);
|
||||||
|
|
||||||
|
// 6. 매장 태그 저장
|
||||||
|
if (request.getTags() != null && !request.getTags().isEmpty()) {
|
||||||
|
storeTagRepositoryPort.saveStoreTags(savedStore.getId(), request.getTags());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 메뉴 정보 저장
|
||||||
|
if (request.getMenus() != null && !request.getMenus().isEmpty()) {
|
||||||
|
menuRepositoryPort.saveMenus(savedStore.getId(),
|
||||||
|
request.getMenus().stream()
|
||||||
|
.map(menuReq -> menuReq.toDomain(savedStore.getId()))
|
||||||
|
.collect(Collectors.toList()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 매장 생성 이벤트 발행
|
||||||
|
eventPort.publishStoreCreatedEvent(savedStore);
|
||||||
|
|
||||||
|
// 9. 캐시 무효화
|
||||||
|
cachePort.invalidateStoreCache(ownerId);
|
||||||
|
|
||||||
|
log.info("매장 등록 완료: storeId={}", savedStore.getId());
|
||||||
|
|
||||||
|
return StoreCreateResponse.builder()
|
||||||
|
.storeId(savedStore.getId())
|
||||||
|
.storeName(savedStore.getStoreName())
|
||||||
|
.message("매장이 성공적으로 등록되었습니다.")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("매장 등록 실패: ownerId={}, error={}", ownerId, e.getMessage(), e);
|
||||||
|
throw new BusinessException("STORE_CREATE_FAILED", "매장 등록에 실패했습니다: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MyStoreListResponse> getMyStores(Long ownerId) {
|
||||||
|
log.info("내 매장 목록 조회: ownerId={}", ownerId);
|
||||||
|
|
||||||
|
// 1. 캐시 확인
|
||||||
|
String cacheKey = "stores:owner:" + ownerId;
|
||||||
|
List<MyStoreListResponse> cachedStores = cachePort.getStoreCache(cacheKey);
|
||||||
|
if (cachedStores != null) {
|
||||||
|
log.info("캐시에서 매장 목록 반환: ownerId={}, count={}", ownerId, cachedStores.size());
|
||||||
|
return cachedStores;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. DB에서 매장 목록 조회
|
||||||
|
List<Store> stores = storeRepositoryPort.findStoresByOwnerId(ownerId);
|
||||||
|
|
||||||
|
// 3. 응답 DTO 변환
|
||||||
|
List<MyStoreListResponse> responses = stores.stream()
|
||||||
|
.map(store -> {
|
||||||
|
String status = calculateStoreStatus(store);
|
||||||
|
return MyStoreListResponse.builder()
|
||||||
|
.storeId(store.getId())
|
||||||
|
.storeName(store.getStoreName())
|
||||||
|
.address(store.getAddress())
|
||||||
|
.category(store.getCategory())
|
||||||
|
.rating(store.getRating())
|
||||||
|
.reviewCount(store.getReviewCount())
|
||||||
|
.status(status)
|
||||||
|
.operatingHours(store.getOperatingHours())
|
||||||
|
.build();
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 4. 캐시 저장 (1시간)
|
||||||
|
cachePort.putStoreCache(cacheKey, responses, 3600);
|
||||||
|
|
||||||
|
log.info("내 매장 목록 조회 완료: ownerId={}, count={}", ownerId, responses.size());
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public StoreDetailResponse getStoreDetail(Long storeId) {
|
||||||
|
log.info("매장 상세 조회: storeId={}", storeId);
|
||||||
|
|
||||||
|
// 1. 매장 기본 정보 조회
|
||||||
|
Store store = storeRepositoryPort.findStoreById(storeId)
|
||||||
|
.orElseThrow(() -> new BusinessException("STORE_NOT_FOUND", "매장을 찾을 수 없습니다."));
|
||||||
|
|
||||||
|
// 2. 매장 태그 조회
|
||||||
|
List<String> tags = storeTagRepositoryPort.findTagsByStoreId(storeId);
|
||||||
|
|
||||||
|
// 3. 메뉴 정보 조회
|
||||||
|
List<MenuResponse> menus = menuRepositoryPort.findMenusByStoreId(storeId)
|
||||||
|
.stream()
|
||||||
|
.map(MenuResponse::from)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
// 4. AI 요약 정보 조회 (외부 서비스)
|
||||||
|
String aiSummary = getAISummary(storeId);
|
||||||
|
|
||||||
|
return StoreDetailResponse.builder()
|
||||||
|
.storeId(store.getId())
|
||||||
|
.storeName(store.getStoreName())
|
||||||
|
.address(store.getAddress())
|
||||||
|
.latitude(store.getLatitude())
|
||||||
|
.longitude(store.getLongitude())
|
||||||
|
.description(store.getDescription())
|
||||||
|
.phone(store.getPhone())
|
||||||
|
.operatingHours(store.getOperatingHours())
|
||||||
|
.category(store.getCategory())
|
||||||
|
.rating(store.getRating())
|
||||||
|
.reviewCount(store.getReviewCount())
|
||||||
|
.status(store.getStatus().name())
|
||||||
|
.tags(tags)
|
||||||
|
.menus(menus)
|
||||||
|
.aiSummary(aiSummary)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public StoreUpdateResponse updateStore(Long storeId, Long ownerId, StoreUpdateRequest request) {
|
||||||
|
log.info("매장 정보 수정: storeId={}, ownerId={}", storeId, ownerId);
|
||||||
|
|
||||||
|
// 1. 매장 소유권 확인
|
||||||
|
Store store = validateStoreOwnership(storeId, ownerId);
|
||||||
|
|
||||||
|
// 2. 주소 변경 시 지오코딩
|
||||||
|
Coordinates coordinates = null;
|
||||||
|
if (!store.getAddress().equals(request.getAddress())) {
|
||||||
|
coordinates = geocodingPort.getCoordinates(request.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 매장 정보 업데이트
|
||||||
|
store.updateBasicInfo(
|
||||||
|
request.getStoreName(),
|
||||||
|
request.getAddress(),
|
||||||
|
request.getDescription(),
|
||||||
|
request.getPhone(),
|
||||||
|
request.getOperatingHours()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (coordinates != null) {
|
||||||
|
store.updateLocation(coordinates);
|
||||||
|
}
|
||||||
|
|
||||||
|
Store updatedStore = storeRepositoryPort.saveStore(store);
|
||||||
|
|
||||||
|
// 4. 태그 업데이트
|
||||||
|
if (request.getTags() != null) {
|
||||||
|
storeTagRepositoryPort.deleteTagsByStoreId(storeId);
|
||||||
|
storeTagRepositoryPort.saveStoreTags(storeId, request.getTags());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 매장 수정 이벤트 발행
|
||||||
|
eventPort.publishStoreUpdatedEvent(updatedStore);
|
||||||
|
|
||||||
|
// 6. 캐시 무효화
|
||||||
|
cachePort.invalidateStoreCache(storeId);
|
||||||
|
cachePort.invalidateStoreCache(ownerId);
|
||||||
|
|
||||||
|
return StoreUpdateResponse.builder()
|
||||||
|
.storeId(storeId)
|
||||||
|
.message("매장 정보가 성공적으로 수정되었습니다.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public StoreDeleteResponse deleteStore(Long storeId, Long ownerId) {
|
||||||
|
log.info("매장 삭제: storeId={}, ownerId={}", storeId, ownerId);
|
||||||
|
|
||||||
|
// 1. 매장 소유권 확인
|
||||||
|
Store store = validateStoreOwnership(storeId, ownerId);
|
||||||
|
|
||||||
|
// 2. 소프트 삭제 (상태 변경)
|
||||||
|
store.delete();
|
||||||
|
storeRepositoryPort.saveStore(store);
|
||||||
|
|
||||||
|
// 3. 매장 삭제 이벤트 발행
|
||||||
|
eventPort.publishStoreDeletedEvent(storeId);
|
||||||
|
|
||||||
|
// 4. 캐시 무효화
|
||||||
|
cachePort.invalidateStoreCache(storeId);
|
||||||
|
cachePort.invalidateStoreCache(ownerId);
|
||||||
|
|
||||||
|
return StoreDeleteResponse.builder()
|
||||||
|
.storeId(storeId)
|
||||||
|
.message("매장이 성공적으로 삭제되었습니다.")
|
||||||
|
.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<StoreSearchResponse> searchStores(String keyword, String category, String tags,
|
||||||
|
Double latitude, Double longitude, Integer radius,
|
||||||
|
Integer page, Integer size) {
|
||||||
|
log.info("매장 검색: keyword={}, category={}, location=({}, {})", keyword, category, latitude, longitude);
|
||||||
|
|
||||||
|
StoreSearchCriteria criteria = StoreSearchCriteria.builder()
|
||||||
|
.keyword(keyword)
|
||||||
|
.category(category)
|
||||||
|
.tags(tags)
|
||||||
|
.latitude(latitude)
|
||||||
|
.longitude(longitude)
|
||||||
|
.radius(radius)
|
||||||
|
.page(page)
|
||||||
|
.size(size)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
List<Store> stores = storeRepositoryPort.searchStores(criteria);
|
||||||
|
|
||||||
|
return stores.stream()
|
||||||
|
.map(store -> StoreSearchResponse.builder()
|
||||||
|
.storeId(store.getId())
|
||||||
|
.storeName(store.getStoreName())
|
||||||
|
.address(store.getAddress())
|
||||||
|
.category(store.getCategory())
|
||||||
|
.rating(store.getRating())
|
||||||
|
.reviewCount(store.getReviewCount())
|
||||||
|
.distance(calculateDistance(latitude, longitude, store.getLatitude(), store.getLongitude()))
|
||||||
|
.build())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
// === Private Helper Methods ===
|
||||||
|
|
||||||
|
private void validateStoreCreateRequest(StoreCreateRequest request) {
|
||||||
|
if (request.getStoreName() == null || request.getStoreName().trim().isEmpty()) {
|
||||||
|
throw new BusinessException("INVALID_STORE_NAME", "매장명은 필수입니다.");
|
||||||
|
}
|
||||||
|
if (request.getStoreName().length() > 100) {
|
||||||
|
throw new BusinessException("INVALID_STORE_NAME", "매장명은 100자를 초과할 수 없습니다.");
|
||||||
|
}
|
||||||
|
if (request.getAddress() == null || request.getAddress().trim().isEmpty()) {
|
||||||
|
throw new BusinessException("INVALID_ADDRESS", "주소는 필수입니다.");
|
||||||
|
}
|
||||||
|
if (request.getPhone() != null && !request.getPhone().matches("^\\d{2,3}-\\d{3,4}-\\d{4}$")) {
|
||||||
|
throw new BusinessException("INVALID_PHONE", "전화번호 형식이 올바르지 않습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void validateOwnerStoreLimit(Long ownerId) {
|
||||||
|
Long storeCount = storeRepositoryPort.countStoresByOwnerId(ownerId);
|
||||||
|
if (storeCount >= 10) {
|
||||||
|
throw new BusinessException("STORE_LIMIT_EXCEEDED", "매장은 최대 10개까지 등록할 수 있습니다.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Store validateStoreOwnership(Long storeId, Long ownerId) {
|
||||||
|
return storeRepositoryPort.findStoreByIdAndOwnerId(storeId, ownerId)
|
||||||
|
.orElseThrow(() -> new BusinessException("STORE_ACCESS_DENIED", "매장에 대한 권한이 없습니다."));
|
||||||
|
}
|
||||||
|
|
||||||
|
private String calculateStoreStatus(Store store) {
|
||||||
|
if (!store.isActive()) {
|
||||||
|
return "비활성";
|
||||||
|
}
|
||||||
|
// 운영시간 기반 현재 상태 계산 로직
|
||||||
|
return "운영중";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getAISummary(Long storeId) {
|
||||||
|
// TODO: AI 분석 서비스 연동 구현
|
||||||
|
return "AI 요약 정보가 준비 중입니다.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private Double calculateDistance(Double lat1, Double lon1, Double lat2, Double lon2) {
|
||||||
|
if (lat1 == null || lon1 == null || lat2 == null || lon2 == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return geocodingPort.calculateDistance(
|
||||||
|
new Coordinates(lat1, lon1),
|
||||||
|
new Coordinates(lat2, lon2)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,26 +1,24 @@
|
|||||||
package com.ktds.hi.store.biz.usecase.out;
|
package com.ktds.hi.store.biz.usecase.out;
|
||||||
|
|
||||||
import java.time.Duration;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 캐시 포트 인터페이스
|
* 캐시 포트 인터페이스
|
||||||
* 캐시 기능을 정의
|
|
||||||
*/
|
*/
|
||||||
public interface CachePort {
|
public interface CachePort {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 캐시에서 매장 데이터 조회
|
* 캐시에서 매장 정보 조회
|
||||||
*/
|
*/
|
||||||
Optional<Object> getStoreCache(String key);
|
<T> T getStoreCache(String key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 캐시에 매장 데이터 저장
|
* 캐시에 매장 정보 저장
|
||||||
*/
|
*/
|
||||||
void putStoreCache(String key, Object value, Duration ttl);
|
void putStoreCache(String key, Object value, long ttlSeconds);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 캐시 무효화
|
* 캐시 무효화
|
||||||
*/
|
*/
|
||||||
void invalidateStoreCache(Long storeId);
|
void invalidateStoreCache(Object key);
|
||||||
}
|
}
|
||||||
@ -1,4 +1,16 @@
|
|||||||
package com.ktds.hi.store.biz.usecase.out;
|
package com.ktds.hi.store.biz.usecase.out;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 좌표 정보 값 객체
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public class Coordinates {
|
public class Coordinates {
|
||||||
|
private Double latitude;
|
||||||
|
private Double longitude;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,17 @@
|
|||||||
package com.ktds.hi.store.biz.usecase.out;
|
package com.ktds.hi.store.biz.usecase.out;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 지오코딩 포트 인터페이스
|
||||||
|
*/
|
||||||
public interface GeocodingPort {
|
public interface GeocodingPort {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주소를 좌표로 변환
|
||||||
|
*/
|
||||||
|
Coordinates getCoordinates(String address);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 두 좌표 간 거리 계산 (km)
|
||||||
|
*/
|
||||||
|
Double calculateDistance(Coordinates coord1, Coordinates coord2);
|
||||||
}
|
}
|
||||||
@ -1,4 +1,24 @@
|
|||||||
package com.ktds.hi.store.biz.usecase.out;
|
package com.ktds.hi.store.biz.usecase.out;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 태그 리포지토리 포트 인터페이스
|
||||||
|
*/
|
||||||
public interface StoreTagRepositoryPort {
|
public interface StoreTagRepositoryPort {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 ID로 태그 목록 조회
|
||||||
|
*/
|
||||||
|
List<String> findTagsByStoreId(Long storeId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 태그 저장
|
||||||
|
*/
|
||||||
|
void saveStoreTags(Long storeId, List<String> tags);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 태그 삭제
|
||||||
|
*/
|
||||||
|
void deleteTagsByStoreId(Long storeId);
|
||||||
}
|
}
|
||||||
@ -1,23 +1,13 @@
|
|||||||
package com.ktds.hi.store.biz.domain;
|
package com.ktds.hi.store.biz.domain;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 도메인 클래스
|
* 메뉴 도메인 엔티티
|
||||||
* 메뉴 정보를 담는 도메인 객체
|
|
||||||
*
|
|
||||||
* @author 하이오더 개발팀
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class Menu {
|
public class Menu {
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -27,164 +17,31 @@ public class Menu {
|
|||||||
private Integer price;
|
private Integer price;
|
||||||
private String category;
|
private String category;
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
private Boolean isAvailable;
|
private Boolean available;
|
||||||
private Integer orderCount;
|
|
||||||
private LocalDateTime createdAt;
|
|
||||||
private LocalDateTime updatedAt;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 기본 정보 업데이트
|
* 메뉴 정보 업데이트
|
||||||
*/
|
*/
|
||||||
public Menu updateInfo(String menuName, String description, Integer price) {
|
public void updateMenuInfo(String menuName, String description, Integer price,
|
||||||
return Menu.builder()
|
String category, String imageUrl) {
|
||||||
.id(this.id)
|
this.menuName = menuName;
|
||||||
.storeId(this.storeId)
|
this.description = description;
|
||||||
.menuName(menuName)
|
this.price = price;
|
||||||
.description(description)
|
this.category = category;
|
||||||
.price(price)
|
this.imageUrl = imageUrl;
|
||||||
.category(this.category)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.isAvailable(this.isAvailable)
|
|
||||||
.orderCount(this.orderCount)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 이미지 업데이트
|
* 메뉴 판매 상태 변경
|
||||||
*/
|
*/
|
||||||
public Menu updateImage(String imageUrl) {
|
public void setAvailable(Boolean available) {
|
||||||
return Menu.builder()
|
this.available = available;
|
||||||
.id(this.id)
|
|
||||||
.storeId(this.storeId)
|
|
||||||
.menuName(this.menuName)
|
|
||||||
.description(this.description)
|
|
||||||
.price(this.price)
|
|
||||||
.category(this.category)
|
|
||||||
.imageUrl(imageUrl)
|
|
||||||
.isAvailable(this.isAvailable)
|
|
||||||
.orderCount(this.orderCount)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 메뉴 판매 가능 상태 설정
|
* 메뉴 이용 가능 여부 확인
|
||||||
*/
|
*/
|
||||||
public Menu setAvailable(Boolean available) {
|
public boolean isAvailable() {
|
||||||
return Menu.builder()
|
return this.available != null && this.available;
|
||||||
.id(this.id)
|
|
||||||
.storeId(this.storeId)
|
|
||||||
.menuName(this.menuName)
|
|
||||||
.description(this.description)
|
|
||||||
.price(this.price)
|
|
||||||
.category(this.category)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.isAvailable(available)
|
|
||||||
.orderCount(this.orderCount)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 주문 수 증가
|
|
||||||
*/
|
|
||||||
public Menu incrementOrderCount() {
|
|
||||||
return Menu.builder()
|
|
||||||
.id(this.id)
|
|
||||||
.storeId(this.storeId)
|
|
||||||
.menuName(this.menuName)
|
|
||||||
.description(this.description)
|
|
||||||
.price(this.price)
|
|
||||||
.category(this.category)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.isAvailable(this.isAvailable)
|
|
||||||
.orderCount(this.orderCount != null ? this.orderCount + 1 : 1)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴 판매 가능 여부 확인
|
|
||||||
*/
|
|
||||||
public Boolean isAvailable() {
|
|
||||||
return this.isAvailable != null && this.isAvailable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 특정 매장에 속하는지 확인
|
|
||||||
*/
|
|
||||||
public boolean belongsToStore(Long storeId) {
|
|
||||||
return this.storeId != null && this.storeId.equals(storeId);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴가 유효한지 확인
|
|
||||||
*/
|
|
||||||
public boolean isValid() {
|
|
||||||
return this.menuName != null && !this.menuName.trim().isEmpty() &&
|
|
||||||
this.price != null && this.price > 0 &&
|
|
||||||
this.storeId != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴 카테고리 업데이트
|
|
||||||
*/
|
|
||||||
public Menu updateCategory(String category) {
|
|
||||||
return Menu.builder()
|
|
||||||
.id(this.id)
|
|
||||||
.storeId(this.storeId)
|
|
||||||
.menuName(this.menuName)
|
|
||||||
.description(this.description)
|
|
||||||
.price(this.price)
|
|
||||||
.category(category)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.isAvailable(this.isAvailable)
|
|
||||||
.orderCount(this.orderCount)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴 가격 할인 적용
|
|
||||||
*/
|
|
||||||
public Menu applyDiscount(double discountRate) {
|
|
||||||
if (discountRate < 0 || discountRate > 1) {
|
|
||||||
throw new IllegalArgumentException("할인율은 0~1 사이의 값이어야 합니다.");
|
|
||||||
}
|
|
||||||
|
|
||||||
int discountedPrice = (int) (this.price * (1 - discountRate));
|
|
||||||
|
|
||||||
return Menu.builder()
|
|
||||||
.id(this.id)
|
|
||||||
.storeId(this.storeId)
|
|
||||||
.menuName(this.menuName)
|
|
||||||
.description(this.description)
|
|
||||||
.price(discountedPrice)
|
|
||||||
.category(this.category)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.isAvailable(this.isAvailable)
|
|
||||||
.orderCount(this.orderCount)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 메뉴 정보가 변경되었는지 확인
|
|
||||||
*/
|
|
||||||
public boolean hasChanges(Menu other) {
|
|
||||||
if (other == null) return true;
|
|
||||||
|
|
||||||
return !this.menuName.equals(other.menuName) ||
|
|
||||||
!this.description.equals(other.description) ||
|
|
||||||
!this.price.equals(other.price) ||
|
|
||||||
!this.category.equals(other.category) ||
|
|
||||||
!this.isAvailable.equals(other.isAvailable);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,24 +1,19 @@
|
|||||||
|
// store/src/main/java/com/ktds/hi/store/biz/domain/Store.java
|
||||||
package com.ktds.hi.store.domain;
|
package com.ktds.hi.store.domain;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.NoArgsConstructor;
|
|
||||||
|
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 도메인 클래스
|
* 매장 도메인 엔티티
|
||||||
* 매장 정보를 담는 도메인 객체
|
* Clean Architecture의 Domain Layer
|
||||||
*
|
*
|
||||||
* @author 하이오더 개발팀
|
* @author 하이오더 개발팀
|
||||||
* @version 1.0.0
|
* @version 1.0.0
|
||||||
*/
|
*/
|
||||||
@Getter
|
@Getter
|
||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
|
||||||
@AllArgsConstructor
|
|
||||||
public class Store {
|
public class Store {
|
||||||
|
|
||||||
private Long id;
|
private Long id;
|
||||||
@ -27,160 +22,87 @@ public class Store {
|
|||||||
private String address;
|
private String address;
|
||||||
private Double latitude;
|
private Double latitude;
|
||||||
private Double longitude;
|
private Double longitude;
|
||||||
private String category;
|
|
||||||
private String description;
|
private String description;
|
||||||
private String phone;
|
private String phone;
|
||||||
private String operatingHours;
|
private String operatingHours;
|
||||||
private List<String> tags;
|
private String category;
|
||||||
private StoreStatus status;
|
|
||||||
private Double rating;
|
private Double rating;
|
||||||
private Integer reviewCount;
|
private Integer reviewCount;
|
||||||
private String imageUrl;
|
private StoreStatus status;
|
||||||
private LocalDateTime createdAt;
|
private LocalDateTime createdAt;
|
||||||
private LocalDateTime updatedAt;
|
private LocalDateTime updatedAt;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 기본 정보 업데이트
|
* 매장 기본 정보 업데이트
|
||||||
*/
|
*/
|
||||||
public Store updateBasicInfo(String storeName, String address, String description,
|
public void updateBasicInfo(String storeName, String address, String description,
|
||||||
String phone, String operatingHours) {
|
String phone, String operatingHours) {
|
||||||
return Store.builder()
|
this.storeName = storeName;
|
||||||
.id(this.id)
|
this.address = address;
|
||||||
.ownerId(this.ownerId)
|
this.description = description;
|
||||||
.storeName(storeName)
|
this.phone = phone;
|
||||||
.address(address)
|
this.operatingHours = operatingHours;
|
||||||
.latitude(this.latitude)
|
this.updatedAt = LocalDateTime.now();
|
||||||
.longitude(this.longitude)
|
|
||||||
.category(this.category)
|
|
||||||
.description(description)
|
|
||||||
.phone(phone)
|
|
||||||
.operatingHours(operatingHours)
|
|
||||||
.tags(this.tags)
|
|
||||||
.status(this.status)
|
|
||||||
.rating(this.rating)
|
|
||||||
.reviewCount(this.reviewCount)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 위치 정보 업데이트
|
* 매장 위치 업데이트
|
||||||
*/
|
*/
|
||||||
public Store updateLocation(Double latitude, Double longitude) {
|
public void updateLocation(Coordinates coordinates) {
|
||||||
return Store.builder()
|
this.latitude = coordinates.getLatitude();
|
||||||
.id(this.id)
|
this.longitude = coordinates.getLongitude();
|
||||||
.ownerId(this.ownerId)
|
this.updatedAt = LocalDateTime.now();
|
||||||
.storeName(this.storeName)
|
|
||||||
.address(this.address)
|
|
||||||
.latitude(latitude)
|
|
||||||
.longitude(longitude)
|
|
||||||
.category(this.category)
|
|
||||||
.description(this.description)
|
|
||||||
.phone(this.phone)
|
|
||||||
.operatingHours(this.operatingHours)
|
|
||||||
.tags(this.tags)
|
|
||||||
.status(this.status)
|
|
||||||
.rating(this.rating)
|
|
||||||
.reviewCount(this.reviewCount)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 평점 및 리뷰 수 업데이트
|
* 매장 평점 업데이트
|
||||||
*/
|
*/
|
||||||
public Store updateRating(Double rating, Integer reviewCount) {
|
public void updateRating(Double rating, Integer reviewCount) {
|
||||||
return Store.builder()
|
this.rating = rating;
|
||||||
.id(this.id)
|
this.reviewCount = reviewCount;
|
||||||
.ownerId(this.ownerId)
|
this.updatedAt = LocalDateTime.now();
|
||||||
.storeName(this.storeName)
|
|
||||||
.address(this.address)
|
|
||||||
.latitude(this.latitude)
|
|
||||||
.longitude(this.longitude)
|
|
||||||
.category(this.category)
|
|
||||||
.description(this.description)
|
|
||||||
.phone(this.phone)
|
|
||||||
.operatingHours(this.operatingHours)
|
|
||||||
.tags(this.tags)
|
|
||||||
.status(this.status)
|
|
||||||
.rating(rating)
|
|
||||||
.reviewCount(reviewCount)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 활성화
|
* 매장 활성화
|
||||||
*/
|
*/
|
||||||
public Store activate() {
|
public void activate() {
|
||||||
return Store.builder()
|
this.status = StoreStatus.ACTIVE;
|
||||||
.id(this.id)
|
this.updatedAt = LocalDateTime.now();
|
||||||
.ownerId(this.ownerId)
|
|
||||||
.storeName(this.storeName)
|
|
||||||
.address(this.address)
|
|
||||||
.latitude(this.latitude)
|
|
||||||
.longitude(this.longitude)
|
|
||||||
.category(this.category)
|
|
||||||
.description(this.description)
|
|
||||||
.phone(this.phone)
|
|
||||||
.operatingHours(this.operatingHours)
|
|
||||||
.tags(this.tags)
|
|
||||||
.status(StoreStatus.ACTIVE)
|
|
||||||
.rating(this.rating)
|
|
||||||
.reviewCount(this.reviewCount)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 비활성화
|
* 매장 비활성화
|
||||||
*/
|
*/
|
||||||
public Store deactivate() {
|
public void deactivate() {
|
||||||
return Store.builder()
|
this.status = StoreStatus.INACTIVE;
|
||||||
.id(this.id)
|
this.updatedAt = LocalDateTime.now();
|
||||||
.ownerId(this.ownerId)
|
|
||||||
.storeName(this.storeName)
|
|
||||||
.address(this.address)
|
|
||||||
.latitude(this.latitude)
|
|
||||||
.longitude(this.longitude)
|
|
||||||
.category(this.category)
|
|
||||||
.description(this.description)
|
|
||||||
.phone(this.phone)
|
|
||||||
.operatingHours(this.operatingHours)
|
|
||||||
.tags(this.tags)
|
|
||||||
.status(StoreStatus.INACTIVE)
|
|
||||||
.rating(this.rating)
|
|
||||||
.reviewCount(this.reviewCount)
|
|
||||||
.imageUrl(this.imageUrl)
|
|
||||||
.createdAt(this.createdAt)
|
|
||||||
.updatedAt(LocalDateTime.now())
|
|
||||||
.build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 활성 상태 확인
|
* 매장 삭제 (소프트 삭제)
|
||||||
|
*/
|
||||||
|
public void delete() {
|
||||||
|
this.status = StoreStatus.DELETED;
|
||||||
|
this.updatedAt = LocalDateTime.now();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 활성 상태 확인
|
||||||
*/
|
*/
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
return StoreStatus.ACTIVE.equals(this.status);
|
return this.status == StoreStatus.ACTIVE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 소유권 확인
|
* 점주 소유 확인
|
||||||
*/
|
*/
|
||||||
public boolean isOwnedBy(Long ownerId) {
|
public boolean isOwnedBy(Long ownerId) {
|
||||||
return this.ownerId != null && this.ownerId.equals(ownerId);
|
return this.ownerId.equals(ownerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 두 좌표 간의 거리 계산 (킬로미터)
|
* 거리 계산
|
||||||
*/
|
*/
|
||||||
public Double calculateDistance(Double targetLatitude, Double targetLongitude) {
|
public Double calculateDistance(Double targetLatitude, Double targetLongitude) {
|
||||||
if (this.latitude == null || this.longitude == null ||
|
if (this.latitude == null || this.longitude == null ||
|
||||||
@ -188,17 +110,18 @@ public class Store {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int EARTH_RADIUS = 6371; // 지구 반지름 (킬로미터)
|
// Haversine 공식을 사용한 거리 계산
|
||||||
|
double earthRadius = 6371; // 지구 반지름 (km)
|
||||||
|
|
||||||
double latDistance = Math.toRadians(targetLatitude - this.latitude);
|
double dLat = Math.toRadians(targetLatitude - this.latitude);
|
||||||
double lonDistance = Math.toRadians(targetLongitude - this.longitude);
|
double dLon = Math.toRadians(targetLongitude - this.longitude);
|
||||||
|
|
||||||
double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
|
double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
|
||||||
+ Math.cos(Math.toRadians(this.latitude)) * Math.cos(Math.toRadians(targetLatitude))
|
Math.cos(Math.toRadians(this.latitude)) * Math.cos(Math.toRadians(targetLatitude)) *
|
||||||
* Math.sin(lonDistance / 2) * Math.sin(lonDistance / 2);
|
Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
||||||
|
|
||||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
|
||||||
return EARTH_RADIUS * c;
|
return earthRadius * c;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,32 +2,12 @@ package com.ktds.hi.store.domain;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 매장 상태 열거형
|
* 매장 상태 열거형
|
||||||
* 매장의 운영 상태를 정의
|
|
||||||
*
|
|
||||||
* @author 하이오더 개발팀
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
*/
|
||||||
public enum StoreStatus {
|
public enum StoreStatus {
|
||||||
|
|
||||||
/**
|
|
||||||
* 활성 상태 - 정상 운영 중
|
|
||||||
*/
|
|
||||||
ACTIVE("활성"),
|
ACTIVE("활성"),
|
||||||
|
|
||||||
/**
|
|
||||||
* 비활성 상태 - 임시 휴업
|
|
||||||
*/
|
|
||||||
INACTIVE("비활성"),
|
INACTIVE("비활성"),
|
||||||
|
DELETED("삭제됨"),
|
||||||
/**
|
PENDING("승인대기");
|
||||||
* 일시 정지 상태 - 관리자에 의한 일시 정지
|
|
||||||
*/
|
|
||||||
SUSPENDED("일시정지"),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 삭제 상태 - 영구 삭제 (소프트 삭제)
|
|
||||||
*/
|
|
||||||
DELETED("삭제");
|
|
||||||
|
|
||||||
private final String description;
|
private final String description;
|
||||||
|
|
||||||
@ -38,33 +18,4 @@ public enum StoreStatus {
|
|||||||
public String getDescription() {
|
public String getDescription() {
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 문자열로부터 StoreStatus 변환
|
|
||||||
*/
|
|
||||||
public static StoreStatus fromString(String status) {
|
|
||||||
if (status == null) {
|
|
||||||
return INACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return StoreStatus.valueOf(status.toUpperCase());
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
return INACTIVE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 매장이 서비스 가능한 상태인지 확인
|
|
||||||
*/
|
|
||||||
public boolean isServiceable() {
|
|
||||||
return this == ACTIVE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 매장이 삭제된 상태인지 확인
|
|
||||||
*/
|
|
||||||
public boolean isDeleted() {
|
|
||||||
return this == DELETED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@ -1,4 +1,120 @@
|
|||||||
|
// store/src/main/java/com/ktds/hi/store/infra/controller/StoreController.java
|
||||||
package com.ktds.hi.store.infra.controller;
|
package com.ktds.hi.store.infra.controller;
|
||||||
|
|
||||||
|
import com.ktds.hi.store.biz.usecase.in.StoreUseCase;
|
||||||
|
import com.ktds.hi.store.infra.dto.*;
|
||||||
|
import com.ktds.hi.common.dto.ApiResponse;
|
||||||
|
import com.ktds.hi.common.security.JwtTokenProvider;
|
||||||
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.http.ResponseEntity;
|
||||||
|
import org.springframework.security.access.prepost.PreAuthorize;
|
||||||
|
import org.springframework.validation.annotation.Validated;
|
||||||
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
import jakarta.validation.Valid;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 관리 컨트롤러
|
||||||
|
* 매장 등록, 수정, 삭제, 조회 기능 제공
|
||||||
|
*
|
||||||
|
* @author 하이오더 개발팀
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
@Tag(name = "매장 관리", description = "매장 등록, 수정, 삭제, 조회 API")
|
||||||
|
@Slf4j
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/api/stores")
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
@Validated
|
||||||
public class StoreController {
|
public class StoreController {
|
||||||
|
|
||||||
|
private final StoreUseCase storeUseCase;
|
||||||
|
private final JwtTokenProvider jwtTokenProvider;
|
||||||
|
|
||||||
|
@Operation(summary = "매장 등록", description = "새로운 매장을 등록합니다.")
|
||||||
|
@PostMapping
|
||||||
|
@PreAuthorize("hasRole('OWNER')")
|
||||||
|
public ResponseEntity<ApiResponse<StoreCreateResponse>> createStore(
|
||||||
|
@Valid @RequestBody StoreCreateRequest request,
|
||||||
|
HttpServletRequest httpRequest) {
|
||||||
|
|
||||||
|
Long ownerId = jwtTokenProvider.extractOwnerInfo(httpRequest);
|
||||||
|
StoreCreateResponse response = storeUseCase.createStore(ownerId, request);
|
||||||
|
|
||||||
|
return ResponseEntity.status(HttpStatus.CREATED)
|
||||||
|
.body(ApiResponse.success(response, "매장이 성공적으로 등록되었습니다."));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "내 매장 목록 조회", description = "점주가 등록한 매장 목록을 조회합니다.")
|
||||||
|
@GetMapping("/my")
|
||||||
|
@PreAuthorize("hasRole('OWNER')")
|
||||||
|
public ResponseEntity<ApiResponse<List<MyStoreListResponse>>> getMyStores(
|
||||||
|
HttpServletRequest httpRequest) {
|
||||||
|
|
||||||
|
Long ownerId = jwtTokenProvider.extractOwnerInfo(httpRequest);
|
||||||
|
List<MyStoreListResponse> responses = storeUseCase.getMyStores(ownerId);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(responses, "내 매장 목록 조회 완료"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "매장 상세 조회", description = "매장의 상세 정보를 조회합니다.")
|
||||||
|
@GetMapping("/{storeId}")
|
||||||
|
public ResponseEntity<ApiResponse<StoreDetailResponse>> getStoreDetail(
|
||||||
|
@Parameter(description = "매장 ID") @PathVariable Long storeId) {
|
||||||
|
|
||||||
|
StoreDetailResponse response = storeUseCase.getStoreDetail(storeId);
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(response, "매장 상세 정보 조회 완료"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "매장 정보 수정", description = "매장 정보를 수정합니다.")
|
||||||
|
@PutMapping("/{storeId}")
|
||||||
|
@PreAuthorize("hasRole('OWNER')")
|
||||||
|
public ResponseEntity<ApiResponse<StoreUpdateResponse>> updateStore(
|
||||||
|
@Parameter(description = "매장 ID") @PathVariable Long storeId,
|
||||||
|
@Valid @RequestBody StoreUpdateRequest request,
|
||||||
|
HttpServletRequest httpRequest) {
|
||||||
|
|
||||||
|
Long ownerId = jwtTokenProvider.extractOwnerInfo(httpRequest);
|
||||||
|
StoreUpdateResponse response = storeUseCase.updateStore(storeId, ownerId, request);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(response, "매장 정보 수정 완료"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "매장 삭제", description = "매장을 삭제합니다.")
|
||||||
|
@DeleteMapping("/{storeId}")
|
||||||
|
@PreAuthorize("hasRole('OWNER')")
|
||||||
|
public ResponseEntity<ApiResponse<StoreDeleteResponse>> deleteStore(
|
||||||
|
@Parameter(description = "매장 ID") @PathVariable Long storeId,
|
||||||
|
HttpServletRequest httpRequest) {
|
||||||
|
|
||||||
|
Long ownerId = jwtTokenProvider.extractOwnerInfo(httpRequest);
|
||||||
|
StoreDeleteResponse response = storeUseCase.deleteStore(storeId, ownerId);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(response, "매장 삭제 완료"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Operation(summary = "매장 검색", description = "조건에 따라 매장을 검색합니다.")
|
||||||
|
@GetMapping("/search")
|
||||||
|
public ResponseEntity<ApiResponse<List<StoreSearchResponse>>> searchStores(
|
||||||
|
@Parameter(description = "검색 키워드") @RequestParam(required = false) String keyword,
|
||||||
|
@Parameter(description = "카테고리") @RequestParam(required = false) String category,
|
||||||
|
@Parameter(description = "태그") @RequestParam(required = false) String tags,
|
||||||
|
@Parameter(description = "위도") @RequestParam(required = false) Double latitude,
|
||||||
|
@Parameter(description = "경도") @RequestParam(required = false) Double longitude,
|
||||||
|
@Parameter(description = "검색 반경(km)") @RequestParam(defaultValue = "5") Integer radius,
|
||||||
|
@Parameter(description = "페이지 번호") @RequestParam(defaultValue = "0") Integer page,
|
||||||
|
@Parameter(description = "페이지 크기") @RequestParam(defaultValue = "20") Integer size) {
|
||||||
|
|
||||||
|
List<StoreSearchResponse> responses = storeUseCase.searchStores(
|
||||||
|
keyword, category, tags, latitude, longitude, radius, page, size);
|
||||||
|
|
||||||
|
return ResponseEntity.ok(ApiResponse.success(responses, "매장 검색 완료"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,54 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import com.ktds.hi.store.biz.domain.Menu;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.Min;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴 등록 요청 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "메뉴 등록 요청")
|
||||||
public class MenuCreateRequest {
|
public class MenuCreateRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "메뉴명은 필수입니다.")
|
||||||
|
@Schema(description = "메뉴명", example = "김치찌개")
|
||||||
|
private String menuName;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 설명", example = "얼큰한 김치찌개")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Min(value = 0, message = "가격은 0원 이상이어야 합니다.")
|
||||||
|
@Schema(description = "가격", example = "8000")
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 카테고리", example = "메인")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "이미지 URL", example = "https://example.com/kimchi.jpg")
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
@Schema(description = "이용 가능 여부", example = "true")
|
||||||
|
private Boolean available = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 도메인 객체로 변환
|
||||||
|
*/
|
||||||
|
public Menu toDomain(Long storeId) {
|
||||||
|
return Menu.builder()
|
||||||
|
.storeId(storeId)
|
||||||
|
.menuName(this.menuName)
|
||||||
|
.description(this.description)
|
||||||
|
.price(this.price)
|
||||||
|
.category(this.category)
|
||||||
|
.imageUrl(this.imageUrl)
|
||||||
|
.available(this.available != null ? this.available : true)
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,55 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import com.ktds.hi.store.biz.domain.Menu;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 메뉴 응답 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "메뉴 응답")
|
||||||
public class MenuResponse {
|
public class MenuResponse {
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 ID", example = "1")
|
||||||
|
private Long menuId;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴명", example = "김치찌개")
|
||||||
|
private String menuName;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 설명", example = "얼큰한 김치찌개")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "가격", example = "8000")
|
||||||
|
private Integer price;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 카테고리", example = "메인")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "이미지 URL")
|
||||||
|
private String imageUrl;
|
||||||
|
|
||||||
|
@Schema(description = "이용 가능 여부", example = "true")
|
||||||
|
private Boolean available;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 도메인 객체로부터 생성
|
||||||
|
*/
|
||||||
|
public static MenuResponse from(Menu menu) {
|
||||||
|
return MenuResponse.builder()
|
||||||
|
.menuId(menu.getId())
|
||||||
|
.menuName(menu.getMenuName())
|
||||||
|
.description(menu.getDescription())
|
||||||
|
.price(menu.getPrice())
|
||||||
|
.category(menu.getCategory())
|
||||||
|
.imageUrl(menu.getImageUrl())
|
||||||
|
.available(menu.getAvailable())
|
||||||
|
.build();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -1,4 +1,42 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 내 매장 목록 응답 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "내 매장 목록 응답")
|
||||||
public class MyStoreListResponse {
|
public class MyStoreListResponse {
|
||||||
|
|
||||||
|
@Schema(description = "매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "카테고리", example = "한식")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "평점", example = "4.5")
|
||||||
|
private Double rating;
|
||||||
|
|
||||||
|
@Schema(description = "리뷰 수", example = "127")
|
||||||
|
private Integer reviewCount;
|
||||||
|
|
||||||
|
@Schema(description = "운영 상태", example = "운영중")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "운영시간", example = "월-금 09:00-21:00")
|
||||||
|
private String operatingHours;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,48 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 등록 요청 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 등록 요청")
|
||||||
public class StoreCreateRequest {
|
public class StoreCreateRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "매장명은 필수입니다.")
|
||||||
|
@Size(max = 100, message = "매장명은 100자를 초과할 수 없습니다.")
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@NotBlank(message = "주소는 필수입니다.")
|
||||||
|
@Schema(description = "매장 주소", example = "서울시 강남구 테헤란로 123")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "매장 설명", example = "맛있는 한식당입니다.")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Schema(description = "운영시간", example = "월-금 09:00-21:00, 토-일 10:00-20:00")
|
||||||
|
private String operatingHours;
|
||||||
|
|
||||||
|
@NotBlank(message = "카테고리는 필수입니다.")
|
||||||
|
@Schema(description = "카테고리", example = "한식")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "매장 태그 목록", example = "[\"맛집\", \"혼밥\", \"가성비\"]")
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 목록")
|
||||||
|
private List<MenuCreateRequest> menus;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,27 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 등록 응답 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 등록 응답")
|
||||||
public class StoreCreateResponse {
|
public class StoreCreateResponse {
|
||||||
|
|
||||||
|
@Schema(description = "생성된 매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@Schema(description = "응답 메시지", example = "매장이 성공적으로 등록되었습니다.")
|
||||||
|
private String message;
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -12,8 +13,12 @@ import lombok.NoArgsConstructor;
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 삭제 응답")
|
||||||
public class StoreDeleteResponse {
|
public class StoreDeleteResponse {
|
||||||
|
|
||||||
private Boolean success;
|
@Schema(description = "삭제된 매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "응답 메시지", example = "매장이 성공적으로 삭제되었습니다.")
|
||||||
private String message;
|
private String message;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,65 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 상세 조회 응답 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 상세 조회 응답")
|
||||||
public class StoreDetailResponse {
|
public class StoreDetailResponse {
|
||||||
|
|
||||||
|
@Schema(description = "매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "위도", example = "37.5665")
|
||||||
|
private Double latitude;
|
||||||
|
|
||||||
|
@Schema(description = "경도", example = "126.9780")
|
||||||
|
private Double longitude;
|
||||||
|
|
||||||
|
@Schema(description = "매장 설명", example = "맛있는 한식당입니다.")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Schema(description = "운영시간", example = "월-금 09:00-21:00")
|
||||||
|
private String operatingHours;
|
||||||
|
|
||||||
|
@Schema(description = "카테고리", example = "한식")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "평점", example = "4.5")
|
||||||
|
private Double rating;
|
||||||
|
|
||||||
|
@Schema(description = "리뷰 수", example = "127")
|
||||||
|
private Integer reviewCount;
|
||||||
|
|
||||||
|
@Schema(description = "매장 상태", example = "ACTIVE")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@Schema(description = "매장 태그 목록")
|
||||||
|
private List<String> tags;
|
||||||
|
|
||||||
|
@Schema(description = "메뉴 목록")
|
||||||
|
private List<MenuResponse> menus;
|
||||||
|
|
||||||
|
@Schema(description = "AI 요약 정보")
|
||||||
|
private String aiSummary;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,39 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Builder;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 검색 응답 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@Builder
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 검색 응답")
|
||||||
public class StoreSearchResponse {
|
public class StoreSearchResponse {
|
||||||
|
|
||||||
|
@Schema(description = "매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@Schema(description = "주소", example = "서울시 강남구 테헤란로 123")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "카테고리", example = "한식")
|
||||||
|
private String category;
|
||||||
|
|
||||||
|
@Schema(description = "평점", example = "4.5")
|
||||||
|
private Double rating;
|
||||||
|
|
||||||
|
@Schema(description = "리뷰 수", example = "127")
|
||||||
|
private Integer reviewCount;
|
||||||
|
|
||||||
|
@Schema(description = "거리(km)", example = "1.2")
|
||||||
|
private Double distance;
|
||||||
}
|
}
|
||||||
@ -1,4 +1,41 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.Size;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 매장 수정 요청 DTO
|
||||||
|
*/
|
||||||
|
@Getter
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 수정 요청")
|
||||||
public class StoreUpdateRequest {
|
public class StoreUpdateRequest {
|
||||||
|
|
||||||
|
@NotBlank(message = "매장명은 필수입니다.")
|
||||||
|
@Size(max = 100, message = "매장명은 100자를 초과할 수 없습니다.")
|
||||||
|
@Schema(description = "매장명", example = "맛집 한번 가볼래?")
|
||||||
|
private String storeName;
|
||||||
|
|
||||||
|
@NotBlank(message = "주소는 필수입니다.")
|
||||||
|
@Schema(description = "매장 주소", example = "서울시 강남구 테헤란로 123")
|
||||||
|
private String address;
|
||||||
|
|
||||||
|
@Schema(description = "매장 설명", example = "맛있는 한식당입니다.")
|
||||||
|
private String description;
|
||||||
|
|
||||||
|
@Schema(description = "전화번호", example = "02-1234-5678")
|
||||||
|
private String phone;
|
||||||
|
|
||||||
|
@Schema(description = "운영시간", example = "월-금 09:00-21:00, 토-일 10:00-20:00")
|
||||||
|
private String operatingHours;
|
||||||
|
|
||||||
|
@Schema(description = "매장 태그 목록", example = "[\"맛집\", \"혼밥\", \"가성비\"]")
|
||||||
|
private List<String> tags;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.ktds.hi.store.infra.dto;
|
package com.ktds.hi.store.infra.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Builder;
|
import lombok.Builder;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
@ -12,8 +13,12 @@ import lombok.NoArgsConstructor;
|
|||||||
@Builder
|
@Builder
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
|
@Schema(description = "매장 수정 응답")
|
||||||
public class StoreUpdateResponse {
|
public class StoreUpdateResponse {
|
||||||
|
|
||||||
private Boolean success;
|
@Schema(description = "매장 ID", example = "1")
|
||||||
|
private Long storeId;
|
||||||
|
|
||||||
|
@Schema(description = "응답 메시지", example = "매장 정보가 성공적으로 수정되었습니다.")
|
||||||
private String message;
|
private String message;
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user