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 @@ -4,6 +4,7 @@
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import java.time.LocalDate;
import java.util.List;

@Schema(description = "기록 생성 요청")
Expand All @@ -13,6 +14,9 @@ public record HistoryCreateRequest(
description = "기록의 내용",
example = "안녕 오늘 오지게 덥다 ㄷㄷ;; 근데 한달 뒤면 가을임 벌써 가을 기대 만발ㅋ")
String content,
@NotNull(message = "기록 작성 날짜는 비워둘 수 없습니다.")
@Schema(description = "기록 작성 날짜", example = "2026-04-12")
LocalDate historyDate,
@NotNull(message = "상황 ID는 비워둘 수 없습니다.") @Schema(description = "상황 ID", example = "7")
Long situationId,
@NotNull(message = "스타일 목록은 비워둘 수 없습니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
@AllArgsConstructor
public enum HistoryErrorCode implements BaseErrorCode {
BANNED_HISTORY(400, "HISTORY_4001", "신고당한 기록은 조회할 수 없습니다."),
HISTORY_ALREADY_EXISTS(400, "HISTORY_4002", "해당 날짜의 기록이 이미 존재합니다."),

LIMITED_AUTHORITY(403, "HISTORY_4031", "기록에 대한 접근 권한이 없습니다."),
BLOCKED_AUTHORITY(403, "HISTORY_4032", "기록 작성자를 차단했거나 차단 당했습니다"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ public class HistoryServiceImpl implements HistoryService {
@Transactional
public HistoryCreateResponse createHistory(HistoryCreateRequest request) {
final Member currentMember = memberUtil.getCurrentMember();
final LocalDate historyDate = request.historyDate();

validateDuplicateHistoryDate(currentMember.getId(), historyDate);

final Situation situation = getSituationById(request.situationId());

Expand All @@ -94,7 +97,7 @@ public HistoryCreateResponse createHistory(HistoryCreateRequest request) {
final String content =
Optional.ofNullable(request.content()).map(String::trim).orElse(null);
final History history =
History.createHistory(LocalDate.now(KST), content, currentMember, situation);
History.createHistory(historyDate, content, currentMember, situation);
historyRepository.save(history);

List<HistoryImage> images = new ArrayList<>();
Expand Down Expand Up @@ -516,6 +519,12 @@ private Map<Long, Style> validateAndLoadStyles(List<Long> styleIds) {
return styleMap;
}

private void validateDuplicateHistoryDate(Long memberId, LocalDate historyDate) {
if (historyRepository.existsByMemberIdAndHistoryDate(memberId, historyDate)) {
throw new BaseCustomException(HistoryErrorCode.HISTORY_ALREADY_EXISTS);
}
}

private Map<Long, Cloth> validateAndLoadClothes(Member member, List<Long> clothIds) {
if (clothIds == null || clothIds.isEmpty()) {
return Map.of();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@ List<MemberLike> findLikedHistoriesByMemberId(
@Query("delete from MemberLike ml where ml.history.id = :historyId")
void deleteAllByHistoryId(Long historyId);

@Modifying
@Query(
"""
delete from MemberLike ml
where ml.member.id = :memberId
and ml.history.member.id = :historyOwnerId
""")
void deleteAllByMemberIdAndHistoryOwnerId(Long memberId, Long historyOwnerId);

@Query(
"""
select ml.history.id
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.clokey.domain.history.repository.HistoryRepository;
import org.clokey.domain.like.repository.MemberLikeRepository;
import org.clokey.domain.member.dto.request.DuplicatedNicknameCheckRequest;
import org.clokey.domain.member.dto.request.ProfileUpdateRequest;
import org.clokey.domain.member.dto.response.*;
Expand Down Expand Up @@ -40,6 +41,7 @@
private final FollowRepository followRepository;
private final PendingFollowRepository pendingFollowRepository;
private final BlockRepository blockRepository;
private final MemberLikeRepository memberLikeRepository;

private final ApplicationEventPublisher eventPublisher;

Expand Down Expand Up @@ -95,6 +97,8 @@
if (existingBlock.isPresent()) {
blockRepository.delete(existingBlock.get());
} else {
memberLikeRepository.deleteAllByMemberIdAndHistoryOwnerId(
blocker.getId(), blocked.getId());
Block block = Block.createBlock(blocker, blocked);
blockRepository.save(block);
}
Expand Down Expand Up @@ -125,7 +129,7 @@
Long memberId, Long lastFollowId, boolean isFollowing, Integer size) {
Member currentMember = memberUtil.getCurrentMember();
Member targetMember = getMemberById(memberId);
SliceResponse<FollowMemberResponse> response;

Check warning on line 132 in clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this unused "response" local variable.

See more on https://sonarcloud.io/project/issues?id=Clokey-dev_clokey-server&issues=AZ2HCbHp56xqTh8SblFo&open=AZ2HCbHp56xqTh8SblFo&pullRequest=409

if (!currentMember.equals(targetMember)) {
validatePrivacy(currentMember, targetMember);
Expand Down Expand Up @@ -241,7 +245,7 @@
private void validatePrivacy(Member currentMember, Member targetMember) {
if (!currentMember.getId().equals(targetMember.getId())
&& targetMember.getVisibility().equals(Visibility.PRIVATE)) {
if (!followRepository.existsByFollowFrom_IdAndFollowTo_Id(

Check warning on line 248 in clokey-api/src/main/java/org/clokey/domain/member/service/MemberServiceImpl.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Merge this if statement with the enclosing one.

See more on https://sonarcloud.io/project/issues?id=Clokey-dev_clokey-server&issues=AZ2HCbHp56xqTh8SblFm&open=AZ2HCbHp56xqTh8SblFm&pullRequest=409
currentMember.getId(), targetMember.getId())) {
throw new BaseCustomException(MemberErrorCode.PRIVATE_MEMBER_ACCESS_DENIED);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDate;
import java.util.List;
import org.clokey.domain.history.dto.request.HistoryCreateRequest;
import org.clokey.domain.history.dto.request.HistoryImagesUploadRequest;
Expand Down Expand Up @@ -88,6 +89,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -127,6 +129,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
longContent,
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -150,11 +153,41 @@ class 기록_생성_요청_시 {
.andExpect(jsonPath("$.result.content").value("기록의 내용은 최대 120자까지 가능합니다."));
}

@Test
void 기록작성날짜가_null이면_예외가_발생한다() throws Exception {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
null,
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
List.of(
new HistoryCreateRequest.Payload(
"testUrl",
List.of(
new HistoryCreateRequest.ClothTag(
1L, 0.42, 0.73)))));

ResultActions perform =
mockMvc.perform(
post("/histories")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(request)));

perform.andExpect(status().isBadRequest())
.andExpect(jsonPath("$.isSuccess").value(false))
.andExpect(jsonPath("$.code").value("COMMON400"))
.andExpect(jsonPath("$.message").value("잘못된 요청입니다."))
.andExpect(jsonPath("$.result.historyDate").value("기록 작성 날짜는 비워둘 수 없습니다."));
}

@Test
void 상황ID가_null이면_예외가_발생한다() throws Exception {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
null,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -183,6 +216,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
null,
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -211,6 +245,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -239,6 +274,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L, 3L, 4L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -267,6 +303,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -290,6 +327,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -313,6 +351,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -350,6 +389,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -373,6 +413,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -401,6 +442,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -430,6 +472,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -458,6 +501,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -487,6 +531,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand Down Expand Up @@ -515,6 +560,7 @@ class 기록_생성_요청_시 {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of(" "),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import static org.mockito.BDDMockito.given;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import org.clokey.IntegrationTest;
import org.clokey.TransactionUtil;
Expand Down Expand Up @@ -59,6 +60,8 @@
@RecordApplicationEvents
class HistoryServiceImplTest extends IntegrationTest {

private static final ZoneId KST = ZoneId.of("Asia/Seoul");

@Autowired private TransactionUtil transactionUtil;

@Autowired private MemberRepository memberRepository;
Expand Down Expand Up @@ -192,6 +195,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -217,8 +221,9 @@ void setUp() {
.extracting(
h -> h.getMember().getId(),
h -> h.getSituation().getId(),
History::getHistoryDate,
History::getContent)
.containsExactly(1L, 1L, "testContent 1");
.containsExactly(1L, 1L, LocalDate.of(2026, 4, 12), "testContent 1");

List<HistoryImage> images = historyImageRepository.findByHistoryId(history.getId());
assertThat(images).hasSize(2);
Expand Down Expand Up @@ -253,12 +258,39 @@ void setUp() {
assertThat(historyHashtags).hasSize(2);
}

@Test
void 같은_날짜의_기록이_이미_존재하면_예외가_발생한다() {
// given
Member currentMember =
transactionUtil.getResult(() -> memberRepository.findById(1L).orElseThrow());
Situation situation =
transactionUtil.getResult(() -> situationRepository.findById(1L).orElseThrow());
historyRepository.save(
History.createHistory(
LocalDate.of(2026, 4, 12), "existing", currentMember, situation));

HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
List.of(new Payload("testUrl1", null)));

// when & then
assertThatThrownBy(() -> historyService.createHistory(request))
.isInstanceOf(BaseCustomException.class)
.hasMessage(HistoryErrorCode.HISTORY_ALREADY_EXISTS.getMessage());
}

@Test
void 존재하지_않는_상황_ID를_포함하는_경우_예외가_발생한다() {
// given
HistoryCreateRequest request =
new HistoryCreateRequest(
"hi",
LocalDate.of(2026, 4, 12),
999L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -281,6 +313,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 10L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -302,6 +335,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -318,6 +352,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -334,6 +369,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2"),
Expand All @@ -354,6 +390,7 @@ void setUp() {
HistoryCreateRequest request =
new HistoryCreateRequest(
"testContent 1 ",
LocalDate.of(2026, 4, 12),
1L,
List.of(1L, 2L),
List.of("testHashtag1", "testHashtag2", "testHashtag3"),
Expand Down Expand Up @@ -1300,10 +1337,10 @@ void setUp() {
situationRepository.save(situation);

History todayHistory =
History.createHistory(LocalDate.now(), "testContent", member1, situation);
History.createHistory(LocalDate.now(KST), "testContent", member1, situation);
History otherHistory =
History.createHistory(
LocalDate.now().minusDays(1), "oldContent", member2, situation);
LocalDate.now(KST).minusDays(1), "oldContent", member2, situation);
historyRepository.saveAll(List.of(todayHistory, otherHistory));
}

Expand Down
Loading
Loading