From d67ef23066151d7275f83afca5563c2766801fda Mon Sep 17 00:00:00 2001 From: Sumin Hwang <163857590+tnals0924@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:45:11 +0900 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A1=B0=ED=9A=8C=C2=B7=EC=88=98=EC=A0=95=20?= =?UTF-8?q?=EB=8F=84=EB=A9=94=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/MemberNotificationSetting.java | 21 +++++++++ ...emberNotificationSettingUpdateCommand.java | 11 +++++ .../domain/NotificationErrorCode.java | 18 ++++++++ .../MemberNotificationSettingRepository.java | 10 +++++ .../MemberNotificationSettingService.java | 10 +++++ .../MemberNotificationSettingServiceImpl.java | 44 +++++++++++++++++++ ...emberNotificationSettingJpaRepository.java | 11 +++++ ...mberNotificationSettingRepositoryImpl.java | 30 +++++++++++++ 8 files changed, 155 insertions(+) create mode 100644 core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSettingUpdateCommand.java create mode 100644 core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/NotificationErrorCode.java create mode 100644 core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/repository/MemberNotificationSettingRepository.java create mode 100644 core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/MemberNotificationSettingService.java create mode 100644 core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/impl/MemberNotificationSettingServiceImpl.java create mode 100644 infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingJpaRepository.java create mode 100644 infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingRepositoryImpl.java diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSetting.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSetting.java index feaeefcf..3e0ac35a 100644 --- a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSetting.java +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSetting.java @@ -15,8 +15,14 @@ public class MemberNotificationSetting { private boolean rentalEnabled; private boolean eventEnabled; private boolean lockerEnabled; + // 게시판 알림. 새 공지와 열린피드백 답변을 함께 다룬다 private boolean noticeEnabled; + // 회원가입할 때 만든다. 처음에는 모든 알림을 받는다 + public static MemberNotificationSetting createDefault(Long memberId) { + return new MemberNotificationSetting(null, memberId, true, true, true, true); + } + public static MemberNotificationSetting of( Long id, Long memberId, @@ -27,4 +33,19 @@ public static MemberNotificationSetting of( ) { return new MemberNotificationSetting(id, memberId, rentalEnabled, eventEnabled, lockerEnabled, noticeEnabled); } + + // 바뀐 값이 있으면 true를 돌려준다 + public boolean update(MemberNotificationSettingUpdateCommand command) { + if (this.rentalEnabled == command.rentalEnabled() + && this.eventEnabled == command.eventEnabled() + && this.lockerEnabled == command.lockerEnabled() + && this.noticeEnabled == command.noticeEnabled()) { + return false; + } + this.rentalEnabled = command.rentalEnabled(); + this.eventEnabled = command.eventEnabled(); + this.lockerEnabled = command.lockerEnabled(); + this.noticeEnabled = command.noticeEnabled(); + return true; + } } diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSettingUpdateCommand.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSettingUpdateCommand.java new file mode 100644 index 00000000..e5fbdf47 --- /dev/null +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/MemberNotificationSettingUpdateCommand.java @@ -0,0 +1,11 @@ +package kr.ac.kookmin.stream.member.domain.notification.domain; + +/** + * 알림 설정 수정 요청. 전체 알림은 저장하지 않으므로 네 카테고리 값을 모두 받아 그대로 덮어쓴다. + */ +public record MemberNotificationSettingUpdateCommand( + boolean rentalEnabled, + boolean eventEnabled, + boolean lockerEnabled, + boolean noticeEnabled +) {} diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/NotificationErrorCode.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/NotificationErrorCode.java new file mode 100644 index 00000000..42281643 --- /dev/null +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/domain/NotificationErrorCode.java @@ -0,0 +1,18 @@ +package kr.ac.kookmin.stream.member.domain.notification.domain; + +import kr.ac.kookmin.stream.common.ErrorCode; +import kr.ac.kookmin.stream.common.ErrorStatus; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; + +@Getter +@Accessors(fluent = true) +@AllArgsConstructor +public enum NotificationErrorCode implements ErrorCode { + + NOTIFICATION_SETTING_NOT_FOUND(ErrorStatus.NOT_FOUND, "알림 설정을 찾을 수 없습니다."); + + private final int status; + private final String message; +} diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/repository/MemberNotificationSettingRepository.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/repository/MemberNotificationSettingRepository.java new file mode 100644 index 00000000..1daae19c --- /dev/null +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/repository/MemberNotificationSettingRepository.java @@ -0,0 +1,10 @@ +package kr.ac.kookmin.stream.member.domain.notification.repository; + +import java.util.Optional; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; + +public interface MemberNotificationSettingRepository { + Optional findByMemberId(Long memberId); + boolean existsByMemberId(Long memberId); + MemberNotificationSetting save(MemberNotificationSetting setting); +} diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/MemberNotificationSettingService.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/MemberNotificationSettingService.java new file mode 100644 index 00000000..df337c67 --- /dev/null +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/MemberNotificationSettingService.java @@ -0,0 +1,10 @@ +package kr.ac.kookmin.stream.member.domain.notification.service; + +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSettingUpdateCommand; + +public interface MemberNotificationSettingService { + MemberNotificationSetting getByMemberId(Long memberId); + void createDefaultIfAbsent(Long memberId); + MemberNotificationSetting update(Long memberId, MemberNotificationSettingUpdateCommand command); +} diff --git a/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/impl/MemberNotificationSettingServiceImpl.java b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/impl/MemberNotificationSettingServiceImpl.java new file mode 100644 index 00000000..1c4187a7 --- /dev/null +++ b/core/domain/member/src/main/java/kr/ac/kookmin/stream/member/domain/notification/service/impl/MemberNotificationSettingServiceImpl.java @@ -0,0 +1,44 @@ +package kr.ac.kookmin.stream.member.domain.notification.service.impl; + +import kr.ac.kookmin.stream.common.BusinessException; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSettingUpdateCommand; +import kr.ac.kookmin.stream.member.domain.notification.domain.NotificationErrorCode; +import kr.ac.kookmin.stream.member.domain.notification.repository.MemberNotificationSettingRepository; +import kr.ac.kookmin.stream.member.domain.notification.service.MemberNotificationSettingService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +class MemberNotificationSettingServiceImpl implements MemberNotificationSettingService { + + private final MemberNotificationSettingRepository memberNotificationSettingRepository; + + @Override + public MemberNotificationSetting getByMemberId(Long memberId) { + return memberNotificationSettingRepository.findByMemberId(memberId) + .orElseThrow(() -> new BusinessException(NotificationErrorCode.NOTIFICATION_SETTING_NOT_FOUND)); + } + + // 필수 약관이 늘어 다시 가입하는 회원은 이미 설정이 있으므로 바꾼 값을 그대로 둔다 + @Override + @Transactional + public void createDefaultIfAbsent(Long memberId) { + if (memberNotificationSettingRepository.existsByMemberId(memberId)) { + return; + } + memberNotificationSettingRepository.save(MemberNotificationSetting.createDefault(memberId)); + } + + @Override + @Transactional + public MemberNotificationSetting update(Long memberId, MemberNotificationSettingUpdateCommand command) { + MemberNotificationSetting setting = getByMemberId(memberId); + if (!setting.update(command)) { + return setting; + } + return memberNotificationSettingRepository.save(setting); + } +} diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingJpaRepository.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingJpaRepository.java new file mode 100644 index 00000000..355d8bd2 --- /dev/null +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingJpaRepository.java @@ -0,0 +1,11 @@ +package kr.ac.kookmin.stream.db.member; + +import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface MemberNotificationSettingJpaRepository extends JpaRepository { + + Optional findByMemberId(Long memberId); + + boolean existsByMemberId(Long memberId); +} diff --git a/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingRepositoryImpl.java b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingRepositoryImpl.java new file mode 100644 index 00000000..dfab9245 --- /dev/null +++ b/infrastructure/db/src/main/java/kr/ac/kookmin/stream/db/member/MemberNotificationSettingRepositoryImpl.java @@ -0,0 +1,30 @@ +package kr.ac.kookmin.stream.db.member; + +import java.util.Optional; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; +import kr.ac.kookmin.stream.member.domain.notification.repository.MemberNotificationSettingRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Repository; + +@Repository +@RequiredArgsConstructor +public class MemberNotificationSettingRepositoryImpl implements MemberNotificationSettingRepository { + + private final MemberNotificationSettingJpaRepository memberNotificationSettingJpaRepository; + + @Override + public Optional findByMemberId(Long memberId) { + return memberNotificationSettingJpaRepository.findByMemberId(memberId) + .map(MemberNotificationSettingJpaEntity::toDomain); + } + + @Override + public boolean existsByMemberId(Long memberId) { + return memberNotificationSettingJpaRepository.existsByMemberId(memberId); + } + + @Override + public MemberNotificationSetting save(MemberNotificationSetting setting) { + return memberNotificationSettingJpaRepository.save(MemberNotificationSettingJpaEntity.from(setting)).toDomain(); + } +} From 0caf409d0daf7b5e3ddfef20a7c05e4f58d6d8f5 Mon Sep 17 00:00:00 2001 From: Sumin Hwang <163857590+tnals0924@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:45:11 +0900 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EA=B8=B0=EB=B3=B8=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EC=84=A4=EC=A0=95=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../api/app/core/member/AppMemberApi.java | 3 ++- .../app/core/member/AppMemberController.java | 4 ++- .../member/usecase/MemberSignUpUseCase.java | 26 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberSignUpUseCase.java diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberApi.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberApi.java index a13b436c..c9fa9913 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberApi.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberApi.java @@ -20,7 +20,8 @@ public interface AppMemberApi { @Operation(summary = "회원가입", description = """ 로그인한 회원의 전화번호와 약관 동의 여부를 저장한다. - 전화번호는 하이픈을 넣어도 되고 빼도 된다. 필수 약관(PRIVACY_POLICY, TERMS_OF_SERVICE)에 모두 동의해야 한다.""") + 전화번호는 하이픈을 넣어도 되고 빼도 된다. 필수 약관(PRIVACY_POLICY, TERMS_OF_SERVICE)에 모두 동의해야 한다. + 가입하면 알림 설정이 모두 켜진 상태로 만들어진다.""") @ApiErrorCode(type = CommonErrorCode.class, codes = {"INVALID_INPUT"}) @ApiErrorCode(type = MemberErrorCode.class, codes = { "MEMBER_NOT_FOUND", diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberController.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberController.java index 67d0ae93..22fd2ed0 100644 --- a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberController.java +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/AppMemberController.java @@ -4,6 +4,7 @@ import kr.ac.kookmin.stream.api.app.AppApiUser; import kr.ac.kookmin.stream.api.app.core.member.request.FcmTokenUpdateRequest; import kr.ac.kookmin.stream.api.app.core.member.request.MemberSignUpRequest; +import kr.ac.kookmin.stream.api.app.core.member.usecase.MemberSignUpUseCase; import kr.ac.kookmin.stream.api.common.dto.ApiResponse; import kr.ac.kookmin.stream.member.domain.member.service.MemberService; import lombok.RequiredArgsConstructor; @@ -19,6 +20,7 @@ public class AppMemberController implements AppMemberApi { private final MemberService memberService; + private final MemberSignUpUseCase memberSignUpUseCase; @Override @PostMapping("/sign-up") @@ -26,7 +28,7 @@ public ApiResponse signUp( AppApiUser apiUser, @Valid @RequestBody MemberSignUpRequest request ) { - memberService.signUp(apiUser.userId(), request.toCommand()); + memberSignUpUseCase.signUp(apiUser.userId(), request.toCommand()); return ApiResponse.success(); } diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberSignUpUseCase.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberSignUpUseCase.java new file mode 100644 index 00000000..9762661d --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/member/usecase/MemberSignUpUseCase.java @@ -0,0 +1,26 @@ +package kr.ac.kookmin.stream.api.app.core.member.usecase; + +import kr.ac.kookmin.stream.member.domain.member.domain.MemberSignUpCommand; +import kr.ac.kookmin.stream.member.domain.member.service.MemberService; +import kr.ac.kookmin.stream.member.domain.notification.service.MemberNotificationSettingService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +/** + * 회원가입. {@code member}(전화번호, 약관 동의)와 {@code notification}(기본 알림 설정) 두 도메인을 조합한다. + */ +@Component +@RequiredArgsConstructor +public class MemberSignUpUseCase { + + private final MemberService memberService; + private final MemberNotificationSettingService memberNotificationSettingService; + + // 가입을 마친 회원에게는 항상 알림 설정이 있도록 한 트랜잭션으로 묶는다 + @Transactional + public void signUp(Long memberId, MemberSignUpCommand command) { + memberService.signUp(memberId, command); + memberNotificationSettingService.createDefaultIfAbsent(memberId); + } +} From b3c79c6e7d8dd9a7db26aed741c55f6bce80e35e Mon Sep 17 00:00:00 2001 From: Sumin Hwang <163857590+tnals0924@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:45:12 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EA=B8=B0=EC=A1=B4=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=20=EA=B8=B0=EB=B3=B8=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...7__insert_default_member_notification_settings.sql | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 infrastructure/db/src/main/resources/db/migration/V17__insert_default_member_notification_settings.sql diff --git a/infrastructure/db/src/main/resources/db/migration/V17__insert_default_member_notification_settings.sql b/infrastructure/db/src/main/resources/db/migration/V17__insert_default_member_notification_settings.sql new file mode 100644 index 00000000..79082f5f --- /dev/null +++ b/infrastructure/db/src/main/resources/db/migration/V17__insert_default_member_notification_settings.sql @@ -0,0 +1,11 @@ +-- 알림 설정은 회원가입할 때 만든다. 그 전에 들어온 회원에게도 기본 설정(모두 켜짐)을 만들어 둔다. +-- 아직 가입을 마치지 않은 회원도 포함한다. 나중에 가입하면 이미 있는 설정을 그대로 쓴다. +INSERT INTO member_notification_settings (member_id) +SELECT m.member_id +FROM members m +WHERE m.is_deleted = 0 + AND NOT EXISTS ( + SELECT 1 + FROM member_notification_settings s + WHERE s.member_id = m.member_id + ); From 68b1a8b46237c7b9686f1d7d5ec6f104e37f4911 Mon Sep 17 00:00:00 2001 From: Sumin Hwang <163857590+tnals0924@users.noreply.github.com> Date: Tue, 13 Oct 2026 18:45:12 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=EC=95=8C=EB=A6=BC=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95=20=EC=A1=B0=ED=9A=8C=C2=B7=EC=88=98=EC=A0=95=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/notification/AppNotificationApi.java | 40 +++++++++++++++++++ .../AppNotificationController.java | 40 +++++++++++++++++++ ...emberNotificationSettingUpdateRequest.java | 23 +++++++++++ .../MemberNotificationSettingResponse.java | 19 +++++++++ 4 files changed, 122 insertions(+) create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationApi.java create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationController.java create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/request/MemberNotificationSettingUpdateRequest.java create mode 100644 api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/response/MemberNotificationSettingResponse.java diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationApi.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationApi.java new file mode 100644 index 00000000..21a245b6 --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationApi.java @@ -0,0 +1,40 @@ +package kr.ac.kookmin.stream.api.app.core.notification; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.ac.kookmin.stream.api.app.AppApiUser; +import kr.ac.kookmin.stream.api.app.core.notification.request.MemberNotificationSettingUpdateRequest; +import kr.ac.kookmin.stream.api.app.core.notification.response.MemberNotificationSettingResponse; +import kr.ac.kookmin.stream.api.common.dto.ApiResponse; +import kr.ac.kookmin.stream.api.common.openapi.ApiErrorCode; +import kr.ac.kookmin.stream.common.CommonErrorCode; +import kr.ac.kookmin.stream.member.domain.notification.domain.NotificationErrorCode; + +/** + * 학생 앱 알림 API의 문서 명세. 구현은 {@link AppNotificationController}가 맡는다. + */ +@Tag(name = "알림", description = "학생 앱 알림 설정") +public interface AppNotificationApi { + + /** 알림 설정 조회. */ + @Operation(summary = "알림 설정 조회", + description = """ + 로그인한 회원의 카테고리별 푸시 수신 여부를 조회한다. + rentalEnabled는 빌릴게, eventEnabled는 행사, lockerEnabled는 사물함, noticeEnabled는 게시판(새 공지·열린피드백 답변) 알림이다. + 전체 알림은 따로 저장하지 않는다. 네 값이 모두 true일 때 켜진 것으로 보여준다.""") + @ApiErrorCode(type = NotificationErrorCode.class, codes = {"NOTIFICATION_SETTING_NOT_FOUND"}) + ApiResponse getSetting(AppApiUser apiUser); + + /** 알림 설정 수정. */ + @Operation(summary = "알림 설정 수정", + description = """ + 로그인한 회원의 카테고리별 푸시 수신 여부를 바꾼다. 네 값을 모두 보내야 하고, 보낸 값으로 덮어쓴다. + 전체 알림을 켜거나 끌 때는 네 값을 모두 true나 false로 보낸다. + 꺼진 카테고리의 알림은 푸시로 보내지 않지만 알림함에는 남는다.""") + @ApiErrorCode(type = CommonErrorCode.class, codes = {"INVALID_INPUT"}) + @ApiErrorCode(type = NotificationErrorCode.class, codes = {"NOTIFICATION_SETTING_NOT_FOUND"}) + ApiResponse updateSetting( + AppApiUser apiUser, + MemberNotificationSettingUpdateRequest request + ); +} diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationController.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationController.java new file mode 100644 index 00000000..b1288a53 --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/AppNotificationController.java @@ -0,0 +1,40 @@ +package kr.ac.kookmin.stream.api.app.core.notification; + +import jakarta.validation.Valid; +import kr.ac.kookmin.stream.api.app.AppApiUser; +import kr.ac.kookmin.stream.api.app.core.notification.request.MemberNotificationSettingUpdateRequest; +import kr.ac.kookmin.stream.api.app.core.notification.response.MemberNotificationSettingResponse; +import kr.ac.kookmin.stream.api.common.dto.ApiResponse; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; +import kr.ac.kookmin.stream.member.domain.notification.service.MemberNotificationSettingService; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/v1/app/notifications") +@RequiredArgsConstructor +public class AppNotificationController implements AppNotificationApi { + + private final MemberNotificationSettingService memberNotificationSettingService; + + @Override + @GetMapping("/settings") + public ApiResponse getSetting(AppApiUser apiUser) { + MemberNotificationSetting setting = memberNotificationSettingService.getByMemberId(apiUser.userId()); + return ApiResponse.success(MemberNotificationSettingResponse.from(setting)); + } + + @Override + @PutMapping("/settings") + public ApiResponse updateSetting( + AppApiUser apiUser, + @Valid @RequestBody MemberNotificationSettingUpdateRequest request + ) { + MemberNotificationSetting setting = memberNotificationSettingService.update(apiUser.userId(), request.toCommand()); + return ApiResponse.success(MemberNotificationSettingResponse.from(setting)); + } +} diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/request/MemberNotificationSettingUpdateRequest.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/request/MemberNotificationSettingUpdateRequest.java new file mode 100644 index 00000000..14aedd8c --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/request/MemberNotificationSettingUpdateRequest.java @@ -0,0 +1,23 @@ +package kr.ac.kookmin.stream.api.app.core.notification.request; + +import jakarta.validation.constraints.NotNull; +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSettingUpdateCommand; + +public record MemberNotificationSettingUpdateRequest( + @NotNull(message = "빌릴게 알림 수신 여부를 입력해 주세요.") + Boolean rentalEnabled, + + @NotNull(message = "행사 알림 수신 여부를 입력해 주세요.") + Boolean eventEnabled, + + @NotNull(message = "사물함 알림 수신 여부를 입력해 주세요.") + Boolean lockerEnabled, + + @NotNull(message = "게시판 알림 수신 여부를 입력해 주세요.") + Boolean noticeEnabled +) { + + public MemberNotificationSettingUpdateCommand toCommand() { + return new MemberNotificationSettingUpdateCommand(rentalEnabled, eventEnabled, lockerEnabled, noticeEnabled); + } +} diff --git a/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/response/MemberNotificationSettingResponse.java b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/response/MemberNotificationSettingResponse.java new file mode 100644 index 00000000..7c46782c --- /dev/null +++ b/api/app-api/src/main/java/kr/ac/kookmin/stream/api/app/core/notification/response/MemberNotificationSettingResponse.java @@ -0,0 +1,19 @@ +package kr.ac.kookmin.stream.api.app.core.notification.response; + +import kr.ac.kookmin.stream.member.domain.notification.domain.MemberNotificationSetting; + +public record MemberNotificationSettingResponse( + boolean rentalEnabled, + boolean eventEnabled, + boolean lockerEnabled, + boolean noticeEnabled +) { + public static MemberNotificationSettingResponse from(MemberNotificationSetting setting) { + return new MemberNotificationSettingResponse( + setting.isRentalEnabled(), + setting.isEventEnabled(), + setting.isLockerEnabled(), + setting.isNoticeEnabled() + ); + } +}