Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ dependencies {
// mysql
runtimeOnly 'com.microsoft.sqlserver:mssql-jdbc'
runtimeOnly 'com.mysql:mysql-connector-j:8.0.33'
runtimeOnly 'com.mysql:mysql-connector-j'

// test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,28 @@ public ResponseEntity<ApiResponse<Object>> getTopStore(
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.from(response));
}

@Operation(
summary = "메인페이지 가맹점 지역 개수 조회",
description =
"""
- 지역은 가맹점 region에서 추출하며, 서울, 인천 지역 제외한 가맹점 지역 개수를 조회합니다.
""")
@GetMapping("/region-count")
public ResponseEntity<ApiResponse<Object>> getStoreRegionCount() {
StoreRegionCountResponse response = storeService.getStoreRegionCount();
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.from(response));
}

@Operation(
summary = "가맹점 찾기 화면 리스트/검색 통합 조회",
description =
"""
- keyword가 없으면: GPS 또는 디폴트 위치(인천 서구) 기준 전체 가맹점 조회
- keyword가 주소(00동, 00구 등)일 경우: 해당 위치 기준 반경 5km 가맹점 조회
- keyword가 음식명 등 일반 키워드일 경우: 현재 위치 기준 가맹점명/주소에 포함된 가맹점 조회
- category 필터 선택안하면 전체 가맹점 조회
- 정렬: 거리순(DISTANCE), 별점순(RATING), 리뷰순(REVIEW_COUNT), 조회수순(VIEW_COUNT)
""")
- keyword가 없으면: GPS 또는 디폴트 위치(인천 서구) 기준 전체 가맹점 조회
- keyword가 주소(00동, 00구 등)일 경우: 해당 위치 기준 반경 5km 가맹점 조회
- keyword가 음식명 등 일반 키워드일 경우: 현재 위치 기준 가맹점명/주소에 포함된 가맹점 조회
- category 필터 선택안하면 전체 가맹점 조회
- 정렬: 거리순(DISTANCE), 별점순(RATING), 리뷰순(REVIEW_COUNT), 조회수순(VIEW_COUNT)
""")
@GetMapping("/list")
public ResponseEntity<ApiResponse<Object>> getStoresList(
@Parameter(description = "인천 서구의 임의의 위도 값", example = "37.545472")
Expand All @@ -76,12 +88,12 @@ public ResponseEntity<ApiResponse<Object>> getStoresList(
summary = "가맹점 지도 화면 리스트/검색 통합 조회",
description =
"""
- keyword가 없으면: GPS 또는 디폴트 위치(인천 서구) 기준 전체 가맹점 조회
- keyword가 주소(00동, 00구 등)일 경우: 해당 위치 기준 반경 5km 가맹점 조회
- keyword가 음식명 등 일반 키워드일 경우: 현재 위치 기준 가맹점명/주소에 포함된 가맹점 조회
- category 필터 선택안하면 전체 가맹점 조회
- radius를 통해 반경을 조절할 수 있다.
""")
- keyword가 없으면: GPS 또는 디폴트 위치(인천 서구) 기준 전체 가맹점 조회
- keyword가 주소(00동, 00구 등)일 경우: 해당 위치 기준 반경 5km 가맹점 조회
- keyword가 음식명 등 일반 키워드일 경우: 현재 위치 기준 가맹점명/주소에 포함된 가맹점 조회
- category 필터 선택안하면 전체 가맹점 조회
- radius를 통해 반경을 조절할 수 있다.
""")
@GetMapping("/map")
public ResponseEntity<ApiResponse<Object>> getStoreMapList(
@Parameter(description = "인천 서구의 임의의 위도 값", example = "37.545472")
Expand All @@ -107,13 +119,13 @@ public ResponseEntity<ApiResponse<Object>> getStoreMapList(
summary = "가맹점 상세 정보 조회",
description =
"""
- 가맹점 ID를 통해 가맹점 정보를 조회합니다.
- 가맹점 ID, 가맹점 카테고리, 가맹점 이름, 가맹점 주소, 영업시간, 스크랩 수, 업데이트 일자, 리뷰 수, 별점, 스크랩 여부를 포함합니다.
- 로그인 한 경우에는 스크랩 여부가 true/false로 반환되고 로그인 하지 않은 경우에는 null로 반환됩니다.
- 가맹점 영업시간 정보는 구글 API 호출을 통해 조회하고 캐싱되어 30일간 유지됩니다.
- 영업시간 정보가 없는 경우, 영업시간 리스트에서 휴무일인 경우에 null로 반환됩니다.
- 가맹점이 ID로 조회되지 않는 경우에는 STORE_NOT_FOUND 400 에러를 반환합니다.
""")
- 가맹점 ID를 통해 가맹점 정보를 조회합니다.
- 가맹점 ID, 가맹점 카테고리, 가맹점 이름, 가맹점 주소, 영업시간, 스크랩 수, 업데이트 일자, 리뷰 수, 별점, 스크랩 여부를 포함합니다.
- 로그인 한 경우에는 스크랩 여부가 true/false로 반환되고 로그인 하지 않은 경우에는 null로 반환됩니다.
- 가맹점 영업시간 정보는 구글 API 호출을 통해 조회하고 캐싱되어 30일간 유지됩니다.
- 영업시간 정보가 없는 경우, 영업시간 리스트에서 휴무일인 경우에 null로 반환됩니다.
- 가맹점이 ID로 조회되지 않는 경우에는 STORE_NOT_FOUND 400 에러를 반환합니다.
""")
@GetMapping("/{storeId}")
public ResponseEntity<ApiResponse<Object>> getStoreInfo(
@PathVariable("storeId") Long storeId,
Expand All @@ -127,12 +139,12 @@ public ResponseEntity<ApiResponse<Object>> getStoreInfo(
summary = "가맹점 메뉴 보러가기 및 길찾기",
description =
"""
- 가맹점 ID를 통해 가맹점 메뉴 보러가기 및 길찾기 링크를 조회합니다.
- 카카오 API를 통해 가맹점 ID를 조회하고, 해당 ID를 통해 메뉴 보러가기 및 길찾기 링크를 생성합니다.
- 카카오에 등록된 가맹점이 없는 경우에는 네이버 검색 링크로 대체합니다.
- 저장된 외부링크는 API 호출을 줄이기 위해 캐싱되어 30일간 유지됩니다.
- 가맹점이 ID로 조회되지 않는 경우에는 STORE_NOT_FOUND 400 에러를 반환합니다.
""")
- 가맹점 ID를 통해 가맹점 메뉴 보러가기 및 길찾기 링크를 조회합니다.
- 카카오 API를 통해 가맹점 ID를 조회하고, 해당 ID를 통해 메뉴 보러가기 및 길찾기 링크를 생성합니다.
- 카카오에 등록된 가맹점이 없는 경우에는 네이버 검색 링크로 대체합니다.
- 저장된 외부링크는 API 호출을 줄이기 위해 캐싱되어 30일간 유지됩니다.
- 가맹점이 ID로 조회되지 않는 경우에는 STORE_NOT_FOUND 400 에러를 반환합니다.
""")
@GetMapping("/{storeId}/external-links")
public ResponseEntity<ApiResponse<Object>> getStoreExternalLink(
@PathVariable("storeId") Long storeId) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.kkinikong.be.store.dto.response;

