6.배달플랫폼 - Store

ys·2024년 3월 5일

배달플랫폼

목록 보기
7/8
  • 이제 사용자 기능까지 어느정도 끝났기 때문에,
  • 매뉴를 주문할 가게에 대해서 만들어 보겠다

StoreEntity

  • 가게는 기본적으로 이름, 주소, 상태(enum), 카테고리(enum)
  • 좋아요수, 가게 섬네일 주소, 최소 금액, 최수 주문 금액, 그리고 가게 번호 이렇게 필드를 지정하였다
@Entity
@Table(name = "store")
@NoArgsConstructor
@AllArgsConstructor
@SuperBuilder
@Getter
@EqualsAndHashCode(callSuper = true)
public class StoreEntity extends BaseEntity {

    @Column(length = 100,nullable = false)
    private String name;

    @Column(length = 150,nullable = false)
    private String address;

    @Column(length = 50,nullable = false)
    @Enumerated(EnumType.STRING)
    private StoreStatus status;

    @Column(length = 50,nullable = false)
    @Enumerated(EnumType.STRING)
    private StoreCategory category;

    private double star;

    @Column(length = 200, nullable = false)
    private String thumbnailUrl;

    @Column(precision = 11,scale = 4,nullable = false)
    private BigDecimal minimumAmount;

    @Column(precision = 11,scale = 4,nullable = false)
    private BigDecimal minimumDeliveryAmount;

    @Column(length = 20)
    private String phoneNumber;

StoreStatus

@AllArgsConstructor
public enum StoreStatus {
    REGISTERED("등록"),
    UNREGISTERED("헤지");

    private String description;
}

StoreCategory

@AllArgsConstructor
public enum StoreCategory {
    // 중식
    CHINESE_FOOD("중식","중식"),
    // 양식
    WESTERN_FOOD("양식","양식"),
    // 한식
    KOREAN_FOOD("한식","한식"),
    //일식
    JAPANESE_FOOD("일식","일식"),
    // 치킨
    CHICKEN("치킨","치킨"),
    // 피자
    PIZZA("피자","피자"),
    // 햄버거
    HAMBURGER("함버거","햄버거"),
    // 커피
    COFFEE_TEA("커피&차","커피&차");

    private String display;
    private String description;
}```

## StoreRepository
```java
@Repository
public interface StoreRepository extends JpaRepository<StoreEntity,Long> {

    // 특정 유효한 스토어
    // select * from Store where id =? and status = ? order by id desc limit 1
    Optional<StoreEntity> findFirstByIdAndStatusOrderByIdDesc(Long id, StoreStatus status);

    // 유효한 스토어 리스트
    // select * from Store where status =? order by id desc
    List<StoreEntity> findAllByStatusOrderByIdDesc(StoreStatus status);

    // 유효한 특정 카테고리 스토어 리스트
    List<StoreEntity> findAllByStatusAndCategoryOrderByStarDesc(StoreStatus status, StoreCategory storeCategory);
}
  • 등록이 되어있고, 가게 아이디를 통해 찾는 기능과
  • 상태가 등록으로 되어있으면 모두 찾는 기능
  • 등록으로 되어있고, 특정 카테고리를 입력하면 모두 받는 기능 이렇게 추가적으로 만들어 줬다

  • 이제 화면을 나타내는 api 모듈로 이동하자
  • domian 패키지에 store 패키지를 만들고
  • 컨트롤러, 서비스, 컨버터, 비지니스를 만들어준다!

StroeService

@Service
@RequiredArgsConstructor
public class StoreService {

    private final StoreRepository storeRepository;

    // 유효한 스토어 가져오기
    public StoreEntity getStoreWithThrow(Long id){
        return storeRepository.findFirstByIdAndStatusOrderByIdDesc(id, StoreStatus.REGISTERED)
                .orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));
    }
    // 스토어 등록
    @Transactional
    public StoreEntity register(StoreEntity storeEntity){
        return Optional.ofNullable(storeEntity)
                .map(it -> {
                    it.starAndStatus(0,StoreStatus.REGISTERED);
                    return storeRepository.save(it);
                })
                .orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));

    }

    // 카테고리로 스토어 검색
    public List<StoreEntity> searchByCategory(StoreCategory storeCategory){
        List<StoreEntity> list = storeRepository.findAllByStatusAndCategoryOrderByStarDesc(
                StoreStatus.REGISTERED,
                storeCategory
        );
        return list;
    }

    // 전체 스토어
    public List<StoreEntity> registerStore(){
        List<StoreEntity> list = storeRepository.findAllByStatusOrderByIdDesc(StoreStatus.REGISTERED);
        return list;
    }

    public Optional<StoreEntity> findById(Long id){
        return storeRepository.findById(id);
    }
}
  • 순수 store에 대한 서비스를 알아보자
  • 가게를 등록하는 register 메서드이다
    • entity에 setter을 지양하기 위해서,
    • star에 0을 넣고, 상태를 REGISTERD로 지정해주는 starAndStatus라는 메서드를
    • 엔티티 레벨에 만들어주었다
