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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import org.springframework.web.bind.annotation.*;

import com.onebridge.ouch.dto.hospital.response.AllDepartmentResponse;
import com.onebridge.ouch.dto.hospital.response.DepartmentMappingDto;
import com.onebridge.ouch.dto.hospital.response.HospitalDetailResponse;
import com.onebridge.ouch.dto.hospital.response.HospitalDistanceResponse;
import com.onebridge.ouch.dto.hospital.response.RegionMappingDto;
Expand All @@ -27,10 +27,11 @@ public class HospitalController {
private final DepartmentService departmentService;
private final RegionService regionService;

@Operation(summary = "거리 순 병원 조회 API", description = "입력된 진료과(department), 종별코드명(type) 위도(lat), 경도(lng)를 기준으로 병원 목록을 거리 순으로 조회합니다. "
@Operation(summary = "거리 순 병원 조회 API", description = "입력된 진료과(department), 종별코드명(type), 위도(lat), 경도(lng)를 기준으로 병원 목록을 거리 순으로 조회합니다. "
+ "진료과나 종별코드명를 입력하지 않으면 입력된 위도, 경도를 기준으로 모든 병원 목록을 거리 순으로 조회합니다. 위도, 경도는 필수로 입력해야 합니다. "
+ "진료과나 종별코드명은 선택적으로 입력 가능합니다. 진료과는 한글로만 입력이 가능하며, 종별코드명은 병원(약국 제외 모든 병원), 약국 두 가지의 키워드로만 입력이 됩니다."
+ "진료과가 존재하지 않는 경우가 있는데 이는 해당 병원이 진료과를 제공하지 않기 때문이며, 응답 필드 중에 department 뿐만 아니라 type(치과의원, 종합병원 등)을 진료과처럼 사용하면 웬만하면 알아볼 수 있습니다.")
+ "진료과가 존재하지 않는 경우가 있는데 이는 해당 병원이 진료과를 제공하지 않기 때문이며, 응답 필드 중에 department 뿐만 아니라 type(치과의원, 종합병원 등)을 진료과처럼 사용하면 웬만하면 알아볼 수 있습니다."
+ "종별코드명(type)에 약국을 입력하면 약국이 나오고, 병원을 입력하면 약국을 제외한 모든 의료기관이 나옵니다.")
@GetMapping("/search")
public List<HospitalDistanceResponse> searchHospitals(
@RequestParam(required = false) String department,
Expand All @@ -52,7 +53,7 @@ public HospitalDetailResponse getHospitalDetail(@PathVariable String ykiho) {

@Operation(summary = "진료과 목록 조회 API", description = "모든 진료과 목록을 조회합니다.")
@GetMapping("/departments")
public List<AllDepartmentResponse> getDepartments() {
public List<DepartmentMappingDto> getDepartments() {
return departmentService.getAllDepartments();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.onebridge.ouch.dto.hospital.response;

import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Getter
public class AllDepartmentResponse {
@Setter
@NoArgsConstructor
public class DepartmentMappingDto {
private Long code;
private String nameKr;
private String nameEn;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,24 +42,33 @@ List<Object[]> findAllOrderByDistance(
"GROUP_CONCAT(DISTINCT hd.department_name) as departments, " +
"(6371 * acos(cos(radians(:lat)) * cos(radians(h.lat)) " +
"* cos(radians(h.lng) - radians(:lng)) + sin(radians(:lat)) * sin(radians(h.lat)))) as distance " +
"FROM hospital h " +
"LEFT JOIN hospital_department hd ON h.ykiho = hd.ykiho " +
"WHERE (:type IS NULL OR h.type = :type " +
" OR (:type = '병원' AND h.type != '약국')) " + // 병원이면 약국 제외
"AND h.type != '요양병원' " +
"AND (:department1 IS NULL OR hd.department_name IN (:department1, :department2)) " + // 내과+가정의학과 포함
"AND h.lat IS NOT NULL AND h.lng IS NOT NULL " +
"AND (:sidoKr IS NULL OR h.sido = :sidoKr) " +
"GROUP BY h.ykiho " +
"ORDER BY distance ASC " +
"LIMIT :limit OFFSET :offset",
"FROM hospital h " +
"LEFT JOIN hospital_department hd ON h.ykiho = hd.ykiho " +
"WHERE (:type IS NULL OR h.type = :type " +
" OR (:type = '병원' AND h.type != '약국')) " + // 병원이면 약국 제외
"AND h.type != '요양병원' " +
"AND ( " +
" :department IS NULL " +
" OR ( :department IN ('내과', '가정의학과') " +
" AND hd.department_name IN ('내과', '가정의학과') ) " +
" OR ( :department = '치과' " +
" AND hd.department_name LIKE '%치%' ) " +
" OR ( :department = '흉부외과' " +
" AND hd.department_name LIKE '%흉부%' ) " +
" OR ( :department NOT IN ('내과', '가정의학과', '치과') " +
" AND hd.department_name = :department ) " +
" ) " +
"AND h.lat IS NOT NULL AND h.lng IS NOT NULL " +
"AND (:sidoKr IS NULL OR h.sido = :sidoKr) " +
"GROUP BY h.ykiho " +
"ORDER BY distance ASC " +
"LIMIT :limit OFFSET :offset",
nativeQuery = true)
List<Object[]> findWithConditionsOrderByDistance(
@Param("lat") double lat,
@Param("lng") double lng,
@Param("type") String type,
@Param("department1") String department1,
@Param("department2") String department2,
@Param("department") String department,
@Param("sidoKr") String sidoKr,
@Param("limit") int limit,
@Param("offset") int offset
Expand Down
Original file line number Diff line number Diff line change
@@ -1,29 +1,77 @@
package com.onebridge.ouch.service.department;

import java.io.InputStream;
import java.util.List;

import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.onebridge.ouch.dto.hospital.response.AllDepartmentResponse;

import com.onebridge.ouch.dto.hospital.response.DepartmentMappingDto;
import jakarta.annotation.PostConstruct;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;

import java.io.InputStream;
import java.util.*;

/**
* 1) data/department.json 을 로딩해서 List<DepartmentMappingDto> 로 저장
* 2) "한·영·중(any) 진료과명 → 한글(nameKr)" 매핑용 Map을 구축
* 3) getAllDepartments() 는 기존과 동일하게 전체 리스트를 반환
* 4) getKrDepartmentName(input) 를 통해 다국어 → 한글 매핑 기능 제공
*/
@Service
public class DepartmentService {
private List<AllDepartmentResponse> departmentList;
// JSON에서 읽어들인 전체 진료과 리스트 (DTO 객체)
private List<DepartmentMappingDto> departmentList;

// key: nameKr, or lower(nameEn), or nameZh → nameKr
private final Map<String, String> anyToKrDept = new HashMap<>();

@PostConstruct
public void init() throws Exception {
// 1) JSON 로드
ObjectMapper mapper = new ObjectMapper();
InputStream input = new ClassPathResource("data/department.json").getInputStream();
departmentList = mapper.readValue(input, new TypeReference<List<AllDepartmentResponse>>() {});
InputStream is = new ClassPathResource("data/department.json").getInputStream();
departmentList = mapper.readValue(is, new TypeReference<List<DepartmentMappingDto>>() {});

// 2) anyToKrDept 맵 구축
for (DepartmentMappingDto dm : departmentList) {
String kr = dm.getNameKr().trim();
String en = dm.getNameEn().trim().toLowerCase();
String zh = dm.getNameZh().trim();

// 하나의 진료과를 세 언어로 모두 매핑
anyToKrDept.put(kr, kr); // 한국어 입력
anyToKrDept.put(en, kr); // 영어(소문자) 입력
anyToKrDept.put(zh, kr); // 중국어 입력
}
}

public List<AllDepartmentResponse> getAllDepartments() {
return departmentList;
/**
* 전체 진료과 목록(한·영·중 포함) 반환
* 예) [{"code":1,"nameKr":"내과","nameEn":"Internal Medicine","nameZh":"内科"}, ...]
*/
public List<DepartmentMappingDto> getAllDepartments() {
return Collections.unmodifiableList(departmentList);
}

/**
* 입력된 진료과명(input)을 한글 진료과명(nameKr)으로 변환
* - 예1) input="Internal Medicine" → return "내과"
* - 예2) input="内科" → return "내과"
* - 예3) input="내과" → return "내과"
* - 매칭 실패 시 null 반환
*/
public String getKrDepartmentName(String input) {
if (input == null || input.isBlank()) {
return null;
}
String key = input.trim();
// 1) 한글 키가 있는 경우
if (anyToKrDept.containsKey(key)) {
return anyToKrDept.get(key);
}
// 2) 소문자 영어로 바꿔서 검색
String lower = key.toLowerCase();
return anyToKrDept.getOrDefault(lower, null);
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import com.onebridge.ouch.dto.hospital.response.HospitalDistanceResponse;
import com.onebridge.ouch.repository.hospital.HospitalRepository;
import com.onebridge.ouch.service.department.DepartmentService;

import lombok.RequiredArgsConstructor;

Expand All @@ -15,6 +16,7 @@
public class HospitalSearchService {
private final HospitalRepository hospitalRepository;
private final RegionService regionService;
private final DepartmentService departmentService;

public List<HospitalDistanceResponse> searchHospitals(
String department,
Expand All @@ -30,31 +32,17 @@ public List<HospitalDistanceResponse> searchHospitals(
// 1) 전달받은 sido를 한국어로 변환
String sidoKr = regionService.getKrSidoName(sido);

int offset = page * size;
List<Object[]> rawList;

// 진료과: 내과→내과,가정의학과 모두 포함
String department1 = null;
String department2 = null;
if (department != null && !department.isBlank()) {
if (department.equals("내과") || department.equals("가정의학과")) {
department1 = "내과";
department2 = "가정의학과";
} else {
department1 = department;
department2 = department;
}
}
// 2) 진료과(any) → 한글 진료과명 (신규 로직)
String deptKr = departmentService.getKrDepartmentName(department);

// type(종별코드명)도 null/입력값에 따라 처리
if ((department1 != null && department2 != null) || (type != null && !type.isBlank()) || (sidoKr != null)) {
rawList = hospitalRepository.findWithConditionsOrderByDistance(
lat, lng,
type, department1, department2, sidoKr, size, offset
);
} else {
rawList = hospitalRepository.findAllOrderByDistance(lat, lng, size, offset);
}
int offset = page * size;
List<Object[]> rawList = hospitalRepository.findWithConditionsOrderByDistance(
lat, lng,
type,
deptKr,
sidoKr,
size, offset
);

List<HospitalDistanceResponse> result = new ArrayList<>();
for (Object[] row : rawList) {
Expand Down