public record StoreRegionCountResponse(Long count) {

public static StoreRegionCountResponse from(Long count) {
return new StoreRegionCountResponse(count);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,23 @@ public interface StoreRepository extends JpaRepository<Store, Long>, StoreReposi
@Modifying(clearAutomatically = true)
@Query("UPDATE Store s SET s.viewCount = s.viewCount + :count WHERE s.id = :storeId")
void incrementViews(@Param("storeId") Long storeId, @Param("count") Long count);

@Query(
value =
"""
SELECT COUNT(DISTINCT city_name) AS city_count
FROM (
SELECT
CASE
WHEN region LIKE '서울%' OR region LIKE '인천%' THEN NULL
WHEN region LIKE '%광역시%' THEN SUBSTRING_INDEX(TRIM(region), ' ', 1)
WHEN region LIKE '%시' THEN SUBSTRING_INDEX(TRIM(region), ' ', -1)
ELSE NULL
END AS city_name
FROM stores
) t
WHERE city_name IS NOT NULL
""",
nativeQuery = true)
long getStoreRegionCount();
}
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,11 @@ public List<StoreCardResponse> getTopViewedStores(Double latitude, Double longit
return stores.stream().map(StoreCardResponse::from).toList();
}

public StoreRegionCountResponse getStoreRegionCount() {
Long regionCounts = storeRepository.getStoreRegionCount();
return StoreRegionCountResponse.from(regionCounts);
}

@Transactional
public StoreScrapResponse addScrap(Long storeId, Long userId) {
User user = getUserOrThrow(userId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,21 +121,27 @@ private Map<String, List<String>> parseOpeningHours(List<String> openingHours) {

private String convertTo24Hour(String time) {
try {
boolean isAm = time.contains("오전");
boolean isPm = time.contains("오후");
time = time.trim();

time = time.replace("오전", "").replace("오후", "").trim();
boolean isAm = time.contains("오전") || time.toUpperCase().contains("AM");
boolean isPm = time.contains("오후") || time.toUpperCase().contains("PM");

// 토큰 제거 (오전/오후/AM/PM)
time =
time.replace("오전", "")
.replace("오후", "")
.replace("AM", "")
.replace("PM", "")
.replace("am", "")
.replace("pm", "")
.trim();

String[] timeParts = time.split(":");
int hour = Integer.parseInt(timeParts[0].trim());
int minute = Integer.parseInt(timeParts[1].trim());

if (isPm && hour != 12) {
hour += 12;
}
if (isAm && hour == 12) {
hour = 0;
}
if (isPm && hour != 12) hour += 12;
if (isAm && hour == 12) hour = 0;

return String.format("%02d:%02d", hour, minute);
} catch (Exception e) {
Expand Down