public void starAndStatus(double star,StoreStatus status){
        this.star = star;
        this.status=status;
}
  • 먼저 아까 만든 레파지토리를 이용해, id와 REGISTERED상태인 StoreEntity를 반환하는 getStoreWithThorw
  • 카테고리를 이용해서 동륵된 리시트를 찾는 searchByCategory
  • 등록된 가게들을 모두 가져오는 regisgerStore
  • 가게 id를 이용해 가게를 찾는 findbyId를 만들어 주었다

StoreRegisterRequest

@Data
@AllArgsConstructor
@NoArgsConstructor
public class StoreRegisterRequest {

    @NotBlank
    private String name;
    @NotBlank // "" , " " , null 모두 안됨
    private String address;
    @NotNull
    private StoreCategory storeCategory;
    @NotBlank
    private String thumbnailUrl;
    @NotNull
    private BigDecimal minimumAmount;
    @NotNull
    private BigDecimal minimumDeliveryAmount;
    @NotBlank
    private String phoneNumber;
}
  • 등록할때는, 다음 요청에 필요한 데이터를 받고

StoreResponse

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class StoreResponse {
    private Long id;
    private String name;
    private String address;
    private StoreStatus status;
    private StoreCategory category;
    private double star;
    private String thumbnailUrl;
    private BigDecimal minimumAmount;
    private BigDecimal minimumDeliveryAmount;
    private String phoneNumber;

}
  • 응답할때에는, 다음 reponse dto를 사용한다

StoreConverter

  • 요청 데이터와, 응답 데이터의 형식은 정해졌고!
  • 이제 repository에서 저장할려면, registgerRequest -> StoreEntity형식으로 바꾸고
  • 응답 데이터 형식으로 entity -> storeResponse형식으로 바꾸는 메서드 두개를 만들어준다
  • builder()패턴을 이용한다
@Converter
public class StoreConverter {

    public StoreEntity toEntity(StoreRegisterRequest request){


        return Optional.ofNullable(request)
                .map(it -> {
                    return StoreEntity.builder()
                            .name(request.getName())
                            .address(request.getAddress())
                            .category(request.getStoreCategory())
                            .minimumAmount(request.getMinimumAmount())
                            .minimumDeliveryAmount(request.getMinimumDeliveryAmount())
                            .thumbnailUrl(request.getThumbnailUrl())
                            .phoneNumber(request.getPhoneNumber())
                            .build();
                }).orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));

    }
    public StoreResponse toResponse(StoreEntity entity){


        return Optional.ofNullable(entity)
                .map(it -> {
                    return StoreResponse.builder()
                            .id(entity.getId())
                            .name(entity.getName())
                            .status(entity.getStatus())
                            .address(entity.getAddress())
                            .category(entity.getCategory())
                            .minimumAmount(entity.getMinimumAmount())
                            .minimumDeliveryAmount(entity.getMinimumDeliveryAmount())
                            .thumbnailUrl(entity.getThumbnailUrl())
                            .phoneNumber(entity.getPhoneNumber())
                            .star(entity.getStar())
                            .build();

                }).orElseThrow(() -> new ApiException(ErrorCode.NULL_POINT));

    }
}

StoreBusiness

@Business
@RequiredArgsConstructor
public class StoreBusiness {
    private final StoreService storeService;
    private final StoreConverter storeConverter;
    public StoreResponse register(StoreRegisterRequest storeRegisterRequest){
        // request -> entity -> response
        StoreEntity entity = storeConverter.toEntity(storeRegisterRequest);
        StoreEntity newEntity = storeService.register(entity);
        StoreResponse response = storeConverter.toResponse(newEntity);
        return response;
    }

