Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
bce7dbc
feat : 닉네임 중복 체크 (nickname unique), 인증코드 검사 예외처리 추가
yyytir777 Dec 8, 2025
3573613
fix : user_id IDENTITY strategy & dev redis host 이름변경 (localhost -> r…
yyytir777 Dec 8, 2025
08cfb63
test코드 생성 & swagger url 삭제 & 환경변수 중복 삭제
yyytir777 Dec 8, 2025
ac96fa0
fix : 엔드포인트 추가
yyytir777 Dec 9, 2025
43e75ed
Merge pull request #27 from tinybite-2025/feature/13-user
yyytir777 Dec 9, 2025
a70941b
Feature/26 notification (#29)
marshmallowing Dec 11, 2025
cbbf597
feat : google login 구현 완료
yyytir777 Dec 11, 2025
d03fd30
fix : main push 시에만 workflow trigger
yyytir777 Dec 11, 2025
00cc289
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
yyytir777 Dec 11, 2025
729ae0a
Feature/#28 google apple login
yyytir777 Dec 16, 2025
b5c29db
Merge branch 'main' into develop
yyytir777 Dec 16, 2025
2e5da55
Merge branch 'develop' of https://github.com/tinybite-2025/tinybite-s…
yyytir777 Dec 18, 2025
1fbd896
merge main into develop : main의 핫픽스 변경사항 develop에 반영
yyytir777 Dec 18, 2025
dd9771a
workflow 줄바꿈 에러 수정
yyytir777 Dec 18, 2025
38de611
hotifx : 에러 핸들링 수정 및 무중단 배포 삭제 (리소스 너무 많이 먹음)
yyytir777 Dec 19, 2025
91b8cba
main의 핫픽스 develop에 반영
yyytir777 Dec 22, 2025
fbc5e90
수정사항 반영 (API 인증 관련, db schema, 예외 처리 등..)
yyytir777 Dec 23, 2025
7b9fb7b
main브랜치 핫픽스 반영
yyytir777 Dec 24, 2025
bbb080f
Feature/35 term (#38)
yyytir777 Dec 24, 2025
4c352aa
fix : docker compose 명령어 수정
yyytir777 Dec 24, 2025
27dfcbb
Feature : 파티 기능 (#42)
milowon Dec 27, 2025
0de3a43
Merge branch 'main' into develop
milowon Dec 31, 2025
2f27dee
hotfix : url parser 경로 제거
milowon Dec 31, 2025
6eb0d8d
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 1, 2026
3f69bc1
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 2, 2026
45191a9
hotfix : 파티 거리 계산 로직 임시 주석 처리
milowon Jan 2, 2026
3a9a6e6
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 2, 2026
b2ca1f4
hotfix : 파티 수정, 삭제 controller 추가
milowon Jan 2, 2026
38ab16c
hotfix : 선택 값들이 존재할때만 넣도록 수정
milowon Jan 2, 2026
037d2e4
hotfix : 위도, 경도 로직 삭제
milowon Jan 2, 2026
9d305f8
Feat : 마이페이지 참여중인 파티 조회 (#50)
milowon Jan 2, 2026
1cee657
hotfix : user service에 transactional 어노테이션 추가
milowon Jan 2, 2026
5213214
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 2, 2026
ed3a399
hotfix : 참여중 파티 조회 반환 형식 통일
milowon Jan 2, 2026
374f720
hotfix : 파티 생성, 조회 시, 거리 계산 로직 반영
milowon Jan 2, 2026
4ddfa39
Hotfix: 유저 좌표 입력 requestParam 형식으로 변경
milowon Jan 2, 2026
42bc4d4
Merge branch 'main' into develop
milowon Jan 2, 2026
9ee078a
Merge branch 'main' into develop
milowon Jan 2, 2026
3be9d38
hotfix : 누락된 swagger 문서 수정사항 반영
milowon Jan 2, 2026
89bbef3
Merge branch 'main' into develop
milowon Jan 2, 2026
b281a29
feat : 회원 탈퇴 및 재가입 방지, 검증 (#65)
milowon Jan 2, 2026
35a62b7
fix : 파티 수정 버그 픽스 (#67)
milowon Jan 2, 2026
a4f3582
hotfix : 탈퇴 유저 마스킹 로직 변경
milowon Jan 3, 2026
9f05a6a
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 3, 2026
d802e15
feat : 마이페이지에서 참여중,호스트인 파티 구분해서 조회 (#71)
milowon Jan 4, 2026
8715584
Feature/73 search party (#74)
yyytir777 Jan 4, 2026
78349d6
fix : 스웨거 description 추가
yyytir777 Jan 4, 2026
ba3d9d8
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
c15e45b
Feature/73 search party (#76)
yyytir777 Jan 4, 2026
92bef29
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
bd136de
Fix : 호스트만 있을때는 파티 수정할 수 있도록 변경 (#78)
milowon Jan 4, 2026
5d3e13d
fix : 파티 삭제시 호스트는 현재인원에서 제외하도록 수정 (#80)
milowon Jan 4, 2026
1e5b600
hotfix : jpa 네이밍 및 쿼리 수정
yyytir777 Jan 4, 2026
ba4ed25
hotfix : jpa 네이밍 및 쿼리 수정
yyytir777 Jan 4, 2026
c2d9465
Merge branch 'main' into develop
yyytir777 Jan 4, 2026
0b783c7
feat : 파티 카테고리, 최신순, 거리순 정렬 (#83)
milowon Jan 4, 2026
4474664
Merge branch 'main' of https://github.com/tinybite-2025/tinybite-serv…
milowon Jan 4, 2026
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