Skip to content

Commit

Permalink
Merge pull request #215 from ttoklip/Feat/28-소통해요-함께공구해요-시-구-동으로-구분
Browse files Browse the repository at this point in the history
feat: 소통해요 페이징 조회시 같은 지역으로 필터링. 시, 구, 동으로 구분 (#214)
  • Loading branch information
toychip authored Sep 22, 2024
2 parents bed4592 + 846721d commit cddd9d5
Show file tree
Hide file tree
Showing 7 changed files with 124 additions and 21 deletions.
28 changes: 28 additions & 0 deletions src/main/java/com/api/ttoklip/domain/town/TownCriteria.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.api.ttoklip.domain.town;

import com.api.ttoklip.global.exception.ApiException;
import com.api.ttoklip.global.exception.ErrorType;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequiredArgsConstructor
public enum TownCriteria {

CITY("시"),
DISTRICT("구"),
TOWN("동"),

;

private final String Criteria;

public static TownCriteria findTownCriteriaByValue(final String value) {
try {
return TownCriteria.valueOf(value.trim().toUpperCase());
} catch (IllegalArgumentException e) {
log.error("Invalid value for TownCriteria: {}", value);
throw new ApiException(ErrorType.INVALID_SORT_TYPE);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.api.ttoklip.domain.town.community.post.repository;

import com.api.ttoklip.domain.town.TownCriteria;
import com.api.ttoklip.domain.town.community.comment.CommunityComment;
import com.api.ttoklip.domain.town.community.post.entity.Community;
import java.util.List;
Expand All @@ -16,5 +17,5 @@ public interface CommunityRepositoryCustom {

List<Community> getRecent3();

Page<Community> getPaging(Pageable pageable);
Page<Community> getPaging(final TownCriteria townCriteria, final Pageable pageable);
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.api.ttoklip.domain.town.community.post.repository;

import static com.api.ttoklip.domain.member.domain.QMember.member;
import static com.api.ttoklip.domain.privacy.domain.QProfile.profile;
import static com.api.ttoklip.domain.town.community.comment.QCommunityComment.communityComment;
import static com.api.ttoklip.domain.town.community.image.entity.QCommunityImage.communityImage;
import static com.api.ttoklip.domain.town.community.post.entity.QCommunity.community;
import static com.api.ttoklip.global.util.SecurityUtil.getCurrentMember;

import com.api.ttoklip.domain.privacy.domain.QProfile;
import com.api.ttoklip.domain.town.TownCriteria;
import com.api.ttoklip.domain.town.community.comment.CommunityComment;
import com.api.ttoklip.domain.town.community.post.entity.Community;
import com.api.ttoklip.global.exception.ApiException;
Expand All @@ -24,6 +26,8 @@
@RequiredArgsConstructor
public class CommunityRepositoryImpl implements CommunityRepositoryCustom {

private static final String SEOUL = "서울특별시";
private static final String SPLIT_CRITERIA = " ";
private final JPAQueryFactory jpaQueryFactory;

@Override
Expand Down Expand Up @@ -100,37 +104,96 @@ private BooleanExpression matchCommunityId(final Long communityId) {
return communityComment.community.id.eq(communityId);
}


// Community 페이징 처리 쿼리 추가
@Override
public Page<Community> getPaging(final Pageable pageable) {
List<Community> pageContent = getPageContent(pageable);
Long count = countQuery();
public Page<Community> getPaging(final TownCriteria townCriteria, final Pageable pageable) {
List<Community> pageContent = getPageContent(townCriteria, pageable);
Long count = countQuery(townCriteria);
return new PageImpl<>(pageContent, pageable, count);
}

private List<Community> getPageContent(final Pageable pageable) {
private List<Community> getPageContent(final TownCriteria townCriteria, final Pageable pageable) {
String writerStreet = getCurrentMember().getStreet();
if (!writerStreet.startsWith(SEOUL)) {
throw new ApiException(ErrorType.INVALID_STREET_TYPE);
}

return jpaQueryFactory
.selectFrom(community)
.where(
getCommunityActivate()
getCommunityActivate(),
getLocationFilterByTownCriteria(townCriteria, writerStreet)
)
.leftJoin(community.member, member).fetchJoin()
.leftJoin(community.member.profile, QProfile.profile).fetchJoin()
.leftJoin(community.member.profile, profile).fetchJoin()
.limit(pageable.getPageSize())
.offset(pageable.getOffset())
.orderBy(community.id.desc())
.fetch();
}

private Long countQuery() {
private Long countQuery(final TownCriteria townCriteria) {
String writerStreet = getCurrentMember().getStreet();
return jpaQueryFactory
.select(Wildcard.count)
.from(community)
.where(
getCommunityActivate()
getCommunityActivate(),
getLocationFilterByTownCriteria(townCriteria, writerStreet)
)
.fetchOne();
}

private BooleanExpression getLocationFilterByTownCriteria(final TownCriteria townCriteria, final String street) {
String[] streetParts = splitStreet(street); // 공통 메서드로 분리

if (townCriteria.equals(TownCriteria.CITY)) {
return filterByCity(streetParts);
}

if (townCriteria.equals(TownCriteria.DISTRICT)) {
return filterByDistrict(streetParts);
}

if (townCriteria.equals(TownCriteria.TOWN)) {
return filterByTown(streetParts);
}

throw new ApiException(ErrorType.INTERNAL_STREET_TYPE);
}

// 주소를 공백으로 분리하는 로직을 하나의 메서드로 추출
private String[] splitStreet(final String street) {
return street.split(SPLIT_CRITERIA);
}

// '시' 부분만 추출해서 필터링 (예: '서울특별시'로 시작하는 모든 주소)
private BooleanExpression filterByCity(final String[] streetParts) {
if (streetParts.length > 0) {
String city = streetParts[0];
return community.member.street.startsWith(city);
}
return null;
}

// '시'와 '구'가 모두 일치해야 함 (예: '서울특별시 서대문구')
private BooleanExpression filterByDistrict(final String[] streetParts) {
if (streetParts.length > 1) {
String city = streetParts[0];
String district = streetParts[1];
return community.member.street.startsWith(city + SPLIT_CRITERIA + district);
}
return null;
}

// '시', '구', '동'이 모두 일치해야 함 (예: '서울특별시 서대문구 북가좌동')
private BooleanExpression filterByTown(final String[] streetParts) {
if (streetParts.length > 2) {
String city = streetParts[0];
String district = streetParts[1];
String town = streetParts[2];
return community.member.street.startsWith(city + SPLIT_CRITERIA + district + SPLIT_CRITERIA + town);
}
return null;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.api.ttoklip.domain.common.report.dto.ReportCreateRequest;
import com.api.ttoklip.domain.common.report.service.ReportService;
import com.api.ttoklip.domain.member.domain.Member;
import com.api.ttoklip.domain.town.TownCriteria;
import com.api.ttoklip.domain.town.community.comment.CommunityComment;
import com.api.ttoklip.domain.town.community.image.service.CommunityImageService;
import com.api.ttoklip.domain.town.community.like.service.CommunityLikeService;
Expand Down Expand Up @@ -202,8 +203,8 @@ public List<CommunityRecent3Response> getRecent3() {


/* -------------------------------------------- Community 페이징 -------------------------------------------- */
public Page<Community> getPaging(final Pageable pageable) {
return communityRepository.getPaging(pageable);
public Page<Community> getPaging(final TownCriteria townCriteria, final Pageable pageable) {
return communityRepository.getPaging(townCriteria, pageable);
}
/* -------------------------------------------- Community 끝 -------------------------------------------- */
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ public SuccessResponse<CartMainResponse> getCarts() {
)))})
@GetMapping("/community")
public SuccessResponse<CommunityPaging> getCommunities(
@Parameter(description = "페이지 번호 (기본값 CITY)", example = "CITY, DISTRICT, TOWN")
@RequestParam(required = false, defaultValue = "CITY") final String criteria,
@Parameter(description = "페이지 번호 (0부터 시작, 기본값 0)", example = "0")
@RequestParam(required = false, defaultValue = "0") final int page) {
@RequestParam(required = false, defaultValue = "0") final int page
) {
Pageable pageable = PageRequest.of(page, PAGE_SIZE);
return new SuccessResponse<>(townMainService.getCommunities(pageable));
return new SuccessResponse<>(townMainService.getCommunities(criteria, pageable));
}

/* Cart Paging */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.api.ttoklip.domain.search.response.CartSearchPaging;
import com.api.ttoklip.domain.search.response.CommunityPaging;
import com.api.ttoklip.domain.search.response.CommunitySingleResponse;
import com.api.ttoklip.domain.town.TownCriteria;
import com.api.ttoklip.domain.town.cart.post.entity.Cart;
import com.api.ttoklip.domain.town.cart.post.repository.CartSearchRepository;
import com.api.ttoklip.domain.town.cart.post.service.CartPostService;
Expand All @@ -27,14 +28,13 @@ public class TownMainService {
private final CommunityPostService communityPostService;
private final CartPostService cartPostService;

public CommunityPaging getCommunities(final Pageable pageable) {
public CommunityPaging getCommunities(final String criteria, final Pageable pageable) {
TownCriteria townCriteria = validCriteria(criteria);

Page<Community> contentPaging = communityPostService.getPaging(pageable);
Page<Community> contentPaging = communityPostService.getPaging(townCriteria, pageable);

// List<Entity>
List<Community> contents = contentPaging.getContent();

// Entity -> SingleResponse 반복
List<CommunitySingleResponse> communitySingleData = contents.stream()
.map(CommunitySingleResponse::communityFrom)
.toList();
Expand All @@ -49,6 +49,10 @@ public CommunityPaging getCommunities(final Pageable pageable) {

}

private TownCriteria validCriteria(final String criteria) {
return TownCriteria.findTownCriteriaByValue(criteria);
}

public CartSearchPaging getCarts(final Pageable pageable,
final Long startMoney,
final Long lastMoney,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ public enum ErrorType {

// ------------------------------------------ Community ------------------------------------------
COMMUNITY_NOT_FOUND(NOT_FOUND, "Community_4040", "소통해요를 찾을 수 없습니다."),
INTERNAL_STREET_TYPE(INTERNAL_SERVER_ERROR, "STREET_5001", "TownCriteria에서 이미 필터링했지만, repository에서 잘못된 값을 받았습니다."),
INVALID_STREET_TYPE(BAD_REQUEST, "STREET_4001", "서울특별시 외의 지역은 아직 개발중입니다."),


// ------------------------------------------ Comment ------------------------------------------
Expand Down Expand Up @@ -185,7 +187,8 @@ public enum ErrorType {
INVALID_EMAIL_KEY_TYPE(INTERNAL_SERVER_ERROR, "AOP_5002", "분산락에 적용할 Unique Email 이 null"),
DUPLICATED_CREATE_BOARD_REQUEST(BAD_REQUEST, "DUPLICATED_4002", "중복된 게시글 작성입니다."),

INVALID_HASH_LENGTH_TYPE(INTERNAL_SERVER_ERROR, "HASH_5001", "잘못된 Hash 길이 요청");
INVALID_HASH_LENGTH_TYPE(INTERNAL_SERVER_ERROR, "HASH_5001", "잘못된 Hash 길이 요청"),
;

private final HttpStatus status;
private final String errorCode;
Expand Down

0 comments on commit cddd9d5

Please sign in to comment.