    public List<StoreResponse> searchCategory(StoreCategory category){
        // entity list -> response List
        List<StoreEntity> storeEntities = storeService.searchByCategory(category);
        List<StoreResponse> storeResponses = storeEntities.stream()
                .map(storeEntity -> {
                    return storeConverter.toResponse(storeEntity);
                })
                .collect(Collectors.toList());

        return storeResponses;
    }

}
  • 비지니스 영역에는, store 등록하고 response형태로 보내는 로직과
  • category를 넣으면, 그 cateogry에 맞는 가게들을 모두 내보내는 메서드
  • 총 2개를 만들었다!

StoreOpenApiContorller

  • 먼저 로그인 인터셉터를 거치지 않는 openApiController에서는
  • Api<StoreResponse>형태로 성공으로 반환을 해줘야한다
  • Api<StoreRegisterRequest>형태로 요청이 들어오고, 여기서 body를 꺼낸 후, 등록을 하고
  • Api<StoreResponse>형태로 Api.OK반환을 해준다
@RequiredArgsConstructor
@RestController
@RequestMapping("/open-api/store")
public class StoreOpenApiController {

    private final StoreBusiness storeBusiness;

    @PostMapping("/register")
    public Api<StoreResponse> register(@Valid @RequestBody Api<StoreRegisterRequest> request){
        StoreResponse register = storeBusiness.register(request.getBody());
        return Api.OK(register);
    }

}

StoreApiController

  • api로그인이 끝나고, token인증을 받은 페이지이다
  • 비지니스 로직에서 searchCategory를 이용해
  • 카테고리를 입력하면, 결과릴 리스트로 만든 후에, API요청 스펙에 맞춰서 내려준다
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/store")
public class StoreApiController {

    private final StoreBusiness storeBusiness;

    @GetMapping("/search")
    public Api<List<StoreResponse>> search(@RequestParam(required = false,value = "storeCategory") StoreCategory storeCategory){
        List<StoreResponse> response = storeBusiness.searchCategory(storeCategory);
        return Api.OK(response);
    }
}

실행

  • 다음과 같이 가게를 등록하고
  • 원하는 형태의 api형식의 응답 메시지가 나간 것을 볼 수 있다
  • 이제 원하는데로, 카테고리를 입력해 데이터를 받으려면
  • 다음과 같이 요청하면
{
  "result": {
    "result_code": 200,
    "result_message": "성공",
    "result_description": "성공"
  },
  "body": [
    {
      "id": 1,
      "name": "starbucks_강남점",
      "address": "서울시 강남구",
      "status": "REGISTERED",
      "category": "COFFEE_TEA",
      "star": 0,
      "thumbnail_url": "https://i.namu.wiki/i/95ZuIdMicr_HrM1pbIcFD3DLfvKxuyhjf3xKsHT5tlddl2uoSm_L2gp0sBL6iO7meZJqb9KMcQUiFEpnFQZ0G-GKIUkyLHOe1LppL3z34p9SURyQCg4LLSYQFmJRVKIIAR_D0wLqmkgl6VBvwanK-w.webp",
      "minimum_amount": 8000,
      "minimum_delivery_amount": 3000,
      "phone_number": "010-1234-5678"
    },
    {
      "id": 2,
      "name": "스타벅스_강남",
      "address": "서울 강남",
      "status": "REGISTERED",
      "category": "COFFEE_TEA",
      "star": 0,
      "thumbnail_url": "https://i.namu.wiki/i/95ZuIdMicr_HrM1pbIcFD3DLfvKxuyhjf3xKsHT5tlddl2uoSm_L2gp0sBL6iO7meZJqb9KMcQUiFEpnFQZ0G-GKIUkyLHOe1LppL3z34p9SURyQCg4LLSYQFmJRVKIIAR_D0wLqmkgl6VBvwanK-w.webp",
      "minimum_amount": 8000,
      "minimum_delivery_amount": 3000,
      "phone_number": "010-1234-5678"
    },
    {
      "id": 4,
      "name": "별다방",
      "address": "제주도",
      "status": "REGISTERED",
      "category": "COFFEE_TEA",
      "star": 0,
      "thumbnail_url": "string",
      "minimum_amount": 4500,
      "minimum_delivery_amount": 3500,
      "phone_number": "010-8888-8888"
    }
  ]
}
  • 우리가 원하는 형식으로 데이터가 들어오고
  • id 4번에 우리가 방금 추가한 별다방이 함께 들어오는 것을 알 수 있다
profile
개발 공부,정리

0개의 댓글