Skip to content

Commit fbfb6c9

Browse files
committed
feat: 감정일기 v2 #112
- 중복요청(따닥) 방지 기능 추가
1 parent 652de21 commit fbfb6c9

File tree

3 files changed

+35
-11
lines changed

3 files changed

+35
-11
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: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,18 @@ public ResponseResult saveEmotionWithMusic(
3333
return emotionRecordService.saveEmotionRecordWithMusic(userId, request);
3434
}
3535

36+
@PostMapping("/v2")
37+
@Operation(
38+
summary = "감정 기록 작성/저장 API",
39+
description = "작성한 감정 기록을 저장 & Idempotency Key를 사용하여 중복 요청을 방지."
40+
)
41+
public ResponseResult saveEmotionWithMusicAndIdempotency(
42+
@RequestHeader(name = "Idempotency-Key") String idempotencyKey,
43+
@AuthenticationPrincipal Long userId,
44+
@Valid @RequestBody EmotionRecordRequestDTO request) {
45+
return emotionRecordService.saveEmotionRecordWithMusicAndIdempotent(idempotencyKey, userId, request);
46+
}
47+
3648
@GetMapping
3749
@Operation(
3850
summary = "감정 기록 전체 조회 메인 API",

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

Lines changed: 23 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,21 @@ 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 (!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+
redisTemplate.opsForValue().set("EmotionRecord:" + idempotencyKey, responseResult, 10, TimeUnit.SECONDS);
115+
116+
return responseResult;
117+
}
118+
98119
@Transactional(readOnly = true)
99120
public ResponseResult getEmotionRecordsByUserId(Long userId, int page, int size) {
100121
ResponseResult pageValidationResult = validateAndCreatePageable(page, size);
@@ -122,7 +143,6 @@ public ResponseResult getEmotionRecordsByUserId(Long userId, int page, int size)
122143
}
123144
}
124145

125-
126146
@Transactional(readOnly = true)
127147
public ResponseResult getEmotionRecordsByLoginId(String userTag, int page, int size) {
128148
ResponseResult pageValidationResult = validateAndCreatePageable(page, size);

0 commit comments

Comments
 (0)