Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.neighbors.tohero.application.address.dto;

import com.neighbors.tohero.common.enums.TargetJob;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import org.hibernate.validator.constraints.Length;

public record SearchAddressRequest (
@Length(min = 1, max = 50)
@NotBlank
String searchAddress,

@NotNull
TargetJob targetJob
){
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.neighbors.tohero.application.address.dto;

import com.neighbors.tohero.domain.domain.address.model.Address;

import java.util.List;

public record SearchAddressResponse (
List<AddressInfo> addressInfos
){
public record AddressInfo(
long addressId,
String officeName,
String roadAddress
) {
public static AddressInfo from(Address address) {
return new AddressInfo(address.getAddressId(), address.getOfficeName(), address.getRoadAddress());
}
}

public static SearchAddressResponse from(List<Address> searchedAddresses) {

List<AddressInfo> addressInfos = searchedAddresses.stream()
.map(AddressInfo::from)
.toList();

return new SearchAddressResponse(addressInfos);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.neighbors.tohero.application.address.service;

import com.neighbors.tohero.application.address.dto.SearchAddressRequest;
import com.neighbors.tohero.application.address.dto.SearchAddressResponse;
import com.neighbors.tohero.application.baseResponse.BaseResponse;
import com.neighbors.tohero.application.baseResponse.BaseResponseMessage;
import com.neighbors.tohero.application.baseResponse.BaseResponseStatus;
import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.common.exception.address.AddressException;
import com.neighbors.tohero.domain.domain.address.model.Address;
import com.neighbors.tohero.domain.domain.address.service.GetAddress;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Service
@RequiredArgsConstructor
public class AddressService {

private final GetAddress getAddress;

public BaseResponse<SearchAddressResponse> searchAddress(SearchAddressRequest searchAddressRequest) {

List<Address> searchedAddresses = getAddressByQuery(searchAddressRequest.searchAddress().trim(), searchAddressRequest.targetJob());
if(searchedAddresses.isEmpty()) {
throw new AddressException(BaseResponseStatus.NO_RESULT, BaseResponseMessage.일치하는_관할서_정보가_없습니다.getMessage());
}

return new BaseResponse(
BaseResponseStatus.OK,
BaseResponseMessage.주소_검색이_성공적으로_응답되었습니다.getMessage(),
SearchAddressResponse.from(searchedAddresses)
);
}

private List<Address> getAddressByQuery(String officeName, TargetJob targetJob) {
List<Address> searchedAddressesByOfficeName = getAddress.searchAddressByOfficeName(officeName, targetJob);
List<Address> searchedAddressesByRoadAddress = getAddress.searchAddressByRoadAddress(officeName, targetJob);

return Stream.concat(searchedAddressesByOfficeName.stream(), searchedAddressesByRoadAddress.stream())
.collect(Collectors.toMap(
Address::getAddressId,
address -> address,
(existing, replacement) -> existing
))
.values()
.stream()
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,12 @@ public enum BaseResponseMessage {

//main page
메인페이지_조회가_정상적으로_실행되었습니다("메인페이지 조회가 정상적으로 실행되었습니다"),
메인페이지_편지_무한페이징_조회가_정상적으로_실행되었습니다("메인페이지 편지 무한페이징 조회가 정상적으로 실행되었습니다");
메인페이지_편지_무한페이징_조회가_정상적으로_실행되었습니다("메인페이지 편지 무한페이징 조회가 정상적으로 실행되었습니다"),

//address
주소_검색_쿼리의_길이는_1부터_50까지만_가능합니다("주소 검색 쿼리의 길이는 1부터 50까지만 가능합니다"),
일치하는_관할서_정보가_없습니다("일치하는 관할서 정보가 없습니다"),
주소_검색이_성공적으로_응답되었습니다("주소 검색이 성공적으로 응답되었습니다");


private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.neighbors.tohero.application.baseResponse;

public enum BaseResponseStatus {
OK, BAD_REQUEST, JWT_ERROR;
OK, BAD_REQUEST, JWT_ERROR, NO_RESULT;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.neighbors.tohero.common.enums;

import lombok.Getter;

@Getter
public enum TargetJob {
POLICE_OFFICER, FIRE_FIGHTER
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.neighbors.tohero.common.exception.address;

import com.neighbors.tohero.application.baseResponse.BaseResponseStatus;

public class AddressException extends RuntimeException{
private final BaseResponseStatus status;
private final String message;

public AddressException(BaseResponseStatus status, String message) {
super(message);
this.status = status;
this.message = message;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.neighbors.tohero.domain.domain.address.model;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class Address {
private long addressId;
private String officeName;
private String roadAddress;
private String phoneNumber;
private String queryPath;

public static Address of(long addressId, String officeName, String roadAddress, String phoneNumber, String queryPath){
return new Address(addressId, officeName, roadAddress, phoneNumber, queryPath);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.neighbors.tohero.domain.domain.address.service;

import com.neighbors.tohero.common.annotaion.DomainService;
import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.domain.domain.address.model.Address;
import com.neighbors.tohero.domain.query.AddressRepository;
import lombok.RequiredArgsConstructor;

import java.util.List;

@DomainService
@RequiredArgsConstructor
public class GetAddress {

private final AddressRepository addressRepository;

public List<Address> searchAddressByOfficeName(String queryPath, TargetJob targetJob){
return addressRepository.searchAddressByOfficeName(queryPath, targetJob);
}

public List<Address> searchAddressByRoadAddress(String queryRoadAddress, TargetJob targetJob){
return addressRepository.searchAddressByRoadAddress(queryRoadAddress, targetJob);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.neighbors.tohero.domain.query;

import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.domain.domain.address.model.Address;

import java.util.List;

public interface AddressRepository {
List<Address> searchAddressByOfficeName(String queryPath, TargetJob targetJob);
List<Address> searchAddressByRoadAddress(String queryRoadAddress, TargetJob targetJob);
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package com.neighbors.tohero.infrastructure.entity;

import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.infrastructure.entity.base.BaseEntity;
import jakarta.persistence.*;
import lombok.Getter;

@Entity
@Table(name = "`Address`")
@Getter
public class AddressEntity extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -22,4 +25,8 @@ public class AddressEntity extends BaseEntity {

@Column(name = "query_path")
private String queryPath;

@Column(name = "target_job")
@Enumerated(value = EnumType.ORDINAL)
private TargetJob targetJob;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.neighbors.tohero.infrastructure.mapper;

import com.neighbors.tohero.domain.domain.address.model.Address;
import com.neighbors.tohero.infrastructure.entity.AddressEntity;
import org.springframework.stereotype.Component;

@Component
public class AddressMapper {
public Address toDomain(AddressEntity addressEntity) {
return Address.of(
addressEntity.getAddressId(),
addressEntity.getBranchOffice(),
addressEntity.getRoadAddress(),
addressEntity.getPhoneNumber(),
addressEntity.getQueryPath()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.neighbors.tohero.infrastructure.query.impl;

import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.domain.domain.address.model.Address;
import com.neighbors.tohero.domain.query.AddressRepository;
import com.neighbors.tohero.infrastructure.entity.AddressEntity;
import com.neighbors.tohero.infrastructure.mapper.AddressMapper;
import com.neighbors.tohero.infrastructure.repository.AddressEntityRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
@RequiredArgsConstructor
public class AddressRepositoryImpl implements AddressRepository {

private final AddressEntityRepository addressEntityRepository;
private final AddressMapper addressMapper;

@Override
public List<Address> searchAddressByOfficeName(String queryPath, TargetJob targetJob) {
List<AddressEntity> addressEntities = addressEntityRepository.findAllContainsOfficeName(queryPath,targetJob);

return addressEntities.stream()
.map(addressMapper::toDomain)
.toList();
}

@Override
public List<Address> searchAddressByRoadAddress(String queryRoadAddress, TargetJob targetJob) {
List<AddressEntity> addressEntities = addressEntityRepository.findAllContainsRoadAddress(queryRoadAddress,targetJob);

return addressEntities.stream()
.map(addressMapper::toDomain)
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.neighbors.tohero.infrastructure.repository;

import com.neighbors.tohero.common.enums.TargetJob;
import com.neighbors.tohero.infrastructure.entity.AddressEntity;
import io.lettuce.core.dynamic.annotation.Param;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;

public interface AddressEntityRepository extends JpaRepository<AddressEntity, Long> {

@Query("SELECT a FROM AddressEntity a WHERE a.queryPath LIKE %:queryPath% AND a.targetJob = :targetJob")
List<AddressEntity> findAllContainsOfficeName(
@Param("queryPath") String queryPath,
@Param("targetJob") TargetJob targetJob
);

@Query("SELECT a FROM AddressEntity a WHERE a.roadAddress LIKE %:queryRoadAddress% AND a.targetJob = :targetJob")
List<AddressEntity> findAllContainsRoadAddress(
@Param("queryRoadAddress") String queryRoadAddress,
@Param("targetJob") TargetJob targetJob
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.neighbors.tohero.presentation.controller;

import com.neighbors.tohero.application.address.dto.SearchAddressRequest;
import com.neighbors.tohero.application.address.service.AddressService;
import com.neighbors.tohero.application.baseResponse.BaseResponse;
import lombok.RequiredArgsConstructor;
import org.springdoc.core.annotations.ParameterObject;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("")
public class AddressController {

private final AddressService addressService;

@GetMapping("/address")
public ResponseEntity<BaseResponse> searchAddress(@ParameterObject @Validated SearchAddressRequest searchAddressRequest) {
return ResponseEntity.ok()
.body(addressService.searchAddress(searchAddressRequest));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,18 @@
import java.util.Map;

import static com.neighbors.tohero.application.baseResponse.BaseResponseMessage.유저_이름의_길이는_1부터_5까지만_가능합니다;
import static com.neighbors.tohero.application.baseResponse.BaseResponseMessage.주소_검색_쿼리의_길이는_1부터_50까지만_가능합니다;

@Priority(0)
@RestControllerAdvice
public class UserExceptionControllerAdvice {
public class DTOFieldExceptionControllerAdvice {

private final Map<String, BaseResponseMessage> fieldErrorMapper;

public UserExceptionControllerAdvice() {
public DTOFieldExceptionControllerAdvice() {
fieldErrorMapper = new HashMap<>();
fieldErrorMapper.put("nickname", 유저_이름의_길이는_1부터_5까지만_가능합니다);
fieldErrorMapper.put("searchAddress", 주소_검색_쿼리의_길이는_1부터_50까지만_가능합니다);
}

@ResponseStatus(HttpStatus.BAD_REQUEST)
Expand Down
Loading