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 @@ -3,9 +3,9 @@
import team.wego.wegobackend.group.domain.entity.GroupImage;

public record GroupImageItemResponse(
int sortOrder,
Long imageId440x240,
Long imageId100x100,
int sortOrder,
String imageUrl440x240,
String imageUrl100x100
) {
Expand All @@ -25,9 +25,9 @@ public static GroupImageItemResponse from(
String thumbUrlVal = (thumb != null) ? thumb.getImageUrl() : null;

return new GroupImageItemResponse(
sortOrder,
mainId,
thumbId,
sortOrder,
mainUrlVal,
thumbUrlVal
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static team.wego.wegobackend.group.domain.entity.GroupUserStatus.ATTEND;
import static team.wego.wegobackend.group.domain.entity.GroupUserStatus.LEFT;

import java.time.LocalDateTime;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
Expand All @@ -28,6 +29,7 @@
import team.wego.wegobackend.group.domain.entity.GroupRole;
import team.wego.wegobackend.group.domain.entity.GroupTag;
import team.wego.wegobackend.group.domain.entity.GroupUser;
import team.wego.wegobackend.group.domain.entity.MyGroupType;
import team.wego.wegobackend.group.domain.exception.GroupErrorCode;
import team.wego.wegobackend.group.domain.exception.GroupException;
import team.wego.wegobackend.group.domain.repository.GroupImageRepository;
Expand Down Expand Up @@ -567,4 +569,97 @@ public void deleteGroup(Long userId, Long groupId) {

groupRepository.delete(group);
}

@Transactional(readOnly = true)
public GetGroupListResponse getMyGroups(
Long userId,
String type,
Long cursor,
int size
) {
MyGroupType myGroupType = MyGroupType.from(type);

int pageSize = Math.max(1, Math.min(size, 50));

return switch (myGroupType) {
case CURRENT -> getMyCurrentGroups(userId, cursor, pageSize);
case MY_POST -> getMyPostGroups(userId, cursor, pageSize);
case PAST -> getMyPastGroups(userId, cursor, pageSize);
};
}

private GetGroupListResponse getMyCurrentGroups(Long userId, Long cursor, int size) {
LocalDateTime now = LocalDateTime.now();

// 지금은 ATTEND 만 사용, 나중에 LEFT 도 포함하려면 여기만 변경하면 됨
List<String> statuses = List.of(ATTEND.name());

List<Group> groups = groupRepository.findCurrentGroupsByUser(
userId,
statuses,
cursor,
now,
size + 1 // 다음 페이지 여부 판단용
);

Long nextCursor = null;
if (groups.size() > size) {
Group lastExtra = groups.remove(size);
nextCursor = lastExtra.getId();
}

List<GroupListItemResponse> items = groups.stream()
.map(this::toGroupListItemResponse)
.toList();

return GetGroupListResponse.of(items, nextCursor);
}

private GetGroupListResponse getMyPostGroups(Long userId, Long cursor, int size) {
List<Group> groups = groupRepository.findMyPostGroupsByHost(
userId,
cursor,
size + 1
);

Long nextCursor = null;
if (groups.size() > size) {
Group lastExtra = groups.remove(size);
nextCursor = lastExtra.getId();
}

List<GroupListItemResponse> items = groups.stream()
.map(this::toGroupListItemResponse)
.toList();

return GetGroupListResponse.of(items, nextCursor);
}

private GetGroupListResponse getMyPastGroups(Long userId, Long cursor, int size) {
LocalDateTime now = LocalDateTime.now();

// 추후 LEFT 포함하고 싶으면 여기만 변경
List<String> statuses = List.of(ATTEND.name());

List<Group> groups = groupRepository.findPastGroupsByUser(
userId,
statuses,
cursor,
now,
size + 1
);

Long nextCursor = null;
if (groups.size() > size) {
Group lastExtra = groups.remove(size);
nextCursor = lastExtra.getId();
}

List<GroupListItemResponse> items = groups.stream()
.map(this::toGroupListItemResponse)
.toList();

return GetGroupListResponse.of(items, nextCursor);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package team.wego.wegobackend.group.domain.entity;

import lombok.AccessLevel;
import lombok.Getter;
import team.wego.wegobackend.group.domain.exception.GroupErrorCode;
import team.wego.wegobackend.group.domain.exception.GroupException;

@Getter(AccessLevel.PUBLIC)
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using @Getter(AccessLevel.PUBLIC) is redundant since PUBLIC is the default access level for @Getter. Simply use @Getter instead.

Suggested change
@Getter(AccessLevel.PUBLIC)
@Getter

Copilot uses AI. Check for mistakes.
public enum MyGroupType {
CURRENT("current"),
MY_POST("myPost"),
PAST("past");

private final String value;

MyGroupType(String value) {
this.value = value;
}

public static MyGroupType from(String value) {
if (value == null) {
throw new GroupException(GroupErrorCode.MY_GROUP_TYPE_NOT_NULL);
}
return switch (value) {
case "current" -> CURRENT;
case "myPost" -> MY_POST;
case "past" -> PAST;
default -> throw new GroupException(GroupErrorCode.INVALID_MY_GROUP_TYPE, value);


};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,13 @@ public enum GroupErrorCode implements ErrorCode {
GROUP_CAPACITY_EXCEEDED(HttpStatus.BAD_REQUEST, "모임: 모임 최대 참가자 수를 초과했습니다. 모임 ID: %s"),
NOT_ATTEND_GROUP(HttpStatus.BAD_REQUEST, "모임: 참여한 적 없거나 이미 나간 상태입니다. 모임 ID: %s 회원 ID: %s"),
HOST_CANNOT_LEAVE_OWN_GROUP(HttpStatus.BAD_REQUEST, "모임: HOST는 나갈 수 없습니다. 모임 ID: %s 회원 ID: %s"),
NO_PERMISSION_TO_UPDATE_GROUP(HttpStatus.FORBIDDEN, "모임: 해당 모임을 수정할 권한이 없습니다. 모임 ID: %s 회원 ID: %s"),
INVALID_MAX_PARTICIPANTS_LESS_THAN_CURRENT(HttpStatus.BAD_REQUEST, "모임: 현재 참여 인원 수(%s)보다 작은 값으로 최대 인원을 줄일 수 없습니다."),
NO_PERMISSION_TO_DELETE_GROUP(HttpStatus.UNAUTHORIZED, "모임: 삭제할 수 있는 권한이 없습니다.");
NO_PERMISSION_TO_UPDATE_GROUP(HttpStatus.FORBIDDEN,
"모임: 해당 모임을 수정할 권한이 없습니다. 모임 ID: %s 회원 ID: %s"),
INVALID_MAX_PARTICIPANTS_LESS_THAN_CURRENT(HttpStatus.BAD_REQUEST,
"모임: 현재 참여 인원 수(%s)보다 작은 값으로 최대 인원을 줄일 수 없습니다."),
NO_PERMISSION_TO_DELETE_GROUP(HttpStatus.UNAUTHORIZED, "모임: 삭제할 수 있는 권한이 없습니다."),
MY_GROUP_TYPE_NOT_NULL(HttpStatus.BAD_REQUEST, "모임: MyGroupType 값은 null일 수 없습니다."),
INVALID_MY_GROUP_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 MyGroupType: %s");

private final HttpStatus status;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package team.wego.wegobackend.group.domain.repository;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
Expand Down Expand Up @@ -33,4 +34,70 @@ List<Group> findGroupsWithKeywordAndCursor(
@Param("cursor") Long cursor,
@Param("limit") int limit
);

/**
* 내가 참여 중인 모임 (CURRENT)
*/
@Query(value = """
SELECT DISTINCT g.*
FROM v1_groups g
JOIN v1_group_users gu ON gu.group_id = g.group_id
WHERE g.deleted_at IS NULL
AND gu.user_id = :userId
AND gu.group_user_status IN (:statuses)
AND (:cursor IS NULL OR g.group_id < :cursor)
AND (g.end_time IS NULL OR g.end_time >= :now)
ORDER BY g.group_id DESC
LIMIT :limit
""", nativeQuery = true)
List<Group> findCurrentGroupsByUser(
@Param("userId") Long userId,
@Param("statuses") List<String> statuses,
@Param("cursor") Long cursor,
@Param("now") LocalDateTime now,
@Param("limit") int limit
);

/**
* 과거에 참여했던 모임 (PAST)
*/
@Query(value = """
SELECT DISTINCT g.*
FROM v1_groups g
JOIN v1_group_users gu ON gu.group_id = g.group_id
WHERE g.deleted_at IS NULL
AND gu.user_id = :userId
AND gu.group_user_status IN (:statuses)
AND (:cursor IS NULL OR g.group_id < :cursor)
AND g.end_time IS NOT NULL
AND g.end_time < :now
ORDER BY g.group_id DESC
LIMIT :limit
""", nativeQuery = true)
List<Group> findPastGroupsByUser(
@Param("userId") Long userId,
@Param("statuses") List<String> statuses,
@Param("cursor") Long cursor,
@Param("now") LocalDateTime now,
@Param("limit") int limit
);

/**
* 내가 만든 모임 (MY_POST) – group_users 안타고 host 기준으로만
*/
@Query(value = """
SELECT DISTINCT g.*
FROM v1_groups g
WHERE g.deleted_at IS NULL
AND g.host_id = :userId
AND (:cursor IS NULL OR g.group_id < :cursor)
ORDER BY g.group_id DESC
LIMIT :limit
""", nativeQuery = true)
List<Group> findMyPostGroupsByHost(
@Param("userId") Long userId,
@Param("cursor") Long cursor,
@Param("limit") int limit
);
}

Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,20 @@ public ResponseEntity<ApiResponse<Void>> deleteGroup(
.body(ApiResponse.success(HttpStatus.NO_CONTENT.value(), null));
}

// 나의 모임 목록 조회

@GetMapping("/me")
public ResponseEntity<ApiResponse<GetGroupListResponse>> getMyGroups(
@RequestParam Long userId, // TODO: 나중에 인증 정보에서 꺼내기
@RequestParam String type,
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The type parameter should include validation annotations to ensure only valid values are accepted. Consider adding @Pattern(regexp = "^(current|myPost|past)$") to validate at the controller level and provide clearer error messages to API consumers before reaching the service layer.

Copilot uses AI. Check for mistakes.
@RequestParam(required = false) Long cursor,
@RequestParam int size
Copy link

Copilot AI Dec 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The size parameter lacks validation constraints. Following the pattern used in getGroupList, consider adding @Min(1) and @Max(50) annotations to enforce valid pagination sizes at the controller level, providing clearer API contracts.

Copilot uses AI. Check for mistakes.
) {
GetGroupListResponse response =
groupService.getMyGroups(userId, type, cursor, size);

return ResponseEntity
.status(HttpStatus.OK)
.body(ApiResponse.success(HttpStatus.OK.value(), response));
}

}
Loading