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 @@ -39,7 +39,8 @@ public AdminActivityCategoryResponse createCategory(ActivityCategoryCreateReques
throw new ActivityException(ActivityExceptionType.CATEGORY_SLUG_DUPLICATED);
}

int displayOrder = activityCategoryRepository.findAllByOrderByDisplayOrderAsc().size();
int displayOrder = DisplayOrders.next(activityCategoryRepository.findAllByOrderByDisplayOrderAsc(),
ActivityCategory::getDisplayOrder);
ActivityCategory category = activityCategoryRepository.save(ActivityCategory.builder()
.slug(request.slug())
.name(request.name())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.bcsdlab.bcsdinternalapiv2.curriculum.model.Curriculum;
import com.bcsdlab.bcsdinternalapiv2.curriculum.repository.CurriculumRepository;
import com.bcsdlab.bcsdinternalapiv2.global.controller.dto.request.PublishRequest;
import com.bcsdlab.bcsdinternalapiv2.global.util.DisplayOrders;
import com.bcsdlab.bcsdinternalapiv2.track.exception.TrackException;
import com.bcsdlab.bcsdinternalapiv2.track.exception.TrackExceptionType;
import com.bcsdlab.bcsdinternalapiv2.track.model.TrackPage;
Expand Down Expand Up @@ -59,7 +60,9 @@ public AdminCurriculumSummaryResponse createCurriculum(Long trackPageId, Curricu
throw new CurriculumException(CurriculumExceptionType.NAME_REQUIRED);
}

int displayOrder = curriculumRepository.findAllByTrackPage_IdOrderByDisplayOrderAsc(trackPageId).size();
int displayOrder = DisplayOrders.next(
curriculumRepository.findAllByTrackPage_IdOrderByDisplayOrderAsc(trackPageId),
Curriculum::getDisplayOrder);
Curriculum curriculum = curriculumRepository.save(Curriculum.builder()
.trackPage(trackPage)
.name(name)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ public CurriculumWeekResponse createWeek(Long curriculumId, WeekRequest request,
.orElseThrow(() -> new CurriculumException(CurriculumExceptionType.CURRICULUM_NOT_FOUND));
requireCurriculumAccess(memberId, curriculum.getTrackPage());

int displayOrder = curriculumWeekRepository.findAllByCurriculum_IdOrderByDisplayOrderAsc(curriculumId).size();
int displayOrder = DisplayOrders.next(
curriculumWeekRepository.findAllByCurriculum_IdOrderByDisplayOrderAsc(curriculumId),
CurriculumWeek::getDisplayOrder);
CurriculumWeek week = curriculumWeekRepository.save(CurriculumWeek.builder()
.curriculum(curriculum)
.weekFrom(request.weekFrom())
Expand Down Expand Up @@ -124,7 +126,8 @@ public void reorderWeeks(Long curriculumId, OrderRequest request, Long memberId)
public CurriculumTopicResponse createTopic(Long weekId, TopicRequest request, Long memberId) {
CurriculumWeek week = findWeekOrThrow(weekId);
requireCurriculumAccess(memberId, week.getCurriculum().getTrackPage());
int displayOrder = curriculumTopicRepository.findAllByWeek_IdOrderByDisplayOrderAsc(weekId).size();
int displayOrder = DisplayOrders.next(curriculumTopicRepository.findAllByWeek_IdOrderByDisplayOrderAsc(weekId),
CurriculumTopic::getDisplayOrder);
CurriculumTopic topic = curriculumTopicRepository.save(CurriculumTopic.builder()
.week(week)
.title(request.title())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ public List<AdminGameMemberResponse> attachMembers(Long gameId, GameMembersAttac
}
}

int nextOrder = gameMemberRepository.findAllByGame_IdOrderByDisplayOrderAsc(gameId).size();
int nextOrder = DisplayOrders.next(gameMemberRepository.findAllByGame_IdOrderByDisplayOrderAsc(gameId),
GameMember::getDisplayOrder);
for (Long memberId : memberIds) {
gameMemberRepository.save(GameMember.builder()
.game(game)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ public AdminGameDetailResponse createGame(GameCreateRequest request, Long member
.oneLiner(request.oneLiner())
.track(track)
.teamLabel(request.teamLabel())
.displayOrder((int) gameRepository.count())
.displayOrder(DisplayOrders.next(gameRepository.findAllByOrderByDisplayOrderAsc(),
Game::getDisplayOrder))
.published(false)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.ToIntFunction;

/**
* {@code display_order} 재부여 순수 함수. 특정 엔티티 타입에 의존하지 않는다 —
Expand All @@ -22,6 +23,19 @@ public final class DisplayOrders {
private DisplayOrders() {
}

/**
* 새 항목의 display_order. 삭제가 순서를 다시 매기지 않아 번호에 빈칸이 생기므로, 개수가 아니라
* 마지막 순서 + 1이다(개수를 쓰면 기존 항목과 순서가 겹친다).
*
* @param siblingsInOrder 같은 부모 아래 형제 항목, display_order 오름차순
*/
public static <T> int next(List<T> siblingsInOrder, ToIntFunction<T> displayOrder) {
if (siblingsInOrder.isEmpty()) {
return 0;
}
return displayOrder.applyAsInt(siblingsInOrder.getLast()) + 1;
}

/**
* @param requestedIds 새 순서대로 나열된 id 목록 (배열 인덱스가 곧 새 display_order)
* @param existingIds 현재 그 부모 아래 존재하는 전체 id 집합
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ public List<AdminMentorSlotResponse> addSlot(MentorSlotCreateRequest request) {
throw new HomeException(HomeExceptionType.MENTOR_ALREADY_ASSIGNED);
}

int nextOrder = (int) mentorSlotRepository.count();
int nextOrder = DisplayOrders.next(mentorSlotRepository.findAllByOrderByDisplayOrderAsc(),
MentorSlot::getDisplayOrder);
mentorSlotRepository.save(MentorSlot.builder().member(member).displayOrder(nextOrder).build());
return getSlots();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public AdminQnaResponse createQnaItem(QnaCreateRequest request) {
QnaItem saved = qnaItemRepository.save(QnaItem.builder()
.question(request.question())
.answer(request.answer())
.displayOrder((int) qnaItemRepository.count())
.displayOrder(DisplayOrders.next(qnaItemRepository.findAllByOrderByDisplayOrderAsc(),
QnaItem::getDisplayOrder))
.published(true)
.build());
return AdminQnaResponse.from(saved);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ public List<AdminTrackPageMemberResponse> attachMembers(Long trackPageId, TrackP
}
}

int nextOrder = trackPageMemberRepository.findAllByTrackPage_IdOrderByDisplayOrderAsc(trackPageId).size();
int nextOrder = DisplayOrders.next(
trackPageMemberRepository.findAllByTrackPage_IdOrderByDisplayOrderAsc(trackPageId),
TrackPageMember::getDisplayOrder);
for (Long memberId : memberIds) {
trackPageMemberRepository.save(TrackPageMember.builder()
.trackPage(trackPage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,8 @@ public AdminTrackPageDetailResponse createTrackPage(TrackPageCreateRequest reque
.slug(slug)
.displayName(request.displayName())
.tagline(request.tagline())
.displayOrder((int) trackPageRepository.count())
.displayOrder(DisplayOrders.next(trackPageRepository.findAllByOrderByDisplayOrderAsc(),
TrackPage::getDisplayOrder))
.published(true)
.build();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.bcsdlab.bcsdinternalapiv2.global;

import static org.assertj.core.api.Assertions.assertThat;

import com.bcsdlab.bcsdinternalapiv2.global.util.DisplayOrders;
import java.util.List;
import java.util.function.ToIntFunction;
import org.junit.jupiter.api.Test;

class DisplayOrdersTest {

private static final ToIntFunction<Integer> SELF = Integer::intValue;

@Test
void 형제가_없으면_0이다() {
assertThat(DisplayOrders.next(List.of(), SELF)).isZero();
}

@Test
void 삭제로_빈_번호가_있어도_마지막_순서_다음이다() {
assertThat(DisplayOrders.next(List.of(0, 2), SELF)).isEqualTo(3);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.bcsdlab.bcsdinternalapiv2.home;

import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
Expand All @@ -21,6 +23,8 @@
import com.bcsdlab.bcsdinternalapiv2.track.model.TrackMaster;
import com.bcsdlab.bcsdinternalapiv2.track.repository.TrackMasterRepository;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -186,4 +190,35 @@ private String login(String studentNumber) throws Exception {
.andReturn().getResponse().getContentAsString();
return objectMapper.readTree(body).get("accessToken").asText();
}

@Test
@DisplayName("Q&A를 지운 뒤 새로 만들면 남은 항목과 순서가 겹치지 않고 맨 뒤에 붙는다")
void 삭제_후_생성한_Q_A는_맨_뒤에_붙는다() throws Exception {
long first = createQna("첫 질문");
long second = createQna("둘째 질문");
createQna("셋째 질문");

mockMvc.perform(delete("/v1/admin/qna/" + second).header("Authorization", "Bearer " + adminToken))
.andExpect(status().isNoContent());
long fourth = createQna("넷째 질문");

String list = mockMvc.perform(get("/v1/admin/qna").header("Authorization", "Bearer " + adminToken))
.andExpect(status().isOk())
.andReturn().getResponse().getContentAsString();
List<Integer> orders = new ArrayList<>();
objectMapper.readTree(list).forEach(item -> orders.add(item.get("displayOrder").asInt()));
assertThat(orders).doesNotHaveDuplicates();
assertThat(objectMapper.readTree(list).get(0).get("id").asLong()).isEqualTo(first);
assertThat(objectMapper.readTree(list).get(2).get("id").asLong()).isEqualTo(fourth);
}

private long createQna(String question) throws Exception {
String body = mockMvc.perform(post("/v1/admin/qna")
.header("Authorization", "Bearer " + adminToken)
.contentType(MediaType.APPLICATION_JSON)
.content("{\"question\":\"%s\",\"answer\":\"답변\"}".formatted(question)))
.andExpect(status().isCreated())
.andReturn().getResponse().getContentAsString();
return objectMapper.readTree(body).get("id").asLong();
}
}
Loading