Skip to content

Commit c15de06

Browse files
authored
Merge pull request #56 from DropThe8bit/feat/alarm
[feature] 알림 API 구현
2 parents aefa5dd + 868964d commit c15de06

File tree

22 files changed

+276
-17
lines changed

22 files changed

+276
-17
lines changed
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package everTale.everTale_be.domain.alarm.controller;
2+
3+
import everTale.everTale_be.domain.alarm.dto.response.AlarmListResponseDto;
4+
import everTale.everTale_be.domain.alarm.service.AlarmService;
5+
import everTale.everTale_be.global.apiPayload.ApiResponse;
6+
import io.swagger.v3.oas.annotations.Operation;
7+
import io.swagger.v3.oas.annotations.Parameter;
8+
import io.swagger.v3.oas.annotations.tags.Tag;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.data.domain.Pageable;
11+
import org.springframework.data.web.PageableDefault;
12+
import org.springframework.web.bind.annotation.*;
13+
14+
@RestController
15+
@RequiredArgsConstructor
16+
@RequestMapping("/alarms")
17+
@Tag(name = "Alarm", description = "알림 API")
18+
public class AlarmController {
19+
20+
private final AlarmService alarmService;
21+
22+
@Operation(summary = "알림 조회 API", description = "사용자의 알림 목록을 8개씩 조회합니다.")
23+
@GetMapping
24+
public ApiResponse<AlarmListResponseDto> getAlarms(@PageableDefault(size = 8) Pageable pageable){
25+
AlarmListResponseDto responseDto = alarmService.getAlarms(pageable);
26+
return ApiResponse.onSuccess(responseDto);
27+
}
28+
29+
@Operation(summary = "알림 읽음 처리", description = "해당 alarmId에 해당하는 알림을 읽음 처리합니다.")
30+
@PostMapping("{alarmId}")
31+
public ApiResponse<String> readAlarm(@Parameter(description = "알림 ID") @PathVariable("alarmId") Long alarmId){
32+
alarmService.readAlarm(alarmId);
33+
return ApiResponse.onSuccess("알림 읽음 처리가 되었습니다.");
34+
}
35+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package everTale.everTale_be.domain.alarm.dto;
2+
3+
import everTale.everTale_be.domain.alarm.entity.Alarm;
4+
import everTale.everTale_be.domain.alarm.entity.Enum.AlarmType;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
9+
@Getter
10+
@Builder
11+
@Schema(description = "알림 요약 정보")
12+
public class AlarmSummary {
13+
14+
@Schema(description = "알림 ID", example = "1")
15+
private Long alarmId;
16+
17+
@Schema(description = "알림 타입", example = "EASTEREGG_VOICE")
18+
private AlarmType alarmType;
19+
20+
@Schema(description = "스토리 ID", example = "12")
21+
private Long storyId;
22+
23+
@Schema(description = "읽음 여부", example = "0")
24+
private boolean isRead;
25+
26+
public static AlarmSummary from(Alarm alarm){
27+
return AlarmSummary.builder()
28+
.alarmId(alarm.getId())
29+
.alarmType(alarm.getAlarmType())
30+
.storyId(alarm.getStory().getId())
31+
.isRead(alarm.isRead())
32+
.build();
33+
}
34+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package everTale.everTale_be.domain.alarm.dto.response;
2+
3+
import everTale.everTale_be.domain.alarm.dto.AlarmSummary;
4+
import everTale.everTale_be.domain.alarm.entity.Alarm;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
import lombok.Builder;
7+
import lombok.Getter;
8+
import org.springframework.data.domain.Page;
9+
10+
import java.util.List;
11+
12+
@Getter
13+
@Builder
14+
@Schema(description = "알림 목록 응답 DTO")
15+
public class AlarmListResponseDto {
16+
17+
@Schema(description = "알림 요약 리스트")
18+
private List<AlarmSummary> alarmSummaries;
19+
20+
@Schema(description = "현재 페이지 번호 (0부터 시작)", example = "0")
21+
private int currentPage;
22+
23+
@Schema(description = "전체 페이지 수", example = "5")
24+
private int totalPage;
25+
26+
@Schema(description = "전체 스토리 개수", example = "123")
27+
private long totalCount;
28+
29+
public static AlarmListResponseDto from(Page<Alarm> alarms){
30+
return AlarmListResponseDto.builder()
31+
.alarmSummaries(alarms.stream().map(AlarmSummary::from).toList())
32+
.currentPage(alarms.getNumber())
33+
.totalPage(alarms.getTotalPages())
34+
.totalCount(alarms.getTotalElements())
35+
.build();
36+
}
37+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package everTale.everTale_be.domain.alarm.entity;
2+
3+
import everTale.everTale_be.domain.alarm.entity.Enum.AlarmType;
4+
import everTale.everTale_be.domain.profile.entity.Profile;
5+
import everTale.everTale_be.domain.story.entity.Story;
6+
import jakarta.persistence.*;
7+
import lombok.AccessLevel;
8+
import lombok.Builder;
9+
import lombok.Getter;
10+
import lombok.NoArgsConstructor;
11+
import org.springframework.data.annotation.CreatedDate;
12+
13+
import java.time.LocalDateTime;
14+
15+
@Entity
16+
@Getter
17+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
18+
public class Alarm {
19+
20+
@Id
21+
@GeneratedValue(strategy = GenerationType.IDENTITY)
22+
private Long id;
23+
24+
@Enumerated(EnumType.STRING)
25+
private AlarmType alarmType;
26+
27+
@Column(nullable = false)
28+
private boolean isRead = false;
29+
30+
@CreatedDate
31+
@Column(name="created_at", updatable = false)
32+
private LocalDateTime createdAt;
33+
34+
@ManyToOne(fetch = FetchType.LAZY)
35+
@JoinColumn(name = "profile_id", updatable = false, nullable = false)
36+
private Profile profile;
37+
38+
@ManyToOne(fetch = FetchType.LAZY)
39+
@JoinColumn(name = "story_id", updatable = false, nullable = false)
40+
private Story story;
41+
42+
@Builder
43+
public Alarm(AlarmType alarmType,
44+
Profile profile,
45+
Story story) {
46+
this.alarmType = alarmType;
47+
this.profile = profile;
48+
this.story = story;
49+
}
50+
51+
public void setRead(){
52+
isRead = true;
53+
}
54+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
package everTale.everTale_be.domain.alarm.entity.Enum;
2+
3+
public enum AlarmType {
4+
EASTEREGG_LETTER, EASTEREGG_VOICE
5+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package everTale.everTale_be.domain.alarm.repository;
2+
3+
import everTale.everTale_be.domain.alarm.entity.Alarm;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
import org.springframework.data.jpa.repository.JpaRepository;
7+
8+
import java.util.Optional;
9+
10+
public interface AlarmRepository extends JpaRepository<Alarm, Long> {
11+
12+
Optional<Alarm> findByIdAndProfileId(Long id, Long profileId);
13+
14+
Page<Alarm> findByProfileId(Long profileId, Pageable pageable);
15+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package everTale.everTale_be.domain.alarm.service;
2+
3+
import everTale.everTale_be.domain.alarm.dto.response.AlarmListResponseDto;
4+
import everTale.everTale_be.domain.alarm.entity.Alarm;
5+
import everTale.everTale_be.domain.alarm.entity.Enum.AlarmType;
6+
import everTale.everTale_be.domain.alarm.repository.AlarmRepository;
7+
import everTale.everTale_be.domain.profile.entity.Profile;
8+
import everTale.everTale_be.domain.profile.util.ProfileHelper;
9+
import everTale.everTale_be.domain.story.entity.Story;
10+
import everTale.everTale_be.global.apiPayload.code.status.ErrorStatus;
11+
import everTale.everTale_be.global.apiPayload.exception.handler.UnAuthorizedHandler;
12+
import lombok.AllArgsConstructor;
13+
import org.springframework.data.domain.Page;
14+
import org.springframework.data.domain.Pageable;
15+
import org.springframework.stereotype.Service;
16+
import org.springframework.transaction.annotation.Transactional;
17+
18+
@Service
19+
@AllArgsConstructor
20+
@Transactional(readOnly = true)
21+
public class AlarmService {
22+
23+
private final AlarmRepository alarmRepository;
24+
private final ProfileHelper profileHelper;
25+
26+
@Transactional
27+
public void createAlarm(AlarmType alarmType, Profile profile, Story story){
28+
Alarm alarm = Alarm.builder()
29+
.alarmType(alarmType)
30+
.profile(profile)
31+
.story(story)
32+
.build();
33+
alarmRepository.save(alarm);
34+
}
35+
36+
public AlarmListResponseDto getAlarms(Pageable pageable){
37+
Long profileId = profileHelper.getAuthenticatedProfileId();
38+
Page<Alarm> alarms = alarmRepository.findByProfileId(profileId, pageable);
39+
return AlarmListResponseDto.from(alarms);
40+
}
41+
42+
@Transactional
43+
public void readAlarm(Long alarmId){
44+
Long profileId = profileHelper.getAuthenticatedProfileId();
45+
46+
Alarm alarm = alarmRepository.findByIdAndProfileId(alarmId, profileId)
47+
.orElseThrow(() -> new UnAuthorizedHandler(ErrorStatus.UNAUTHORIZED_PROFILE_ACCESS));
48+
alarm.setRead();
49+
}
50+
}

src/main/java/everTale/everTale_be/domain/easterEgg/dto/easterEggLetter/EasterEggLetterStoriesResponseDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package everTale.everTale_be.domain.easterEgg.dto.easterEggLetter;
22

3-
import everTale.everTale_be.domain.story.dto.StoryCollectionResponseDto;
3+
import everTale.everTale_be.domain.story.dto.response.StoryCollectionResponseDto;
44
import everTale.everTale_be.domain.story.entity.Story;
55
import io.swagger.v3.oas.annotations.media.Schema;
66
import lombok.Builder;

src/main/java/everTale/everTale_be/domain/easterEgg/dto/easterEggVoice/response/EasterEggVoiceStoriesResponseDto.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package everTale.everTale_be.domain.easterEgg.dto.easterEggVoice.response;
22

3-
import everTale.everTale_be.domain.story.dto.StoryCollectionResponseDto;
3+
import everTale.everTale_be.domain.story.dto.response.StoryCollectionResponseDto;
44
import everTale.everTale_be.domain.story.entity.Story;
55
import io.swagger.v3.oas.annotations.media.Schema;
66
import lombok.Builder;

src/main/java/everTale/everTale_be/domain/easterEgg/repository/EasterEggVoiceRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package everTale.everTale_be.domain.easterEgg.repository;
22

33
import everTale.everTale_be.domain.easterEgg.entity.EasterEggVoice;
4+
import everTale.everTale_be.domain.story.entity.Scene;
45
import everTale.everTale_be.domain.story.entity.Story;
56
import org.springframework.data.domain.Page;
67
import org.springframework.data.domain.Pageable;
@@ -16,6 +17,8 @@ public interface EasterEggVoiceRepository extends JpaRepository<EasterEggVoice,
1617

1718
Optional<EasterEggVoice> findByScene_Id(Long sceneId);
1819

20+
boolean existsByScene(Scene scene);
21+
1922
@Query(
2023
value = """
2124
SELECT DISTINCT s

0 commit comments

Comments
 (0)