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 @@ -9,13 +9,15 @@
import io.swagger.v3.oas.annotations.tags.Tag;
import ita.tinybite.domain.auth.entity.JwtTokenProvider;
import ita.tinybite.domain.party.dto.request.PartyCreateRequest;
import ita.tinybite.domain.party.dto.request.PartyListRequest;
import ita.tinybite.domain.party.dto.request.PartyQueryListResponse;
import ita.tinybite.domain.party.dto.request.PartyUpdateRequest;
import ita.tinybite.domain.party.dto.response.ChatRoomResponse;
import ita.tinybite.domain.party.dto.response.PartyDetailResponse;
import ita.tinybite.domain.party.dto.response.PartyListResponse;
import ita.tinybite.domain.party.entity.PartyParticipant;
import ita.tinybite.domain.party.enums.PartyCategory;
import ita.tinybite.domain.party.enums.PartySortType;
import ita.tinybite.domain.party.service.PartySearchService;
import ita.tinybite.domain.party.service.PartyService;
import ita.tinybite.global.response.APIResponse;
Expand Down Expand Up @@ -275,10 +277,42 @@ public ResponseEntity<PartyListResponse> getPartyList(
example = "ALL",
schema = @Schema(allowableValues = {"ALL", "DELIVERY", "GROCERY", "HOUSEHOLD"})
)
@RequestParam(defaultValue = "ALL") PartyCategory category
) {
PartyListResponse response = partyService.getPartyList(
userId, category);
@RequestParam(defaultValue = "ALL") PartyCategory category,
@RequestParam(required = false, defaultValue = "LATEST") PartySortType sortType,
@RequestParam(required = false) String userLat,
@RequestParam(required = false) String userLon
) {
Double lat = null;
Double lon = null;
// 거리순 정렬 시 위치 정보 검증
if (sortType == PartySortType.DISTANCE) {
if (userLat == null || userLon == null) {
throw new IllegalArgumentException("거리순 정렬을 위해서는 현재 위치 정보가 필요합니다.");
}

try {
lat = Double.parseDouble(userLat);
lon = Double.parseDouble(userLon);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(
String.format("위치 정보 형식이 올바르지 않습니다. userLat: %s, userLon: %s", userLat, userLon)
);
}

// 위도/경도 범위 검증
if (lat < -90 || lat > 90 || lon < -180 || lon > 180) {
throw new IllegalArgumentException("위도/경도 값이 유효한 범위를 벗어났습니다.");
}
}

PartyListRequest request = PartyListRequest.builder()
.category(category)
.sortType(sortType)
.userLat(lat)
.userLon(lon)
.build();

PartyListResponse response = partyService.getPartyList(userId, request);

return ResponseEntity.ok(response);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ita.tinybite.domain.party.dto.request;

import ita.tinybite.domain.party.enums.PartyCategory;
import ita.tinybite.domain.party.enums.PartySortType;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class PartyListRequest {
private PartyCategory category; // 필터: 카테고리
private PartySortType sortType; // 정렬: 최신순/거리순

// 거리순 정렬을 위한 현재 위치 (선택)
private Double userLat;
private Double userLon;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import ita.tinybite.domain.party.enums.ParticipantStatus;
import ita.tinybite.domain.party.enums.PartyCategory;
import ita.tinybite.domain.party.enums.PartyStatus;
import ita.tinybite.global.util.DistanceCalculator;
import lombok.*;

import java.time.Duration;
Expand All @@ -20,7 +21,7 @@ public class PartyCardResponse {
private Integer pricePerPerson; // 1/N 가격
private String participantStatus; // "1/4명"
private String distance; // "300m" or "1.2km" (화면 표시용)
private Double distanceKm; // km 단위 거리 (정렬용)
private String distanceKm; // km 단위 거리 (정렬용)
private String timeAgo; // "10분 전", "3시간 전"
private Boolean isClosed; // 마감 여부
private PartyCategory category;
Expand Down Expand Up @@ -109,4 +110,9 @@ private static Boolean checkIfClosed(Party party, int currentParticipants) {

return false;
}

public void addDistanceKm(Double distance) {
this.distanceKm = DistanceCalculator.formatDistance(distance);
this.distance = Double.toString(distance);
}
}
15 changes: 15 additions & 0 deletions src/main/java/ita/tinybite/domain/party/enums/PartySortType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package ita.tinybite.domain.party.enums;

import lombok.Getter;

@Getter
public enum PartySortType {
LATEST("최신순"),
DISTANCE("거리순");

private final String description;

PartySortType(String description) {
this.description = description;
}
}
136 changes: 70 additions & 66 deletions src/main/java/ita/tinybite/domain/party/service/PartyService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,31 @@
import ita.tinybite.domain.chat.enums.ChatRoomType;
import ita.tinybite.domain.chat.repository.ChatRoomRepository;
import ita.tinybite.domain.party.dto.request.PartyCreateRequest;
import ita.tinybite.domain.party.dto.request.PartyQueryListResponse;
import ita.tinybite.domain.party.dto.request.PartyListRequest;
import ita.tinybite.domain.party.dto.request.PartyUpdateRequest;
import ita.tinybite.domain.party.dto.response.*;
import ita.tinybite.domain.party.entity.Party;
import ita.tinybite.domain.party.entity.PartyParticipant;
import ita.tinybite.domain.party.entity.PickupLocation;
import ita.tinybite.domain.party.enums.ParticipantStatus;
import ita.tinybite.domain.party.enums.PartyCategory;
import ita.tinybite.domain.party.enums.PartySortType;
import ita.tinybite.domain.party.enums.PartyStatus;
import ita.tinybite.domain.party.repository.PartyParticipantRepository;
import ita.tinybite.domain.party.repository.PartyRepository;
import ita.tinybite.domain.user.entity.User;
import ita.tinybite.domain.user.repository.UserRepository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

import ita.tinybite.global.location.LocationService;
import ita.tinybite.global.util.DistanceCalculator;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;


@Service
@RequiredArgsConstructor
Expand All @@ -42,6 +40,7 @@ public class PartyService {
private final PartyParticipantRepository partyParticipantRepository;
private final ChatRoomRepository chatRoomRepository;
private final PartyParticipantRepository participantRepository;

/**
* 파티 생성
*/
Expand Down Expand Up @@ -105,72 +104,43 @@ public Long createParty(Long userId, PartyCreateRequest request) {
/**
* 파티 목록 조회 (홈 화면)
*/
public PartyListResponse getPartyList(Long userId, PartyCategory category) {
public PartyListResponse getPartyList(Long userId, PartyListRequest request) {
User user = null;
if (userId != null) {
user = userRepository.findById(userId).orElse(null);
}

// 동네 기준으로 파티 조회
List<Party> parties = List.of();
if (user != null && user.getLocation() != null) {
if (category == PartyCategory.ALL) {
parties = partyRepository.findByPickupLocation_Place(user.getLocation());
} else {
parties = partyRepository.findByPickupLocation_PlaceAndCategory(
user.getLocation(), category);
}
}
// else {
// // 비회원이거나 동네 미설정 시
// String location = locationService.getLocation(userLat, userLon);
// if (category == PartyCategory.ALL) {
// parties = partyRepository.findByPickupLocation_Place(location);
// } else {
// parties = partyRepository.findByPickupLocation_PlaceAndCategory(
// location, category);
// }
// }

// List<PartyCardResponse> cardResponses = parties.stream()
// .map(party -> {
// // DistanceCalculator 활용
// double distance = DistanceCalculator.calculateDistance(
// Double.parseDouble(userLat), Double.parseDouble(userLon),
// party.getLatitude(), party.getLongitude()
// );
// return convertToCardResponse(party, distance, userId, party.getCreatedAt());
// })
// .collect(Collectors.toList());
//
// // 진행 중 파티: 거리 가까운 순 정렬
// List<PartyCardResponse> activeParties = cardResponses.stream()
// .filter(p -> !p.getIsClosed())
// .sorted((a, b) -> Double.compare(a.getDistanceKm(), b.getDistanceKm()))
// .collect(Collectors.toList());
//
// // 마감된 파티: 거리 가까운 순 정렬
// List<PartyCardResponse> closedParties = cardResponses.stream()
// .filter(PartyCardResponse::getIsClosed)
// .sorted((a, b) -> Double.compare(a.getDistanceKm(), b.getDistanceKm()))
// .collect(Collectors.toList());

List<Party> parties = fetchPartiesByLocation(user, request);

// PartyCardResponse로 변환
List<PartyCardResponse> cardResponses = parties.stream()
.map(party -> convertToCardResponse(party, userId, party.getCreatedAt()))
.map(party -> {
// 거리순 정렬인 경우 거리 계산
if (request.getSortType() == PartySortType.DISTANCE) {
double distance = DistanceCalculator.calculateDistance(
request.getUserLat(),
request.getUserLon(),
party.getPickupLocation().getPickupLatitude(),
party.getPickupLocation().getPickupLongitude()
);
return convertToCardResponseWithDistance(party, distance);
}
return convertToCardResponse(party,party.getCreatedAt());
})
.toList();

// 진행 중 파티 정렬
List<PartyCardResponse> activeParties = cardResponses.stream()
.filter(p -> !p.getIsClosed())
.sorted(getComparator(request.getSortType()))
.collect(Collectors.toList());

// 진행 중 파티: 최신순 정렬 (createdAt 기준 내림차순)
List<PartyCardResponse> activeParties = cardResponses.stream()
.filter(p -> !p.getIsClosed())
.sorted((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt()))
.collect(Collectors.toList());

// 마감된 파티: 최신순 정렬 (createdAt 기준 내림차순)
List<PartyCardResponse> closedParties = cardResponses.stream()
.filter(PartyCardResponse::getIsClosed)
.sorted((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt()))
.collect(Collectors.toList());
// 마감된 파티 정렬
List<PartyCardResponse> closedParties = cardResponses.stream()
.filter(PartyCardResponse::getIsClosed)
.sorted(getComparator(request.getSortType()))
.collect(Collectors.toList());

return PartyListResponse.builder()
.activeParties(activeParties)
Expand Down Expand Up @@ -272,7 +242,7 @@ private String getDefaultImageIfEmpty(List<String> images, PartyCategory categor
};
}

private PartyCardResponse convertToCardResponse(Party party, Long userId,
private PartyCardResponse convertToCardResponse(Party party,
LocalDateTime createdAt) {
int pricePerPerson = party.getPrice() / party.getMaxParticipants();
String participantStatus = party.getCurrentParticipants() + "/"
Expand Down Expand Up @@ -675,6 +645,40 @@ private String formatDistanceIfExists(Double distance) {
return distance!= null? DistanceCalculator.formatDistance(distance):null;
}

//카테고리에 따라 파티 조회
private List<Party> fetchPartiesByLocation(User user, PartyListRequest request) {
if (user == null || user.getLocation() == null) {
return List.of();
}

String location = user.getLocation();
PartyCategory category = request.getCategory();

if (category == PartyCategory.ALL) {
return partyRepository.findByPickupLocation_Place(location);
} else {
return partyRepository.findByPickupLocation_PlaceAndCategory(location, category);
}
}

// 정렬 기준에 따른 Comparator 반환
private Comparator<PartyCardResponse> getComparator(PartySortType sortType) {
if (sortType == PartySortType.DISTANCE) {
// 거리 가까운 순
return Comparator.comparing(PartyCardResponse::getDistanceKm)
.thenComparing((a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt()));
} else {
// 최신순 (createdAt 내림차순)
return (a, b) -> b.getCreatedAt().compareTo(a.getCreatedAt());
}
}

// 거리 정보 포함 변환
private PartyCardResponse convertToCardResponseWithDistance(
Party party, Double distance) {
PartyCardResponse response = convertToCardResponse(party, party.getCreatedAt());
response.addDistanceKm(distance);
return response;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ public static double calculateDistanceInMeters(double lat1, double lon1,
public static String formatDistance(double distanceKm) {
if (distanceKm < 1.0) {
// 1km 미만: 미터 단위로 표시 (반올림)
return Math.round(distanceKm * 1000) + "m";
return String.format("%.1fkm", distanceKm);
} else {
// 1km 이상: km 단위로 표시 (소수점 1자리)
return String.format("%.1fkm", distanceKm);
Expand Down