Skip to content

Commit 4755083

Browse files
authored
merge: pull request #113 from feat/idempotency/1
feat: 감정일기 v2 #112
2 parents 652de21 + 6dd1a86 commit 4755083

File tree

3 files changed

+29
-14
lines changed

3 files changed

+29
-14
lines changed

default/build.gradle.kts

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,6 @@ plugins {
22
id("java")
33
id("org.springframework.boot") version "3.4.2"
44
id("io.spring.dependency-management") version "1.1.7"
5-
id("org.sonarqube") version "4.4.1.3373"
6-
}
7-
8-
sonarqube {
9-
properties {
10-
property("sonar.exclusions", "**/JwtProvider.java,**/JwtAuthenticationFilter.java,...")
11-
property("sonar.duplication.exclusions", "**/JwtProvider.java,**/JwtAuthenticationFilter.java,...")
12-
}
135
}
146

157
group = "org.dfbf"

default/src/main/java/org/dfbf/soundlink/domain/emotionRecord/controller/EmotionRecordController.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,13 @@ public class EmotionRecordController {
2525
@PostMapping
2626
@Operation(
2727
summary = "감정 기록 작성/저장 API",
28-
description = "작성한 감정 기록을 저장합니다."
28+
description = "작성한 감정 기록을 저장 & Idempotency Key를 사용하여 중복 요청을 방지"
2929
)
30-
public ResponseResult saveEmotionWithMusic(
30+
public ResponseResult saveEmotionWithMusicAndIdempotency(
31+
@RequestHeader(name = "Idempotency-Key") String idempotencyKey,
3132
@AuthenticationPrincipal Long userId,
3233
@Valid @RequestBody EmotionRecordRequestDTO request) {
33-
return emotionRecordService.saveEmotionRecordWithMusic(userId, request);
34+
return emotionRecordService.saveEmotionRecordWithMusicAndIdempotent(idempotencyKey, userId, request);
3435
}
3536

3637
@GetMapping

default/src/main/java/org/dfbf/soundlink/domain/emotionRecord/service/EmotionRecordService.java

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package org.dfbf.soundlink.domain.emotionRecord.service;
22

3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import feign.Response;
35
import jakarta.persistence.OptimisticLockException;
46
import lombok.RequiredArgsConstructor;
57
import lombok.extern.slf4j.Slf4j;
@@ -26,13 +28,15 @@
2628
import org.springframework.data.domain.PageRequest;
2729
import org.springframework.data.domain.Pageable;
2830
import org.springframework.data.domain.Sort;
31+
import org.springframework.data.redis.core.RedisTemplate;
2932
import org.springframework.retry.annotation.Backoff;
3033
import org.springframework.retry.annotation.Recover;
3134
import org.springframework.retry.annotation.Retryable;
3235
import org.springframework.stereotype.Service;
3336
import org.springframework.transaction.annotation.Transactional;
3437

3538
import java.util.List;
39+
import java.util.concurrent.TimeUnit;
3640

3741
@Service
3842
@RequiredArgsConstructor
@@ -47,6 +51,8 @@ public class EmotionRecordService {
4751
// private final EmotionRecordCacheService emotionRecordCacheService;
4852
private final ChatRoomRepository chatRoomRepository;
4953

54+
private final RedisTemplate<String, Object> redisTemplate;
55+
5056
@Transactional
5157
public ResponseResult saveEmotionRecordWithMusic(Long userId, EmotionRecordRequestDTO request) {
5258

@@ -74,8 +80,8 @@ public ResponseResult saveEmotionRecordWithMusic(Long userId, EmotionRecordReque
7480
.build();
7581
emotionRecordRepository.save(emotionRecord);
7682

77-
/* // 게시글 생성 시, 해당 조건에 맞는 캐시 키 삭제
78-
emotionRecordCacheService.evictEmotionRecordCache(
83+
// 게시글 생성 시, 해당 조건에 맞는 캐시 키 삭제
84+
/*emotionRecordCacheService.evictEmotionRecordCache(
7985
userId,
8086
request.spotifyId(),
8187
request.emotion().name()
@@ -95,6 +101,23 @@ public ResponseResult saveEmotionRecordWithMusic(Long userId, EmotionRecordReque
95101
}
96102
}
97103

104+
// 감정일기 추가
105+
// 중복 요청 방지 기능 추가 (멱등성 보장)
106+
@Transactional
107+
public ResponseResult saveEmotionRecordWithMusicAndIdempotent(String idempotencyKey, Long userId, EmotionRecordRequestDTO request) {
108+
if (idempotencyKey != null && !redisTemplate.keys("EmotionRecord:" + idempotencyKey).isEmpty()) {
109+
ObjectMapper mapper = new ObjectMapper();
110+
return mapper.convertValue(redisTemplate.opsForValue().get("EmotionRecord:" + idempotencyKey), ResponseResult.class);
111+
}
112+
113+
ResponseResult responseResult = saveEmotionRecordWithMusic(userId, request);
114+
if (idempotencyKey != null) {
115+
redisTemplate.opsForValue().set("EmotionRecord:" + idempotencyKey, responseResult, 10, TimeUnit.SECONDS);
116+
}
117+
118+
return responseResult;
119+
}
120+
98121
@Transactional(readOnly = true)
99122
public ResponseResult getEmotionRecordsByUserId(Long userId, int page, int size) {
100123
ResponseResult pageValidationResult = validateAndCreatePageable(page, size);
@@ -122,7 +145,6 @@ public ResponseResult getEmotionRecordsByUserId(Long userId, int page, int size)
122145
}
123146
}
124147

125-
126148
@Transactional(readOnly = true)
127149
public ResponseResult getEmotionRecordsByLoginId(String userTag, int page, int size) {
128150
ResponseResult pageValidationResult = validateAndCreatePageable(page, size);

0 commit comments

Comments
 (0)