From 3f1ba15ceb34cc14898da8d9e6762a31669efd96 Mon Sep 17 00:00:00 2001 From: minsu20 Date: Mon, 2 Oct 2023 22:37:10 +0900 Subject: [PATCH 01/11] =?UTF-8?q?fix:=20=EC=86=8C=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=ED=83=88=ED=87=B4=20=ED=9B=84=20=ED=9A=8C=EC=9B=90=20=EB=8B=89?= =?UTF-8?q?=EB=84=A4=EC=9E=84=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/application/dto/response/BoardBlocks.java | 13 ++++++++++++- .../repository/BoardCustomRepositoryImpl.java | 5 +++-- .../application/dto/response/CommentBlocks.java | 7 +++++++ .../BoardCommentCustomRepositoryImpl.java | 11 +++++------ .../backend/domain/member/domain/entity/Member.java | 9 ++++++++- .../mypage/application/service/SignOutUserCase.java | 3 +++ .../application/service/DisbandTeamUserCase.java | 5 +++++ .../application/service/WithdrawTeamUserCase.java | 2 +- .../domain/teamMember/domain/entity/TeamMember.java | 2 +- .../board/presentation/BoardControllerTest.java | 4 ++++ .../presentation/BoardCommentControllerTest.java | 3 +++ .../representation/MissionControllerTest.java | 5 +++++ 12 files changed, 57 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/moing/backend/domain/board/application/dto/response/BoardBlocks.java b/src/main/java/com/moing/backend/domain/board/application/dto/response/BoardBlocks.java index eb878fb8..4c06d3c0 100644 --- a/src/main/java/com/moing/backend/domain/board/application/dto/response/BoardBlocks.java +++ b/src/main/java/com/moing/backend/domain/board/application/dto/response/BoardBlocks.java @@ -26,8 +26,10 @@ public class BoardBlocks { private Boolean isRead; + private Boolean writerIsDeleted; + @QueryProjection - public BoardBlocks(Long boardId, String writerNickName, Boolean writerIsLeader, String writerProfileImage, String title, String content, Integer commentNum) { + public BoardBlocks(Long boardId, String writerNickName, Boolean writerIsLeader, String writerProfileImage, String title, String content, Integer commentNum, Boolean writerIsDeleted) { this.boardId = boardId; this.writerNickName = writerNickName; this.writerIsLeader = writerIsLeader; @@ -36,9 +38,18 @@ public BoardBlocks(Long boardId, String writerNickName, Boolean writerIsLeader, this.content = content; this.commentNum = commentNum; this.isRead = false; + this.writerIsDeleted=writerIsDeleted; + deleteMember(); } public void readBoard() { this.isRead = true; } + + public void deleteMember() { + if(Boolean.TRUE.equals(writerIsDeleted)) { + this.writerNickName = "(알 수 없음)"; + this.writerProfileImage = "undef"; + } + } } diff --git a/src/main/java/com/moing/backend/domain/board/domain/repository/BoardCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/board/domain/repository/BoardCustomRepositoryImpl.java index b1b0e863..2532ea09 100644 --- a/src/main/java/com/moing/backend/domain/board/domain/repository/BoardCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/board/domain/repository/BoardCustomRepositoryImpl.java @@ -13,7 +13,7 @@ import static com.moing.backend.domain.board.domain.entity.QBoard.board; import static com.moing.backend.domain.boardRead.domain.entity.QBoardRead.boardRead; -public class BoardCustomRepositoryImpl implements BoardCustomRepository { +public class BoardCustomRepositoryImpl implements BoardCustomRepository { private final JPAQueryFactory queryFactory; public BoardCustomRepositoryImpl(EntityManager em) { @@ -51,7 +51,8 @@ public GetAllBoardResponse findBoardAll(Long teamId, Long memberId) { b.getWriterProfileImage(), b.getTitle(), b.getContent(), - b.getCommentNum() + b.getCommentNum(), + b.getTeamMember().isDeleted() ); if (isRead) { boardBlocks.readBoard(); diff --git a/src/main/java/com/moing/backend/domain/boardComment/application/dto/response/CommentBlocks.java b/src/main/java/com/moing/backend/domain/boardComment/application/dto/response/CommentBlocks.java index 37e40ed0..8a24f5f9 100644 --- a/src/main/java/com/moing/backend/domain/boardComment/application/dto/response/CommentBlocks.java +++ b/src/main/java/com/moing/backend/domain/boardComment/application/dto/response/CommentBlocks.java @@ -21,4 +21,11 @@ public class CommentBlocks { private String writerProfileImage; private Boolean isWriter; + + private Boolean writerIsDeleted; + + public void deleteMember(){ + this.writerNickName="(알 수 없음)"; + this.writerProfileImage="undef"; + } } diff --git a/src/main/java/com/moing/backend/domain/boardComment/domain/repository/BoardCommentCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/boardComment/domain/repository/BoardCommentCustomRepositoryImpl.java index 178a26ea..f31d6e11 100644 --- a/src/main/java/com/moing/backend/domain/boardComment/domain/repository/BoardCommentCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/boardComment/domain/repository/BoardCommentCustomRepositoryImpl.java @@ -1,11 +1,7 @@ package com.moing.backend.domain.boardComment.domain.repository; -import com.moing.backend.domain.board.domain.entity.Board; import com.moing.backend.domain.boardComment.application.dto.response.CommentBlocks; import com.moing.backend.domain.boardComment.application.dto.response.GetBoardCommentResponse; -import com.moing.backend.domain.boardComment.application.service.GetBoardCommentUserCase; -import com.moing.backend.domain.boardComment.domain.entity.BoardComment; -import com.moing.backend.domain.boardComment.domain.entity.QBoardComment; import com.moing.backend.domain.teamMember.domain.entity.QTeamMember; import com.moing.backend.domain.teamMember.domain.entity.TeamMember; import com.querydsl.core.types.ExpressionUtils; @@ -17,7 +13,6 @@ import java.util.List; import static com.moing.backend.domain.boardComment.domain.entity.QBoardComment.boardComment; -import static com.moing.backend.domain.teamMember.domain.entity.QTeamMember.teamMember; public class BoardCommentCustomRepositoryImpl implements BoardCommentCustomRepository{ @@ -42,11 +37,15 @@ public GetBoardCommentResponse findBoardCommentAll(Long boardId, TeamMember team .from(QTeamMember.teamMember) .where(QTeamMember.teamMember.eq(teamMember) .and(QTeamMember.teamMember.eq(boardComment.teamMember))) - .exists(), "isWriter"))) + .exists(), "isWriter"), + boardComment.teamMember.isDeleted)) .from(boardComment) .where(boardComment.board.boardId.eq(boardId)) .orderBy(boardComment.createdDate.asc()) .fetch(); + + commentBlocks.forEach(CommentBlocks::deleteMember); + return new GetBoardCommentResponse(commentBlocks); } } diff --git a/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java b/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java index 465f4a4f..b8990e7c 100644 --- a/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java +++ b/src/main/java/com/moing/backend/domain/member/domain/entity/Member.java @@ -79,7 +79,7 @@ public class Member extends BaseTimeEntity { @ColumnDefault("true") private boolean isFirePush; - @OneToMany(mappedBy = "member", cascade = CascadeType.ALL) + @OneToMany(mappedBy = "member") private List teamMembers = new ArrayList<>(); //최대 3개이므로 양방향 //==생성 메서드==// @@ -168,4 +168,11 @@ public Member(String ageRange, String email, String fcmToken, String gender, Str this.socialId = socialId; } + public void deleteTeamMember(){ + List teamMemberList=this.getTeamMembers(); + for(TeamMember teamMember:teamMemberList){ + teamMember.deleteMember(); + } + } + } diff --git a/src/main/java/com/moing/backend/domain/mypage/application/service/SignOutUserCase.java b/src/main/java/com/moing/backend/domain/mypage/application/service/SignOutUserCase.java index abce0334..cbb14594 100644 --- a/src/main/java/com/moing/backend/domain/mypage/application/service/SignOutUserCase.java +++ b/src/main/java/com/moing/backend/domain/mypage/application/service/SignOutUserCase.java @@ -2,6 +2,7 @@ import com.moing.backend.domain.member.domain.entity.Member; import com.moing.backend.domain.member.domain.service.MemberGetService; +import com.moing.backend.domain.teamMember.domain.entity.TeamMember; import com.moing.backend.global.config.security.jwt.TokenUtil; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -11,8 +12,10 @@ public class SignOutUserCase { private final TokenUtil tokenUtil; + private final MemberGetService memberGetService; public void signOut(String socialId){ + //TODO: teamMember가 isDeleted가 아닌게 한개라도 있으면 error 반환 tokenUtil.expireRefreshToken(socialId); } } diff --git a/src/main/java/com/moing/backend/domain/team/application/service/DisbandTeamUserCase.java b/src/main/java/com/moing/backend/domain/team/application/service/DisbandTeamUserCase.java index 19e133ad..77ef8c86 100644 --- a/src/main/java/com/moing/backend/domain/team/application/service/DisbandTeamUserCase.java +++ b/src/main/java/com/moing/backend/domain/team/application/service/DisbandTeamUserCase.java @@ -6,6 +6,8 @@ import com.moing.backend.domain.team.domain.entity.Team; import com.moing.backend.domain.team.domain.service.TeamGetService; import com.moing.backend.domain.team.exception.NotAuthByTeamException; +import com.moing.backend.domain.teamMember.domain.entity.TeamMember; +import com.moing.backend.domain.teamMember.domain.service.TeamMemberGetService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -19,11 +21,14 @@ public class DisbandTeamUserCase { private final MemberGetService memberGetService; private final TeamGetService teamGetService; private final CheckLeaderUserCase checkLeaderUserCase; + private final TeamMemberGetService teamMemberGetService; public DeleteTeamResponse disbandTeam(String socialId, Long teamId) { Member member = memberGetService.getMemberBySocialId(socialId); Team team = teamGetService.getTeamByTeamId(teamId); + if (checkLeaderUserCase.isTeamLeader(member, team)) { + //TODO: 팀의 모든 멤버 isDeleted true 해주환 team.deleteTeam(); } else { throw new NotAuthByTeamException(); diff --git a/src/main/java/com/moing/backend/domain/team/application/service/WithdrawTeamUserCase.java b/src/main/java/com/moing/backend/domain/team/application/service/WithdrawTeamUserCase.java index 60c15d3c..46c42dfa 100644 --- a/src/main/java/com/moing/backend/domain/team/application/service/WithdrawTeamUserCase.java +++ b/src/main/java/com/moing/backend/domain/team/application/service/WithdrawTeamUserCase.java @@ -25,7 +25,7 @@ public DeleteTeamResponse withdrawTeam(String socialId, Long teamId) { Member member = memberGetService.getMemberBySocialId(socialId); Team team = teamGetService.getTeamByTeamId(teamId); TeamMember teamMember = teamMemberGetService.getTeamMember(member, team); - teamMember.withdrawTeam(); + teamMember.deleteMember(); return new DeleteTeamResponse(team.getTeamId()); } } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/entity/TeamMember.java b/src/main/java/com/moing/backend/domain/teamMember/domain/entity/TeamMember.java index 1acc968a..c30cf2ed 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/entity/TeamMember.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/entity/TeamMember.java @@ -42,7 +42,7 @@ public void updateMember(Member member) { member.getTeamMembers().add(this); } - public void withdrawTeam() { + public void deleteMember() { this.isDeleted = true; } } diff --git a/src/test/java/com/moing/backend/domain/board/presentation/BoardControllerTest.java b/src/test/java/com/moing/backend/domain/board/presentation/BoardControllerTest.java index 1c76633c..000a3f04 100644 --- a/src/test/java/com/moing/backend/domain/board/presentation/BoardControllerTest.java +++ b/src/test/java/com/moing/backend/domain/board/presentation/BoardControllerTest.java @@ -193,6 +193,7 @@ public void get_board_all() throws Exception { .boardId(1L) .writerIsLeader(true) .writerNickName("작성자 닉네임") + .writerIsDeleted(false) .writerProfileImage("작성자 프로필 이미지") .title("공지 제목") .content("공지 내용") @@ -204,6 +205,7 @@ public void get_board_all() throws Exception { .boardId(1L) .writerIsLeader(true) .writerNickName("작성자 닉네임") + .writerIsDeleted(false) .writerProfileImage("작성자 프로필 이미지") .title("게시글 제목") .content("게시글 내용") @@ -246,6 +248,7 @@ public void get_board_all() throws Exception { fieldWithPath("data.noticeBlocks[0].writerNickName").description("작성자 닉네임"), fieldWithPath("data.noticeBlocks[0].writerIsLeader").description("작성자 소모임장 여부"), fieldWithPath("data.noticeBlocks[0].writerProfileImage").description("작성자 프로필 이미지"), + fieldWithPath("data.noticeBlocks[0].writerIsDeleted").description("작성자 삭제 여부"), fieldWithPath("data.noticeBlocks[0].title").description("공지 제목"), fieldWithPath("data.noticeBlocks[0].content").description("공지 내용"), fieldWithPath("data.noticeBlocks[0].commentNum").description("공지 댓글 개수"), @@ -255,6 +258,7 @@ public void get_board_all() throws Exception { fieldWithPath("data.notNoticeBlocks[0].writerNickName").description("작성자 닉네임"), fieldWithPath("data.notNoticeBlocks[0].writerIsLeader").description("작성자 소모임장 여부"), fieldWithPath("data.notNoticeBlocks[0].writerProfileImage").description("작성자 프로필 이미지"), + fieldWithPath("data.notNoticeBlocks[0].writerIsDeleted").description("작성자 삭제 여부"), fieldWithPath("data.notNoticeBlocks[0].title").description("일반 게시글 제목"), fieldWithPath("data.notNoticeBlocks[0].content").description("일반 게시글 내용"), fieldWithPath("data.notNoticeBlocks[0].commentNum").description("일반 게시글 댓글 개수"), diff --git a/src/test/java/com/moing/backend/domain/boardComment/presentation/BoardCommentControllerTest.java b/src/test/java/com/moing/backend/domain/boardComment/presentation/BoardCommentControllerTest.java index c3592fdb..846fd443 100644 --- a/src/test/java/com/moing/backend/domain/boardComment/presentation/BoardCommentControllerTest.java +++ b/src/test/java/com/moing/backend/domain/boardComment/presentation/BoardCommentControllerTest.java @@ -30,6 +30,7 @@ @WebMvcTest(BoardCommentController.class) public class BoardCommentControllerTest extends CommonControllerTest { + @MockBean private CreateBoardCommentUserCase createBoardCommentUserCase; @MockBean @@ -139,6 +140,7 @@ public void get_board_comment_all() throws Exception { .writerIsLeader(true) .writerNickName("작성자 닉네임") .writerProfileImage("작성자 프로필 이미지") + .writerIsDeleted(false) .isWriter(true) .build(); @@ -177,6 +179,7 @@ public void get_board_comment_all() throws Exception { fieldWithPath("data.commentBlocks[0].writerIsLeader").description("작성자 소모임장 여부"), fieldWithPath("data.commentBlocks[0].writerNickName").description("작성자 닉네임"), fieldWithPath("data.commentBlocks[0].writerProfileImage").description("작성자 프로필 이미지"), + fieldWithPath("data.commentBlocks[0].writerIsDeleted").description("작성자 삭제 여부"), fieldWithPath("data.commentBlocks[0].isWriter").description("댓글 작성자 여부") ) diff --git a/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java b/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java index 8ac524c7..6b7424de 100644 --- a/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java +++ b/src/test/java/com/moing/backend/domain/mission/representation/MissionControllerTest.java @@ -9,8 +9,10 @@ import com.moing.backend.domain.mission.application.service.MissionReadUseCase; import com.moing.backend.domain.mission.application.service.MissionUpdateUseCase; import com.moing.backend.domain.mission.domain.repository.MissionRepository; +import com.moing.backend.domain.mission.domain.service.MissionQueryService; import com.moing.backend.domain.mission.presentation.MissionController; import org.junit.jupiter.api.Test; +import org.mockito.Mock; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -47,6 +49,9 @@ public class MissionControllerTest extends CommonControllerTest { @MockBean private MissionDeleteUseCase missionDeleteUseCase; + @MockBean + private MissionQueryService missionQueryService; + @Test From ba5c6ce0564e13e6daddc00b5ca63806c093d0b6 Mon Sep 17 00:00:00 2001 From: minsu20 Date: Wed, 4 Oct 2023 22:29:56 +0900 Subject: [PATCH 02/11] =?UTF-8?q?feat:=20fcm=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../backend/global/config/fcm/FcmConfig.java | 59 +++++++ .../config/fcm/dto/request/MultiRequest.java | 23 +++ .../config/fcm/dto/request/SingleRequest.java | 21 +++ .../fcm/dto/response/MultiResponse.java | 14 ++ .../fcm/dto/response/SingleResponse.java | 11 ++ .../fcm/exception/FirebaseException.java | 11 ++ .../fcm/exception/InitializeException.java | 12 ++ .../fcm/exception/MessagingException.java | 11 ++ .../fcm/exception/NotificationException.java | 11 ++ .../global/config/fcm/service/FcmService.java | 151 ++++++++++++++++++ .../backend/global/response/ErrorCode.java | 5 + 11 files changed, 329 insertions(+) create mode 100644 src/main/java/com/moing/backend/global/config/fcm/FcmConfig.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/dto/request/MultiRequest.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/dto/request/SingleRequest.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/dto/response/MultiResponse.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/dto/response/SingleResponse.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/exception/FirebaseException.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/exception/InitializeException.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/exception/MessagingException.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/exception/NotificationException.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/service/FcmService.java diff --git a/src/main/java/com/moing/backend/global/config/fcm/FcmConfig.java b/src/main/java/com/moing/backend/global/config/fcm/FcmConfig.java new file mode 100644 index 00000000..711c4290 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/FcmConfig.java @@ -0,0 +1,59 @@ +package com.moing.backend.global.config.fcm; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import com.google.firebase.messaging.FirebaseMessaging; +import com.moing.backend.global.config.fcm.exception.InitializeException; +import com.moing.backend.global.config.fcm.exception.MessagingException; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +@Configuration +@Slf4j +public class FcmConfig { + + + @Value("${firebase.config.path}") + private String firebaseConfigPath; + + @Value("${firebase.config.projectId}") + private String projectId; + + @Bean + public FirebaseApp firebaseApp() { + try { + FileInputStream serviceAccount = new FileInputStream(firebaseConfigPath); + + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .setProjectId(projectId) + .build(); + + return FirebaseApp.initializeApp(options); + } catch (FileNotFoundException e) { + throw new IllegalStateException("파일을 찾을 수 없습니다." + e.getMessage()); + } catch (IOException e) { + throw new InitializeException(); + } + } + + @Bean + public FirebaseMessaging firebaseMessaging() { + try { + return FirebaseMessaging.getInstance(firebaseApp()); + } catch (IllegalStateException e) { + throw new MessagingException("FirebaseApp 초기화에 실패하였습니다." + e.getMessage()); + } catch (NullPointerException e) { + throw new IllegalStateException("FirebaseApp을 불러오는데 실패하였습니다." + e.getMessage()); + } catch (Exception e) { + throw new IllegalArgumentException("firebaseConfigPath를 읽어오는데 실패하였습니다." + e.getMessage()); + } + } +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/dto/request/MultiRequest.java b/src/main/java/com/moing/backend/global/config/fcm/dto/request/MultiRequest.java new file mode 100644 index 00000000..222e1258 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/dto/request/MultiRequest.java @@ -0,0 +1,23 @@ +package com.moing.backend.global.config.fcm.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class MultiRequest { + @NotNull(message = "기기 등록 토큰들을 입력해 주세요.") + private List registrationToken; + + @NotNull(message = "알림 제목을 입력해 주세요.") + private String title; + + @NotNull(message = "알림 내용을 입력해 주세요.") + private String body; + +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/dto/request/SingleRequest.java b/src/main/java/com/moing/backend/global/config/fcm/dto/request/SingleRequest.java new file mode 100644 index 00000000..01f2a37a --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/dto/request/SingleRequest.java @@ -0,0 +1,21 @@ +package com.moing.backend.global.config.fcm.dto.request; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.constraints.NotNull; + +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class SingleRequest { + @NotNull(message = "기기 등록 토큰을 입력해 주세요.") + private String registrationToken; + + @NotNull(message = "알림 제목을 입력해 주세요.") + private String title; + + @NotNull(message = "알림 내용을 입력해 주세요.") + private String body; +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/dto/response/MultiResponse.java b/src/main/java/com/moing/backend/global/config/fcm/dto/response/MultiResponse.java new file mode 100644 index 00000000..39f67767 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/dto/response/MultiResponse.java @@ -0,0 +1,14 @@ +package com.moing.backend.global.config.fcm.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.util.List; + +@Getter +@AllArgsConstructor +public class MultiResponse { + private final String response; + private final List failedTokens; + +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/global/config/fcm/dto/response/SingleResponse.java b/src/main/java/com/moing/backend/global/config/fcm/dto/response/SingleResponse.java new file mode 100644 index 00000000..b4f0cbf9 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/dto/response/SingleResponse.java @@ -0,0 +1,11 @@ +package com.moing.backend.global.config.fcm.dto.response; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@Getter +@AllArgsConstructor +public class SingleResponse { + private final String response; + +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/exception/FirebaseException.java b/src/main/java/com/moing/backend/global/config/fcm/exception/FirebaseException.java new file mode 100644 index 00000000..11c67de3 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/exception/FirebaseException.java @@ -0,0 +1,11 @@ +package com.moing.backend.global.config.fcm.exception; + +import com.moing.backend.global.exception.ApplicationException; +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public abstract class FirebaseException extends ApplicationException { + protected FirebaseException(ErrorCode errorCode, HttpStatus httpStatus) { + super(errorCode, httpStatus); + } +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/exception/InitializeException.java b/src/main/java/com/moing/backend/global/config/fcm/exception/InitializeException.java new file mode 100644 index 00000000..34b560ca --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/exception/InitializeException.java @@ -0,0 +1,12 @@ +package com.moing.backend.global.config.fcm.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class InitializeException extends FirebaseException { + public InitializeException() { + super(ErrorCode.INITIALIZE_ERROR, + HttpStatus.INTERNAL_SERVER_ERROR + ); + } +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/exception/MessagingException.java b/src/main/java/com/moing/backend/global/config/fcm/exception/MessagingException.java new file mode 100644 index 00000000..0acc476b --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/exception/MessagingException.java @@ -0,0 +1,11 @@ +package com.moing.backend.global.config.fcm.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class MessagingException extends FirebaseException { + public MessagingException(String message) { + super(ErrorCode.MESSAGING_ERROR, + HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/exception/NotificationException.java b/src/main/java/com/moing/backend/global/config/fcm/exception/NotificationException.java new file mode 100644 index 00000000..b7dd8a90 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/exception/NotificationException.java @@ -0,0 +1,11 @@ +package com.moing.backend.global.config.fcm.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class NotificationException extends FirebaseException { + public NotificationException(String message) { + super(ErrorCode.NOTIFICATION_ERROR, + HttpStatus.INTERNAL_SERVER_ERROR); + } +} diff --git a/src/main/java/com/moing/backend/global/config/fcm/service/FcmService.java b/src/main/java/com/moing/backend/global/config/fcm/service/FcmService.java new file mode 100644 index 00000000..74cdb54d --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/service/FcmService.java @@ -0,0 +1,151 @@ +package com.moing.backend.global.config.fcm.service; + +import com.google.firebase.messaging.*; +import com.moing.backend.global.config.fcm.dto.request.MultiRequest; +import com.moing.backend.global.config.fcm.dto.request.SingleRequest; +import com.moing.backend.global.config.fcm.dto.response.MultiResponse; +import com.moing.backend.global.config.fcm.dto.response.SingleResponse; +import com.moing.backend.global.config.fcm.exception.NotificationException; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.ArrayList; +import java.util.List; + +@Service +@Transactional +@RequiredArgsConstructor +@Slf4j +public class FcmService { + + private final FirebaseMessaging firebaseMessaging; + + + public SingleResponse sendSingleDevice(SingleRequest toSingleRequest) { + + Notification notification = Notification.builder() + .setTitle(toSingleRequest.getTitle()) + .setBody(toSingleRequest.getBody()) + .build(); + + // Android Configuration + AndroidConfig androidConfig = AndroidConfig.builder() + .setPriority(AndroidConfig.Priority.HIGH) + .setNotification(AndroidNotification.builder() + .setChannelId("FCM_Channel") + .setTitle(toSingleRequest.getTitle()) + .setBody(toSingleRequest.getBody()) + .build()) + .build(); + + // APNs Configuration + ApnsConfig apnsConfig = ApnsConfig.builder() + .setAps(Aps.builder() + .setCategory("YOUR_CATEGORY") // Replace with your category + .setAlert(ApsAlert.builder() + .setTitle(toSingleRequest.getTitle()) + .setBody(toSingleRequest.getBody()) + .build()) + .build()) + .build(); + + Message message = Message.builder() + .setToken(toSingleRequest.getRegistrationToken()) + .setNotification(notification) + .setAndroidConfig(androidConfig) // Applying Android configuration + .setApnsConfig(apnsConfig) // Applying APNs configuration + .build(); + + try { + String response = firebaseMessaging.send(message); + return new SingleResponse(response); + } catch (FirebaseMessagingException e) { + throw handleException(e); + } + } + + + public MultiResponse sendMultipleDevices(MultiRequest toMultiRequest) { + + Notification notification = Notification.builder() + .setTitle(toMultiRequest.getTitle()) + .setBody(toMultiRequest.getBody()) + .build(); + + // Android Configuration + AndroidConfig androidConfig = AndroidConfig.builder() + .setPriority(AndroidConfig.Priority.HIGH) + .setNotification(AndroidNotification.builder() + .setChannelId("FCM_Channel") + .setTitle(toMultiRequest.getTitle()) + .setBody(toMultiRequest.getBody()) + .build()) + .build(); + + // APNs Configuration + ApnsConfig apnsConfig = ApnsConfig.builder() + .setAps(Aps.builder() + .setCategory("YOUR_CATEGORY") // Replace with your category + .setAlert(ApsAlert.builder() + .setTitle(toMultiRequest.getTitle()) + .setBody(toMultiRequest.getBody()) + .build()) + .build()) + .build(); + + MulticastMessage message = MulticastMessage.builder() + .addAllTokens(toMultiRequest.getRegistrationToken()) + .setNotification(notification) + .setAndroidConfig(androidConfig) // Applying Android configuration + .setApnsConfig(apnsConfig) // Applying APNs configuration + .build(); + + try { + BatchResponse response = firebaseMessaging.sendMulticast(message); + List failedTokens = new ArrayList<>(); + + if (response.getFailureCount() > 0) { + List responses = response.getResponses(); + + for (int i = 0; i < responses.size(); i++) { + if (!responses.get(i).isSuccessful()) { + // Add the failed tokens to the list + failedTokens.add(toMultiRequest.getRegistrationToken().get(i)); + } + } + } + + String messageString = String.format("%d messages were sent successfully.", response.getSuccessCount()); + + return new MultiResponse(messageString, failedTokens); + + } catch (FirebaseMessagingException e) { + throw handleException(e); + } + } + + + + private NotificationException handleException(FirebaseMessagingException e) { + String errorCode = e.getErrorCode().name(); + String errorMessage = e.getMessage(); + + switch (errorCode) { + case "INVALID_ARGUMENT": + return new NotificationException("올바르지 않은 인자 값입니다: " + errorMessage); + case "NOT_FOUND": + return new NotificationException("등록 토큰이 유효하지 않거나, 주제(Topic)가 존재하지 않습니다: " + errorMessage); + case "UNREGISTERED": + return new NotificationException("해당 주제(Topic)의 구독이 해지되었습니다: " + errorMessage); + case "UNAVAILABLE": + return new NotificationException("서비스를 사용할 수 없습니다: " + errorMessage); + default: + return new NotificationException("메시지 전송에 실패했습니다: " + errorMessage); + } + } + +} + + diff --git a/src/main/java/com/moing/backend/global/response/ErrorCode.java b/src/main/java/com/moing/backend/global/response/ErrorCode.java index 31aa29b6..e31bf8ed 100644 --- a/src/main/java/com/moing/backend/global/response/ErrorCode.java +++ b/src/main/java/com/moing/backend/global/response/ErrorCode.java @@ -13,6 +13,11 @@ public enum ErrorCode { INTERNAL_SERVER_ERROR("500", "서버에서 요청을 처리하는 동안 오류가 발생했습니다."), NOT_FOUND_REFRESH_TOKEN_ERROR( "J0008", "유효하지 않는 RefreshToken 입니다."), + //FCM 토큰 관련 + INITIALIZE_ERROR("F0001", "Firebase Admin SDK 초기화에 실패했습니다."), + NOTIFICATION_ERROR("F0002", "메시지 전송에 실패했습니다."), + MESSAGING_ERROR("F0003", "firebaseConfigPath를 읽어오는데 실패하였습니다"), + //유저 관련 에러 코드 NOT_FOUND_BY_SOCIAL_ID_ERROR( "U0001", "해당 socialId인 유저가 존재하지 않습니다."), ACCOUNT_ALREADY_EXIST("AU0001", "해당 email로 다른 소셜 플랫폼으로 가입하였습니다."), From 0f8e244cf25bf9a805b633bcdde6d240c291d83f Mon Sep 17 00:00:00 2001 From: minsu20 Date: Thu, 5 Oct 2023 21:34:58 +0900 Subject: [PATCH 03/11] =?UTF-8?q?feat:=20=EC=8B=A0=EA=B7=9C=EA=B3=B5?= =?UTF-8?q?=EC=A7=80=20=EC=95=8C=EB=A6=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/AlarmBoardUserCase.java | 42 +++++++++++++++++++ .../service/CreateBoardUserCase.java | 7 +++- .../TeamMemberCustomRepository.java | 2 + .../TeamMemberCustomRepositoryImpl.java | 11 +++++ .../domain/service/TeamMemberGetService.java | 6 +++ .../config/fcm/constant/NewUploadTitle.java | 13 ++++++ 6 files changed, 79 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java create mode 100644 src/main/java/com/moing/backend/global/config/fcm/constant/NewUploadTitle.java diff --git a/src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java new file mode 100644 index 00000000..fab65fc7 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java @@ -0,0 +1,42 @@ +package com.moing.backend.domain.board.application.service; + +import com.moing.backend.domain.board.domain.entity.Board; +import com.moing.backend.domain.member.domain.entity.Member; +import com.moing.backend.domain.team.domain.entity.Team; +import com.moing.backend.domain.teamMember.domain.service.TeamMemberGetService; +import com.moing.backend.global.config.fcm.dto.request.MultiRequest; +import com.moing.backend.global.config.fcm.service.FcmService; +import com.moing.backend.global.response.BaseServiceResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +import static com.moing.backend.global.config.fcm.constant.NewUploadTitle.UPLOAD_NOTICE_NEW_TITLE; + +@Service +@RequiredArgsConstructor +@Transactional +public class AlarmBoardUserCase { + + private final TeamMemberGetService teamMemberGetService; + private final FcmService fcmService; + + public void sendNewUploadAlarm(BaseServiceResponse baseServiceResponse, Board board) { + Member member = baseServiceResponse.getMember(); + Team team = baseServiceResponse.getTeam(); + + if (board.isNotice() && member.isNewUploadPush()) { + String title = team.getName() + " " + UPLOAD_NOTICE_NEW_TITLE.getTitle(); + String message = board.getTitle(); + Optional> fcmTokens = teamMemberGetService.getFcmTokens(team.getTeamId(), member.getMemberId()); + if (fcmTokens.isPresent() && !fcmTokens.get().isEmpty()) { + MultiRequest toMultiRequest = new MultiRequest(fcmTokens.get(), title, message); + fcmService.sendMultipleDevices(toMultiRequest); + } + } + } +} + diff --git a/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java index 84139ae8..e5d11c0e 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java @@ -40,12 +40,15 @@ public class CreateBoardUserCase { * 게시글 생성 */ public CreateBoardResponse createBoard(String socialId, Long teamId, CreateBoardRequest createBoardRequest) { + //1, 게시글 생성, 저장 BaseServiceResponse data=baseService.getCommonData(socialId, teamId); - boolean isLeader = checkLeaderUserCase.isTeamLeader(data.getMember(), data.getTeam()); + boolean isLeader = checkLeaderUserCase.isTeamLeader(data.getMember(), data.getTeam()); //작성자 리더 여부 Board board=boardSaveService.saveBoard(boardMapper.toBoard(data.getMember(), data.getTeamMember(), data.getTeam(), createBoardRequest, isLeader)); - //읽음 처리 + //2. 읽음 처리 - 생성한 사람은 무조건 읽음 createBoardReadUserCase.createBoardRead(data.getTeam(), data.getMember(), board); + + //3. 알림 보내기 - 공지인 경우 return new CreateBoardResponse(board.getBoardId()); } } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java index 4c7f0370..cc81ff9f 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java @@ -1,7 +1,9 @@ package com.moing.backend.domain.teamMember.domain.repository; import java.util.List; +import java.util.Optional; public interface TeamMemberCustomRepository { List findMemberIdsByTeamId(Long teamId); + Optional> findFcmTokensByTeamId(Long teamId, Long userId); } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java index 880d3e73..615603e7 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java @@ -5,6 +5,7 @@ import javax.persistence.EntityManager; import java.util.List; +import java.util.Optional; import static com.moing.backend.domain.teamMember.domain.entity.QTeamMember.teamMember; @@ -24,4 +25,14 @@ public List findMemberIdsByTeamId(Long teamId) { .where(teamMember.team.isDeleted.eq(false)) .fetch(); } + + @Override + public Optional> findFcmTokensByTeamId(Long teamId, Long memberId) { + return Optional.ofNullable(queryFactory.select(teamMember.member.fcmToken) + .from(teamMember) + .where(teamMember.team.teamId.eq(teamId)) //해당 소모임에 참여하고 있고 + .where(teamMember.member.isNewUploadPush.eq(true)) //알림 설정 on해 있고 + .where(teamMember.member.memberId.ne(memberId)) //지금 유저가 아닌 경우 + .fetch()); + } } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java b/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java index e224a89a..a8ba11bc 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java @@ -9,6 +9,7 @@ import lombok.RequiredArgsConstructor; import java.util.List; +import java.util.Optional; @DomainService @RequiredArgsConstructor @@ -22,4 +23,9 @@ public List getTeamMemberIds(Long teamId){ public TeamMember getTeamMember(Member member, Team team){ return teamMemberRepository.findTeamMemberByTeamAndMember(team, member).orElseThrow(()-> new NotFoundByTeamIdException()); } + + public Optional> getFcmTokens(Long teamId, Long memberId) { + return teamMemberRepository.findFcmTokensByTeamId(teamId, memberId); + } + } diff --git a/src/main/java/com/moing/backend/global/config/fcm/constant/NewUploadTitle.java b/src/main/java/com/moing/backend/global/config/fcm/constant/NewUploadTitle.java new file mode 100644 index 00000000..98690142 --- /dev/null +++ b/src/main/java/com/moing/backend/global/config/fcm/constant/NewUploadTitle.java @@ -0,0 +1,13 @@ +package com.moing.backend.global.config.fcm.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum NewUploadTitle{ + UPLOAD_NOTICE_NEW_TITLE("새로운 공지 알려드려요!"), + UPLOAD_VOTE_NEW_TITLE("새로운 투표 알려드려요!"), + UPLOAD_MISSION_NEW_TITLE("새로운 미션 알려드려요!"); + private final String title; +} From c830a0440a873b49728b9365e396ea8ee9b36051 Mon Sep 17 00:00:00 2001 From: minsu20 Date: Fri, 6 Oct 2023 22:10:55 +0900 Subject: [PATCH 04/11] =?UTF-8?q?feat:=20=EC=86=8C=EB=AA=A8=EC=9E=84=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EB=B0=98=EB=A0=A4=20=EC=95=8C=EB=A6=BC?= =?UTF-8?q?=20=EB=B3=B4=EB=82=B4=EA=B8=B0=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/CreateBoardUserCase.java | 12 +----- .../service/DeleteBoardUserCase.java | 3 ++ .../application/service/GetBoardUserCase.java | 3 +- ...rCase.java => SendBoardAlarmUserCase.java} | 4 +- .../service/UpdateBoardUserCase.java | 3 ++ .../service/CreateBoardCommentUserCase.java | 5 +++ .../service/DeleteBoardCommentUserCase.java | 8 ++++ .../service/GetBoardCommentUserCase.java | 3 ++ .../service/SendTeamAlarmUserCase.java | 43 +++++++++++++++++++ .../presentation/AdminTeamController.java | 33 ++++++++++++-- .../constant/TeamResponseMessage.java | 4 +- .../TeamMemberCustomRepository.java | 3 +- .../TeamMemberCustomRepositoryImpl.java | 11 ++++- .../domain/service/TeamMemberGetService.java | 8 +++- 14 files changed, 122 insertions(+), 21 deletions(-) rename src/main/java/com/moing/backend/domain/board/application/service/{AlarmBoardUserCase.java => SendBoardAlarmUserCase.java} (94%) create mode 100644 src/main/java/com/moing/backend/domain/team/application/service/SendTeamAlarmUserCase.java diff --git a/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java index e5d11c0e..c6e401c0 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/CreateBoardUserCase.java @@ -5,18 +5,8 @@ import com.moing.backend.domain.board.application.mapper.BoardMapper; import com.moing.backend.domain.board.domain.entity.Board; import com.moing.backend.domain.board.domain.service.BoardSaveService; -import com.moing.backend.domain.boardRead.application.mapper.BoardReadMapper; import com.moing.backend.domain.boardRead.application.service.CreateBoardReadUserCase; -import com.moing.backend.domain.boardRead.domain.entity.BoardRead; -import com.moing.backend.domain.boardRead.domain.service.BoardReadSaveService; -import com.moing.backend.domain.member.domain.entity.Member; -import com.moing.backend.domain.member.domain.service.MemberGetService; import com.moing.backend.domain.team.application.service.CheckLeaderUserCase; -import com.moing.backend.domain.team.domain.entity.Team; -import com.moing.backend.domain.team.domain.service.TeamGetService; -import com.moing.backend.domain.teamMember.domain.entity.TeamMember; -import com.moing.backend.domain.teamMember.domain.service.TeamMemberGetService; -import com.moing.backend.global.config.security.dto.User; import com.moing.backend.global.response.BaseServiceResponse; import com.moing.backend.global.util.BaseService; import lombok.RequiredArgsConstructor; @@ -35,6 +25,7 @@ public class CreateBoardUserCase { private final BoardMapper boardMapper; private final CreateBoardReadUserCase createBoardReadUserCase; private final BaseService baseService; + private final SendBoardAlarmUserCase sendBoardAlarmUserCase; /** * 게시글 생성 @@ -49,6 +40,7 @@ public CreateBoardResponse createBoard(String socialId, Long teamId, CreateBoard createBoardReadUserCase.createBoardRead(data.getTeam(), data.getMember(), board); //3. 알림 보내기 - 공지인 경우 + sendBoardAlarmUserCase.sendNewUploadAlarm(data, board); return new CreateBoardResponse(board.getBoardId()); } } diff --git a/src/main/java/com/moing/backend/domain/board/application/service/DeleteBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/DeleteBoardUserCase.java index 387e17da..0274a771 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/DeleteBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/DeleteBoardUserCase.java @@ -21,8 +21,11 @@ public class DeleteBoardUserCase { * 게시글 삭제 */ public void deleteBoard(String socialId, Long teamId, Long boardId){ + //1. 게시글 조회 BaseBoardServiceResponse data= baseBoardService.getCommonData(socialId,teamId,boardId); + //2. 작성자인 경우 if (data.getTeamMember() == data.getBoard().getTeamMember()) { + //3. 삭제 boardDeleteService.deleteBoard(data.getBoard()); } else throw new NotAuthByBoardException(); } diff --git a/src/main/java/com/moing/backend/domain/board/application/service/GetBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/GetBoardUserCase.java index 56430dcc..e72be8e8 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/GetBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/GetBoardUserCase.java @@ -31,8 +31,9 @@ public class GetBoardUserCase { * 게시글 상세 조회 */ public GetBoardDetailResponse getBoardDetail(String socialId, Long teamId, Long boardId) { + // 1. 게시글 조회 BaseBoardServiceResponse data = baseBoardService.getCommonData(socialId, teamId, boardId); - //읽음 처리 + // 2. 읽음 처리 createBoardReadUserCase.createBoardRead(data.getTeam(), data.getMember(), data.getBoard()); return boardMapper.toBoardDetail(data.getBoard(), data.getTeamMember() == data.getBoard().getTeamMember()); } diff --git a/src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/SendBoardAlarmUserCase.java similarity index 94% rename from src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java rename to src/main/java/com/moing/backend/domain/board/application/service/SendBoardAlarmUserCase.java index fab65fc7..94efba16 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/AlarmBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/SendBoardAlarmUserCase.java @@ -19,7 +19,7 @@ @Service @RequiredArgsConstructor @Transactional -public class AlarmBoardUserCase { +public class SendBoardAlarmUserCase { private final TeamMemberGetService teamMemberGetService; private final FcmService fcmService; @@ -31,7 +31,7 @@ public void sendNewUploadAlarm(BaseServiceResponse baseServiceResponse, Board bo if (board.isNotice() && member.isNewUploadPush()) { String title = team.getName() + " " + UPLOAD_NOTICE_NEW_TITLE.getTitle(); String message = board.getTitle(); - Optional> fcmTokens = teamMemberGetService.getFcmTokens(team.getTeamId(), member.getMemberId()); + Optional> fcmTokens = teamMemberGetService.getFcmTokensExceptMe(team.getTeamId(), member.getMemberId()); if (fcmTokens.isPresent() && !fcmTokens.get().isEmpty()) { MultiRequest toMultiRequest = new MultiRequest(fcmTokens.get(), title, message); fcmService.sendMultipleDevices(toMultiRequest); diff --git a/src/main/java/com/moing/backend/domain/board/application/service/UpdateBoardUserCase.java b/src/main/java/com/moing/backend/domain/board/application/service/UpdateBoardUserCase.java index 394b34e3..21e20b91 100644 --- a/src/main/java/com/moing/backend/domain/board/application/service/UpdateBoardUserCase.java +++ b/src/main/java/com/moing/backend/domain/board/application/service/UpdateBoardUserCase.java @@ -21,8 +21,11 @@ public class UpdateBoardUserCase { * 게시글 수정 */ public UpdateBoardResponse updateBoard(String socialId, Long teamId, Long boardId, UpdateBoardRequest updateBoardRequest){ + // 1. 게시글 조회 BaseBoardServiceResponse data= baseBoardService.getCommonData(socialId, teamId, boardId); + // 2. 게시글 작성자만 if (data.getTeamMember() == data.getBoard().getTeamMember()) { + // 3. 수정 data.getBoard().updateBoard(updateBoardRequest); return new UpdateBoardResponse(data.getBoard().getBoardId()); } else throw new NotAuthByBoardException(); diff --git a/src/main/java/com/moing/backend/domain/boardComment/application/service/CreateBoardCommentUserCase.java b/src/main/java/com/moing/backend/domain/boardComment/application/service/CreateBoardCommentUserCase.java index 759fc5c4..23e1d12b 100644 --- a/src/main/java/com/moing/backend/domain/boardComment/application/service/CreateBoardCommentUserCase.java +++ b/src/main/java/com/moing/backend/domain/boardComment/application/service/CreateBoardCommentUserCase.java @@ -21,9 +21,14 @@ public class CreateBoardCommentUserCase { private final BoardCommentMapper boardCommentMapper; private final BaseBoardService baseBoardService; + /** + * 게시글 댓글 생성 + */ public CreateBoardCommentResponse createBoardComment(String socialId, Long teamId, Long boardId, CreateBoardCommentRequest createBoardCommentRequest) { + // 1. 게시글 댓글 생성 BaseBoardServiceResponse data = baseBoardService.getCommonData(socialId, teamId, boardId); BoardComment boardComment = boardCommentSaveService.saveBoardComment(boardCommentMapper.toBoardComment(data.getTeamMember(), data.getBoard(), createBoardCommentRequest)); + // 2. 게시글 댓글 개수 증가 data.getBoard().incrComNum(); return new CreateBoardCommentResponse(boardComment.getBoardCommentId()); } diff --git a/src/main/java/com/moing/backend/domain/boardComment/application/service/DeleteBoardCommentUserCase.java b/src/main/java/com/moing/backend/domain/boardComment/application/service/DeleteBoardCommentUserCase.java index 6201717f..2f0285c3 100644 --- a/src/main/java/com/moing/backend/domain/boardComment/application/service/DeleteBoardCommentUserCase.java +++ b/src/main/java/com/moing/backend/domain/boardComment/application/service/DeleteBoardCommentUserCase.java @@ -20,11 +20,19 @@ public class DeleteBoardCommentUserCase { private final BoardCommentDeleteService boardCommentDeleteService; private final BaseBoardService baseBoardService; + /** + * 게시글 댓글 삭제 + */ + public void deleteBoardComment(String socialId, Long teamId, Long boardId, Long boardCommentId){ + // 1. 게시글 댓글 조회 BaseBoardServiceResponse data = baseBoardService.getCommonData(socialId, teamId, boardId); BoardComment boardComment=boardCommentGetService.getBoardComment(boardCommentId); + // 2. 게시글 댓글 작성자만 if (data.getTeamMember() == boardComment.getTeamMember()) { + // 3. 삭제 boardCommentDeleteService.deleteBoardComment(boardComment); + // 4. 댓글 개수 줄이기 data.getBoard().decrComNum(); } else throw new NotAuthByBoardCommentException(); } diff --git a/src/main/java/com/moing/backend/domain/boardComment/application/service/GetBoardCommentUserCase.java b/src/main/java/com/moing/backend/domain/boardComment/application/service/GetBoardCommentUserCase.java index df53c6e1..544c5174 100644 --- a/src/main/java/com/moing/backend/domain/boardComment/application/service/GetBoardCommentUserCase.java +++ b/src/main/java/com/moing/backend/domain/boardComment/application/service/GetBoardCommentUserCase.java @@ -17,6 +17,9 @@ public class GetBoardCommentUserCase { private final BoardCommentGetService boardCommentGetService; private final BaseBoardService baseBoardService; + /** + * 게시글 댓글 전체 조회 + */ public GetBoardCommentResponse getBoardCommentAll(String socialId, Long teamId, Long boardId){ BaseBoardServiceResponse data = baseBoardService.getCommonData(socialId, teamId, boardId); return boardCommentGetService.getBoardCommentAll(boardId, data.getTeamMember()); diff --git a/src/main/java/com/moing/backend/domain/team/application/service/SendTeamAlarmUserCase.java b/src/main/java/com/moing/backend/domain/team/application/service/SendTeamAlarmUserCase.java new file mode 100644 index 00000000..e5ef6690 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/team/application/service/SendTeamAlarmUserCase.java @@ -0,0 +1,43 @@ +package com.moing.backend.domain.team.application.service; + +import com.moing.backend.domain.teamMember.domain.service.TeamMemberGetService; +import com.moing.backend.global.config.fcm.dto.request.MultiRequest; +import com.moing.backend.global.config.fcm.service.FcmService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import javax.transaction.Transactional; +import java.util.List; +import java.util.Optional; + +import static com.moing.backend.global.config.fcm.constant.NewUploadTitle.UPLOAD_NOTICE_NEW_TITLE; + +@Service +@Transactional +@RequiredArgsConstructor +public class SendTeamAlarmUserCase { + private final TeamMemberGetService teamMemberGetService; + private final FcmService fcmService; + + public void sendApprovalAlarm(Long teamId) { + //TODO: 승인, 반려 문구 constant로 + String title = "소모임 승인 안내"; + String message = "소모임이 승인되었습니다."; + sendAlarm(teamId, title, message); + } + + public void sendRejectionAlarm(Long teamId) { + String title = "소모임 반려 안내"; + String message = "소모임이 반려되었습니다."; + sendAlarm(teamId, title, message); + } + + private void sendAlarm(Long teamId, String title, String message) { + Optional> fcmTokens = teamMemberGetService.getFcmTokens(teamId); + if(fcmTokens.isPresent() && !fcmTokens.get().isEmpty()) { + MultiRequest toMultiRequest = new MultiRequest(fcmTokens.get(), title, message); + fcmService.sendMultipleDevices(toMultiRequest); + } + } +} + diff --git a/src/main/java/com/moing/backend/domain/team/presentation/AdminTeamController.java b/src/main/java/com/moing/backend/domain/team/presentation/AdminTeamController.java index 3241479e..5ffa70f9 100644 --- a/src/main/java/com/moing/backend/domain/team/presentation/AdminTeamController.java +++ b/src/main/java/com/moing/backend/domain/team/presentation/AdminTeamController.java @@ -1,18 +1,45 @@ package com.moing.backend.domain.team.presentation; -import com.moing.backend.domain.team.application.service.CreateTeamUserCase; +import com.moing.backend.domain.team.application.service.SendTeamAlarmUserCase; +import com.moing.backend.global.response.SuccessResponse; import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import static com.moing.backend.domain.team.presentation.constant.TeamResponseMessage.*; + @RestController @AllArgsConstructor @RequestMapping("/api/admin/team") public class AdminTeamController { - //TODO 소모임 승인, 반려하기 - private final CreateTeamUserCase createTeamService; + private final SendTeamAlarmUserCase sendTeamAlarmUserCase; + + /** + * 소모임 승인 알림 보내기 + * [POST] api/admin/team/approval/{teamId} + * 작성자 : 김민수 + */ + + @PostMapping("/approval/{teamId}") + public ResponseEntity sendApproveAlarm(@PathVariable Long teamId) { + this.sendTeamAlarmUserCase.sendApprovalAlarm(teamId); + return ResponseEntity.ok(SuccessResponse.create(SEND_APPROVAL_ALARM_SUCCESS.getMessage())); + } + /** + * 소모임 반려 알림 보내기 + * [POST] api/admin/team/rejection/{teamId} + * 작성자: 김민수 + */ + @PostMapping("/rejection/{teamId}") + public ResponseEntity sendRejectionAlarm(@PathVariable Long teamId){ + this.sendTeamAlarmUserCase.sendRejectionAlarm(teamId); + return ResponseEntity.ok(SuccessResponse.create(SEND_REJECTION_ALARM_SUCCESS.getMessage())); + } } diff --git a/src/main/java/com/moing/backend/domain/team/presentation/constant/TeamResponseMessage.java b/src/main/java/com/moing/backend/domain/team/presentation/constant/TeamResponseMessage.java index 45ae0eb5..2b1ccdcb 100644 --- a/src/main/java/com/moing/backend/domain/team/presentation/constant/TeamResponseMessage.java +++ b/src/main/java/com/moing/backend/domain/team/presentation/constant/TeamResponseMessage.java @@ -10,6 +10,8 @@ public enum TeamResponseMessage { GET_TEAM_SUCCESS("홈 화면에서 내 소모임을 모두 조회했습니다."), SIGNIN_TEAM_SUCCESS("소모임에 가입하였습니다"), DISBAND_TEAM_SUCCESS("[소모임장 권한] 소모임을 강제 종료했습니다."), - WITHDRAW_TEAM_SUCCESS("[소모임원 권한] 소모임을 탈퇴하였습니다"); + WITHDRAW_TEAM_SUCCESS("[소모임원 권한] 소모임을 탈퇴하였습니다"), + SEND_APPROVAL_ALARM_SUCCESS("소모임 승인 알림을 보냈습니다."), + SEND_REJECTION_ALARM_SUCCESS("소모임 반려 알림을 보냈습니다."); private final String message; } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java index cc81ff9f..725b70e1 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepository.java @@ -5,5 +5,6 @@ public interface TeamMemberCustomRepository { List findMemberIdsByTeamId(Long teamId); - Optional> findFcmTokensByTeamId(Long teamId, Long userId); + Optional> findFcmTokensByTeamIdAndMemberId(Long teamId, Long memberId); + Optional> findFcmTokensByTeamId(Long teamId); } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java index 615603e7..40f64eaa 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/repository/TeamMemberCustomRepositoryImpl.java @@ -27,7 +27,7 @@ public List findMemberIdsByTeamId(Long teamId) { } @Override - public Optional> findFcmTokensByTeamId(Long teamId, Long memberId) { + public Optional> findFcmTokensByTeamIdAndMemberId(Long teamId, Long memberId) { return Optional.ofNullable(queryFactory.select(teamMember.member.fcmToken) .from(teamMember) .where(teamMember.team.teamId.eq(teamId)) //해당 소모임에 참여하고 있고 @@ -35,4 +35,13 @@ public Optional> findFcmTokensByTeamId(Long teamId, Long memberId) .where(teamMember.member.memberId.ne(memberId)) //지금 유저가 아닌 경우 .fetch()); } + + @Override + public Optional> findFcmTokensByTeamId(Long teamId) { + return Optional.ofNullable(queryFactory.select(teamMember.member.fcmToken) + .from(teamMember) + .where(teamMember.team.teamId.eq(teamId)) //해당 소모임에 참여하고 있고 + .where(teamMember.member.isNewUploadPush.eq(true)) //알림 설정 on해 있고 + .fetch()); + } } diff --git a/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java b/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java index a8ba11bc..6ba8f3db 100644 --- a/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java +++ b/src/main/java/com/moing/backend/domain/teamMember/domain/service/TeamMemberGetService.java @@ -24,8 +24,12 @@ public TeamMember getTeamMember(Member member, Team team){ return teamMemberRepository.findTeamMemberByTeamAndMember(team, member).orElseThrow(()-> new NotFoundByTeamIdException()); } - public Optional> getFcmTokens(Long teamId, Long memberId) { - return teamMemberRepository.findFcmTokensByTeamId(teamId, memberId); + public Optional> getFcmTokensExceptMe(Long teamId, Long memberId) { + return teamMemberRepository.findFcmTokensByTeamIdAndMemberId(teamId, memberId); + } + + public Optional> getFcmTokens(Long teamId) { + return teamMemberRepository.findFcmTokensByTeamId(teamId); } } From aa30809ad467cb5137a7281a666d8c8ae6d47c63 Mon Sep 17 00:00:00 2001 From: seungyeonnnnnni Date: Tue, 10 Oct 2023 16:53:32 +0900 Subject: [PATCH 05/11] =?UTF-8?q?feat=20:=20=EB=B6=88=EB=8D=98=EC=A7=80?= =?UTF-8?q?=EA=B8=B0=20Fire=20=EA=B0=9D=EC=B2=B4=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/res/FireThrowRes.java | 11 +++++++ .../fire/application/mapper/FireMapper.java | 13 ++++++++ .../application/service/FireThrowUseCase.java | 31 +++++++++++++++++++ .../domain/fire/domain/entity/Fire.java | 28 +++++++++++++++++ .../repository/FireCustomRepository.java | 13 ++++++++ .../repository/FireCustomRepositoryImpl.java | 26 ++++++++++++++++ .../fire/domain/service/FireQueryService.java | 18 +++++++++++ .../fire/domain/service/FireSaveService.java | 19 ++++++++++++ 8 files changed, 159 insertions(+) create mode 100644 src/main/java/com/moing/backend/domain/fire/application/dto/res/FireThrowRes.java create mode 100644 src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java create mode 100644 src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/entity/Fire.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java diff --git a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireThrowRes.java b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireThrowRes.java new file mode 100644 index 00000000..12d19db3 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireThrowRes.java @@ -0,0 +1,11 @@ +package com.moing.backend.domain.fire.application.dto.res; + +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FireThrowRes { + + private Long receiveMemberId; +} diff --git a/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java new file mode 100644 index 00000000..b9fe4b27 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java @@ -0,0 +1,13 @@ +package com.moing.backend.domain.fire.application.mapper; + +import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; +import com.moing.backend.domain.fire.domain.entity.Fire; + +public class FireMapper { + + public static FireThrowRes mapToFireThrowRes(Fire fire) { + return FireThrowRes.builder() + .receiveMemberId(fire.getReceiveMemberId()) + .build(); + } +} diff --git a/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java new file mode 100644 index 00000000..6c1eec71 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java @@ -0,0 +1,31 @@ +package com.moing.backend.domain.fire.application.service; + +import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; +import com.moing.backend.domain.fire.application.mapper.FireMapper; +import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; +import com.moing.backend.domain.fire.domain.service.FireQueryService; +import com.moing.backend.domain.fire.domain.service.FireSaveService; +import com.moing.backend.domain.mission.application.service.MissionCreateUseCase; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@Transactional +@RequiredArgsConstructor +public class FireThrowUseCase { + + public final FireSaveService fireSaveService; + public final FireQueryService fireQueryService; + + public FireThrowRes createFireThrow(Long throwMemberId, Long receiveMemberId) { + + + + return FireMapper.mapToFireThrowRes(fireSaveService.save(Fire.builder() + .throwMemberId(throwMemberId) + .receiveMemberId(receiveMemberId) + .build())); + } +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/entity/Fire.java b/src/main/java/com/moing/backend/domain/fire/domain/entity/Fire.java new file mode 100644 index 00000000..9b32a630 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/entity/Fire.java @@ -0,0 +1,28 @@ +package com.moing.backend.domain.fire.domain.entity; + +import com.moing.backend.global.entity.BaseTimeEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.persistence.*; + +@Entity +@Builder +@Getter +@NoArgsConstructor +@AllArgsConstructor +public class Fire extends BaseTimeEntity { + + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "fire_id") + private Long id; + + private Long throwMemberId; + + private Long receiveMemberId; + +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java new file mode 100644 index 00000000..30b97109 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java @@ -0,0 +1,13 @@ +package com.moing.backend.domain.fire.domain.repository; + + +import com.moing.backend.domain.fire.domain.entity.Fire; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.Optional; + +public interface FireCustomRepository extends JpaRepository { + + Optional findMyRecentFire(Long memberId); + +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java new file mode 100644 index 00000000..24c19af5 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java @@ -0,0 +1,26 @@ +package com.moing.backend.domain.fire.domain.repository; + +import com.moing.backend.domain.fire.domain.entity.Fire; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import javax.persistence.EntityManager; +import java.util.Optional; + +public class FireCustomRepositoryImpl implements FireCustomRepository { + private final JPAQueryFactory queryFactory; + + public FireCustomRepositoryImpl(EntityManager em) { + this.queryFactory = new JPAQueryFactory(em); + } + +// +// Optional findMyRecentFire(Long memberId){ +// return Optional.ofNullable(queryFactory +// .select() +// .from() +// .where() +// .fetchFirst() +// ); +// } + +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java new file mode 100644 index 00000000..aa9a9090 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java @@ -0,0 +1,18 @@ +package com.moing.backend.domain.fire.domain.service; + +import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class FireQueryService { + + private final FireCustomRepository fireCustomRepository; + + public Fire findMyRecentFire(Long memberId) { + return fireCustomRepository.findMyRecentFire(Long, memberId); + } + +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java b/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java new file mode 100644 index 00000000..bf9e1f2c --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java @@ -0,0 +1,19 @@ +package com.moing.backend.domain.fire.domain.service; + +import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; +import com.moing.backend.global.annotation.DomainService; +import lombok.RequiredArgsConstructor; +import org.springframework.transaction.annotation.Transactional; + +@DomainService +@Transactional +@RequiredArgsConstructor +public class FireSaveService { + + private final FireCustomRepository fireCustomRepository; + + public Fire save(Fire fire) { + return fireCustomRepository.save(fire); + } +} From 4a54be3cb3c179d73d7c136b52a34bf6f4aeaacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Tue, 10 Oct 2023 18:55:40 +0900 Subject: [PATCH 06/11] =?UTF-8?q?feat=20:=20=EB=B6=88=EB=8D=98=EC=A7=80?= =?UTF-8?q?=EA=B8=B0=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/res/FireReceiveRes.java | 8 ++++ .../application/service/FireThrowUseCase.java | 16 ++++++- .../repository/FireCustomRepository.java | 4 +- .../repository/FireCustomRepositoryImpl.java | 32 ++++++++++---- .../domain/repository/FireRepository.java | 7 ++++ .../fire/domain/service/FireQueryService.java | 15 +++++-- .../fire/domain/service/FireSaveService.java | 5 ++- .../domain/fire/exception/FireException.java | 13 ++++++ .../exception/NoAuthThrowFireException.java | 12 ++++++ .../fire/exception/NotFoundFireException.java | 12 ++++++ .../fire/presentation/FireController.java | 42 +++++++++++++++++++ .../constant/FireResponseMessage.java | 13 ++++++ .../backend/global/response/ErrorCode.java | 4 ++ 13 files changed, 166 insertions(+), 17 deletions(-) create mode 100644 src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java create mode 100644 src/main/java/com/moing/backend/domain/fire/domain/repository/FireRepository.java create mode 100644 src/main/java/com/moing/backend/domain/fire/exception/FireException.java create mode 100644 src/main/java/com/moing/backend/domain/fire/exception/NoAuthThrowFireException.java create mode 100644 src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireException.java create mode 100644 src/main/java/com/moing/backend/domain/fire/presentation/FireController.java create mode 100644 src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java diff --git a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java new file mode 100644 index 00000000..56eff3eb --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java @@ -0,0 +1,8 @@ +package com.moing.backend.domain.fire.application.dto.res; + +public class FireReceiveRes { + + private Long receiveMemberId; + private String nickName; + private String fireStatus; +} diff --git a/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java index 6c1eec71..2ffd75cb 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java +++ b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java @@ -1,16 +1,21 @@ package com.moing.backend.domain.fire.application.service; +import com.moing.backend.domain.fire.application.dto.res.FireReceiveRes; import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; import com.moing.backend.domain.fire.application.mapper.FireMapper; import com.moing.backend.domain.fire.domain.entity.Fire; import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; import com.moing.backend.domain.fire.domain.service.FireQueryService; import com.moing.backend.domain.fire.domain.service.FireSaveService; +import com.moing.backend.domain.fire.exception.NoAuthThrowFireException; +import com.moing.backend.domain.member.domain.service.MemberGetService; import com.moing.backend.domain.mission.application.service.MissionCreateUseCase; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @Service @Transactional @RequiredArgsConstructor @@ -18,14 +23,23 @@ public class FireThrowUseCase { public final FireSaveService fireSaveService; public final FireQueryService fireQueryService; + private final MemberGetService memberGetService; - public FireThrowRes createFireThrow(Long throwMemberId, Long receiveMemberId) { + public FireThrowRes createFireThrow(String userId, Long receiveMemberId) { + Long throwMemberId = memberGetService.getMemberBySocialId(userId).getMemberId(); + if (fireQueryService.hasFireCreatedWithinOneHour(throwMemberId, receiveMemberId)) { + throw new NoAuthThrowFireException(); + } return FireMapper.mapToFireThrowRes(fireSaveService.save(Fire.builder() .throwMemberId(throwMemberId) .receiveMemberId(receiveMemberId) .build())); } + +// public List getFireReceiveList(Long missionArchiveId) { +// fireQueryService. +// } } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java index 30b97109..f0fc3d4a 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java @@ -6,8 +6,8 @@ import java.util.Optional; -public interface FireCustomRepository extends JpaRepository { +public interface FireCustomRepository { - Optional findMyRecentFire(Long memberId); + boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMemberId); } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java index 24c19af5..437a10ec 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java @@ -1,11 +1,16 @@ package com.moing.backend.domain.fire.domain.repository; import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.fire.domain.entity.QFire; import com.querydsl.jpa.impl.JPAQueryFactory; import javax.persistence.EntityManager; +import java.time.LocalDateTime; +import java.util.List; import java.util.Optional; +import static com.moing.backend.domain.fire.domain.entity.QFire.fire; + public class FireCustomRepositoryImpl implements FireCustomRepository { private final JPAQueryFactory queryFactory; @@ -13,14 +18,23 @@ public FireCustomRepositoryImpl(EntityManager em) { this.queryFactory = new JPAQueryFactory(em); } -// -// Optional findMyRecentFire(Long memberId){ -// return Optional.ofNullable(queryFactory -// .select() -// .from() -// .where() -// .fetchFirst() -// ); -// } + + public boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMemberId) { + LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1); // 현재 시간에서 1시간을 뺀 시간 + long count = queryFactory + .select() + .from(fire) + .where( + fire.throwMemberId.eq(throwMemberId), + fire.receiveMemberId.eq(receiveMemberId), + fire.createdDate.after(oneHourAgo) // createdDate가 oneHourAgo 이후인 데이터 + ) + .fetchCount(); + + return count > 0; // 1시간 이내에 생성된 데이터가 존재하면 true를 반환, 그렇지 않으면 false 반환 + } + + +// public List get } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireRepository.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireRepository.java new file mode 100644 index 00000000..6a9b01af --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireRepository.java @@ -0,0 +1,7 @@ +package com.moing.backend.domain.fire.domain.repository; + +import com.moing.backend.domain.fire.domain.entity.Fire; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface FireRepository extends JpaRepository,FireCustomRepository { +} diff --git a/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java index aa9a9090..d1ce2ea5 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java @@ -2,17 +2,26 @@ import com.moing.backend.domain.fire.domain.entity.Fire; import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; +import com.moing.backend.domain.fire.domain.repository.FireRepository; +import com.moing.backend.domain.fire.exception.NotFoundFireException; +import com.moing.backend.domain.member.domain.entity.Member; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; +import java.util.List; + @Service @RequiredArgsConstructor public class FireQueryService { - private final FireCustomRepository fireCustomRepository; + private final FireRepository fireRepository; - public Fire findMyRecentFire(Long memberId) { - return fireCustomRepository.findMyRecentFire(Long, memberId); + public boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMemberId) { + return fireRepository.hasFireCreatedWithinOneHour(throwMemberId,receiveMemberId); } +// public List getNotYetMissionMember(Long missionArchiveId) { +// return fireCustomRepository. +// } + } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java b/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java index bf9e1f2c..14a94b30 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/service/FireSaveService.java @@ -2,6 +2,7 @@ import com.moing.backend.domain.fire.domain.entity.Fire; import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; +import com.moing.backend.domain.fire.domain.repository.FireRepository; import com.moing.backend.global.annotation.DomainService; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -11,9 +12,9 @@ @RequiredArgsConstructor public class FireSaveService { - private final FireCustomRepository fireCustomRepository; + private final FireRepository fireRepository; public Fire save(Fire fire) { - return fireCustomRepository.save(fire); + return fireRepository.save(fire); } } diff --git a/src/main/java/com/moing/backend/domain/fire/exception/FireException.java b/src/main/java/com/moing/backend/domain/fire/exception/FireException.java new file mode 100644 index 00000000..9540cd09 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/exception/FireException.java @@ -0,0 +1,13 @@ +package com.moing.backend.domain.fire.exception; + +import com.moing.backend.global.exception.ApplicationException; +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public abstract class FireException extends ApplicationException { + + protected FireException(ErrorCode errorCode, HttpStatus httpStatus) { + super(errorCode, httpStatus); + } + +} \ No newline at end of file diff --git a/src/main/java/com/moing/backend/domain/fire/exception/NoAuthThrowFireException.java b/src/main/java/com/moing/backend/domain/fire/exception/NoAuthThrowFireException.java new file mode 100644 index 00000000..fda8174d --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/exception/NoAuthThrowFireException.java @@ -0,0 +1,12 @@ +package com.moing.backend.domain.fire.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class NoAuthThrowFireException extends FireException { + + public NoAuthThrowFireException() { + super(ErrorCode.NOT_AUTH_FIRE_THROW, + HttpStatus.NOT_FOUND); + } +} diff --git a/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireException.java b/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireException.java new file mode 100644 index 00000000..666d85b0 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireException.java @@ -0,0 +1,12 @@ +package com.moing.backend.domain.fire.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class NotFoundFireException extends FireException { + + public NotFoundFireException() { + super(ErrorCode.NOT_FOUND_FIRE, + HttpStatus.NOT_FOUND); + } +} diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java new file mode 100644 index 00000000..3ca5bf05 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java @@ -0,0 +1,42 @@ +package com.moing.backend.domain.fire.presentation; + +import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; +import com.moing.backend.domain.fire.application.service.FireThrowUseCase; +import com.moing.backend.domain.mission.application.dto.res.MissionReadRes; +import com.moing.backend.global.config.security.dto.User; +import com.moing.backend.global.response.SuccessResponse; +import lombok.AllArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.annotation.AuthenticationPrincipal; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.THROW_FIRE_SUCCESS; +import static com.moing.backend.domain.mission.presentation.constant.MissionResponseMessage.READ_MISSION_SUCCESS; + +@RestController +@AllArgsConstructor +@RequestMapping("/api/{teamId}/missions/{missionId}/fire") +public class FireController { + + private final FireThrowUseCase fireThrowUseCase; + + /** + * 불 던지기 + * [POST] {teamId}/mission/{missionId}/archive/{missionArchiveId} + * 작성자 : 정승연 + */ + + @PostMapping("{receiveMemberId}") + public ResponseEntity> throwFire (@AuthenticationPrincipal User user, @PathVariable("teamId") Long teamId, + @PathVariable("receiveMemberId") Long receiveMemberId) { + return ResponseEntity.ok(SuccessResponse.create(THROW_FIRE_SUCCESS.getMessage(), this.fireThrowUseCase.createFireThrow(user.getSocialId(),receiveMemberId))); + } + + + + + +} diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java b/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java new file mode 100644 index 00000000..81048fd0 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java @@ -0,0 +1,13 @@ +package com.moing.backend.domain.fire.presentation.constant; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum FireResponseMessage { + THROW_FIRE_SUCCESS("불던지기를 완료 했습니다"); + + private final String message; + +} diff --git a/src/main/java/com/moing/backend/global/response/ErrorCode.java b/src/main/java/com/moing/backend/global/response/ErrorCode.java index 31aa29b6..ccf340c9 100644 --- a/src/main/java/com/moing/backend/global/response/ErrorCode.java +++ b/src/main/java/com/moing/backend/global/response/ErrorCode.java @@ -25,6 +25,10 @@ public enum ErrorCode { NOT_FOUND_MISSION("M0002", "미션을 찾을 수 없습니다."), NOT_FOUND_MISSION_ARCHIVE("MA0001", "아직 미션을 제출하지 않았습니다."), + //불던지기 관련 에러 코드 + NOT_FOUND_FIRE("F001","불던지기 현황을 찾을 수 없습니다"), + NOT_AUTH_FIRE_THROW("F002","1시간 이내에 불던지기를 할 수 없습니다"), + //팀 관련 에러 코드 NOT_FOUND_BY_TEAM_ID_ERROR("T0001", "해당 teamId인 팀이 존재하지 않습니다."), NOT_AUTH_BY_TEAM_ERROR("T0002","권한이 없습니다."), From 18a4dead8a03e4380e351de6c591cbfeea326d58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Tue, 10 Oct 2023 20:21:53 +0900 Subject: [PATCH 07/11] =?UTF-8?q?feat=20:=20=EB=B6=88=20=EB=B0=9B=EC=9D=84?= =?UTF-8?q?=20=EC=82=AC=EB=9E=8C=20list=20return=20&=20=EB=B6=88=EB=8D=98?= =?UTF-8?q?=EC=A7=80=EA=B8=B0=20=EA=B0=80=EB=8A=A5=EC=97=AC=EB=B6=80=20?= =?UTF-8?q?=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/res/FireReceiveRes.java | 11 +++++++ .../fire/application/mapper/FireMapper.java | 21 ++++++++++++++ .../application/service/FireThrowUseCase.java | 15 +++++++--- .../repository/FireCustomRepository.java | 5 +++- .../repository/FireCustomRepositoryImpl.java | 29 +++++++++++++++++-- .../fire/domain/service/FireQueryService.java | 7 +++-- .../NotFoundFireReceiversException.java | 12 ++++++++ .../fire/presentation/FireController.java | 20 ++++++++++--- .../backend/global/response/ErrorCode.java | 1 + 9 files changed, 107 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireReceiversException.java diff --git a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java index 56eff3eb..d5e228a1 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java +++ b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java @@ -1,8 +1,19 @@ package com.moing.backend.domain.fire.application.dto.res; +import lombok.Builder; +import lombok.Getter; + +@Builder +@Getter public class FireReceiveRes { private Long receiveMemberId; private String nickName; private String fireStatus; + + public void updateFireStatus(boolean status) { + if (status) + this.fireStatus = "True"; + else this.fireStatus = "False"; + } } diff --git a/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java index b9fe4b27..82b0853e 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java +++ b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java @@ -1,7 +1,12 @@ package com.moing.backend.domain.fire.application.mapper; +import com.moing.backend.domain.fire.application.dto.res.FireReceiveRes; import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.member.domain.entity.Member; + +import java.util.ArrayList; +import java.util.List; public class FireMapper { @@ -10,4 +15,20 @@ public static FireThrowRes mapToFireThrowRes(Fire fire) { .receiveMemberId(fire.getReceiveMemberId()) .build(); } + + public static List mapToFireReceiversList(List members) { + List fireReceiveResList = new ArrayList<>(); + members.forEach( + member -> fireReceiveResList.add(FireMapper.mapToFireReceiveRes(member)) + ); + return fireReceiveResList; + } + + public static FireReceiveRes mapToFireReceiveRes(Member member) { + return FireReceiveRes.builder() + .receiveMemberId(member.getMemberId()) + .nickName(member.getNickName()) + .fireStatus("TRUE") + .build(); + } } diff --git a/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java index 2ffd75cb..36d5175f 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java +++ b/src/main/java/com/moing/backend/domain/fire/application/service/FireThrowUseCase.java @@ -29,7 +29,7 @@ public FireThrowRes createFireThrow(String userId, Long receiveMemberId) { Long throwMemberId = memberGetService.getMemberBySocialId(userId).getMemberId(); - if (fireQueryService.hasFireCreatedWithinOneHour(throwMemberId, receiveMemberId)) { + if (!fireQueryService.hasFireCreatedWithinOneHour(throwMemberId, receiveMemberId)) { throw new NoAuthThrowFireException(); } @@ -39,7 +39,14 @@ public FireThrowRes createFireThrow(String userId, Long receiveMemberId) { .build())); } -// public List getFireReceiveList(Long missionArchiveId) { -// fireQueryService. -// } + public List getFireReceiveList(String userId,Long teamId, Long missionId) { + Long memberId = memberGetService.getMemberBySocialId(userId).getMemberId(); + + List fireReceiveRes = FireMapper.mapToFireReceiversList(fireQueryService.getNotYetMissionMember(teamId, missionId)); + fireReceiveRes.forEach( + res -> res.updateFireStatus(fireQueryService.hasFireCreatedWithinOneHour(memberId,res.getReceiveMemberId()) + )); + + return fireReceiveRes; + } } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java index f0fc3d4a..4e2d2b10 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepository.java @@ -2,12 +2,15 @@ import com.moing.backend.domain.fire.domain.entity.Fire; +import com.moing.backend.domain.member.domain.entity.Member; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.List; import java.util.Optional; public interface FireCustomRepository { boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMemberId); + Optional> getFireReceivers(Long teamId, Long missionId); -} + } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java index 437a10ec..92b958eb 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java @@ -2,6 +2,9 @@ import com.moing.backend.domain.fire.domain.entity.Fire; import com.moing.backend.domain.fire.domain.entity.QFire; +import com.moing.backend.domain.member.domain.entity.Member; +import com.moing.backend.domain.missionArchive.domain.entity.MissionArchive; +import com.querydsl.jpa.JPAExpressions; import com.querydsl.jpa.impl.JPAQueryFactory; import javax.persistence.EntityManager; @@ -10,6 +13,12 @@ import java.util.Optional; import static com.moing.backend.domain.fire.domain.entity.QFire.fire; +import static com.moing.backend.domain.member.domain.entity.QMember.member; +import static com.moing.backend.domain.mission.domain.entity.QMission.mission; +import static com.moing.backend.domain.missionArchive.domain.entity.QMissionArchive.missionArchive; +import static com.moing.backend.domain.team.domain.entity.QTeam.team; +import static com.moing.backend.domain.teamMember.domain.entity.QTeamMember.teamMember; +import static com.querydsl.jpa.JPAExpressions.select; public class FireCustomRepositoryImpl implements FireCustomRepository { private final JPAQueryFactory queryFactory; @@ -20,6 +29,8 @@ public FireCustomRepositoryImpl(EntityManager em) { public boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMemberId) { + + LocalDateTime oneHourAgo = LocalDateTime.now().minusHours(1); // 현재 시간에서 1시간을 뺀 시간 long count = queryFactory .select() @@ -31,10 +42,24 @@ public boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMembe ) .fetchCount(); - return count > 0; // 1시간 이내에 생성된 데이터가 존재하면 true를 반환, 그렇지 않으면 false 반환 + return count <= 0; // 1시간 이내에 생성된 데이터가 존재하면 true를 반환, 그렇지 않으면 false 반환 } -// public List get + public Optional> getFireReceivers(Long teamId, Long missionId) { + return Optional.ofNullable(queryFactory + .select(teamMember.member) + .from(teamMember) + .where( + teamMember.team.teamId.eq(teamId), + teamMember.member.memberId.notIn( + JPAExpressions + .select(missionArchive.member.memberId) + .from(missionArchive) + .where(missionArchive.mission.id.eq(missionId)) + ) + ) + .fetch()); + } } diff --git a/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java index d1ce2ea5..abb94e44 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/service/FireQueryService.java @@ -4,6 +4,7 @@ import com.moing.backend.domain.fire.domain.repository.FireCustomRepository; import com.moing.backend.domain.fire.domain.repository.FireRepository; import com.moing.backend.domain.fire.exception.NotFoundFireException; +import com.moing.backend.domain.fire.exception.NotFoundFireReceiversException; import com.moing.backend.domain.member.domain.entity.Member; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -20,8 +21,8 @@ public boolean hasFireCreatedWithinOneHour(Long throwMemberId, Long receiveMembe return fireRepository.hasFireCreatedWithinOneHour(throwMemberId,receiveMemberId); } -// public List getNotYetMissionMember(Long missionArchiveId) { -// return fireCustomRepository. -// } + public List getNotYetMissionMember(Long teamId, Long missionId) { + return fireRepository.getFireReceivers(teamId, missionId).orElseThrow(NotFoundFireReceiversException::new); + } } diff --git a/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireReceiversException.java b/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireReceiversException.java new file mode 100644 index 00000000..b41c2eb7 --- /dev/null +++ b/src/main/java/com/moing/backend/domain/fire/exception/NotFoundFireReceiversException.java @@ -0,0 +1,12 @@ +package com.moing.backend.domain.fire.exception; + +import com.moing.backend.global.response.ErrorCode; +import org.springframework.http.HttpStatus; + +public class NotFoundFireReceiversException extends FireException { + + public NotFoundFireReceiversException() { + super(ErrorCode.NOT_FOUND_FIRE_RECEIVERS, + HttpStatus.NOT_FOUND); + } +} diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java index 3ca5bf05..dcd03370 100644 --- a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java +++ b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java @@ -1,5 +1,6 @@ package com.moing.backend.domain.fire.presentation; +import com.moing.backend.domain.fire.application.dto.res.FireReceiveRes; import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; import com.moing.backend.domain.fire.application.service.FireThrowUseCase; import com.moing.backend.domain.mission.application.dto.res.MissionReadRes; @@ -8,10 +9,9 @@ import lombok.AllArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; + +import java.util.List; import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.THROW_FIRE_SUCCESS; import static com.moing.backend.domain.mission.presentation.constant.MissionResponseMessage.READ_MISSION_SUCCESS; @@ -35,6 +35,18 @@ public ResponseEntity> throwFire (@AuthenticationP return ResponseEntity.ok(SuccessResponse.create(THROW_FIRE_SUCCESS.getMessage(), this.fireThrowUseCase.createFireThrow(user.getSocialId(),receiveMemberId))); } + /** + * 불 던지기 + * [POST] {teamId}/mission/{missionId}/archive/{missionArchiveId} + * 작성자 : 정승연 + */ + + @GetMapping("") + public ResponseEntity>> throwFireList (@AuthenticationPrincipal User user, @PathVariable("teamId") Long teamId, + @PathVariable("missionId") Long missionId) { + return ResponseEntity.ok(SuccessResponse.create(THROW_FIRE_SUCCESS.getMessage(), this.fireThrowUseCase.getFireReceiveList(user.getSocialId(),teamId,missionId))); + } + diff --git a/src/main/java/com/moing/backend/global/response/ErrorCode.java b/src/main/java/com/moing/backend/global/response/ErrorCode.java index ccf340c9..a84ef05e 100644 --- a/src/main/java/com/moing/backend/global/response/ErrorCode.java +++ b/src/main/java/com/moing/backend/global/response/ErrorCode.java @@ -27,6 +27,7 @@ public enum ErrorCode { //불던지기 관련 에러 코드 NOT_FOUND_FIRE("F001","불던지기 현황을 찾을 수 없습니다"), + NOT_FOUND_FIRE_RECEIVERS("F001","불던지기를 받을 사람을 찾을 수 없습니다"), NOT_AUTH_FIRE_THROW("F002","1시간 이내에 불던지기를 할 수 없습니다"), //팀 관련 에러 코드 From 27693effb113aa2c4960736396b1f3b1a2597dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Thu, 12 Oct 2023 02:44:28 +0900 Subject: [PATCH 08/11] fix : MissionArchiveController edit --- .../missionArchive/presentation/MissionArchiveController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/moing/backend/domain/missionArchive/presentation/MissionArchiveController.java b/src/main/java/com/moing/backend/domain/missionArchive/presentation/MissionArchiveController.java index 0d5e628a..e594d99a 100644 --- a/src/main/java/com/moing/backend/domain/missionArchive/presentation/MissionArchiveController.java +++ b/src/main/java/com/moing/backend/domain/missionArchive/presentation/MissionArchiveController.java @@ -86,8 +86,8 @@ public ResponseEntity>> getOtherPeopleArch } /** - * 인증 성공 인원 조회 - * [GET] {teamId}/m원issions/{missionId}/archive/status + * 인증 성공 인원 조회 (n/n명) + * [GET] {teamId}/missions/{missionId}/archive/status * 작성자 : 정승연 **/ From ee25e7855cbc5e87e2f7a4c0dd3cbf653cc581e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Thu, 12 Oct 2023 17:10:26 +0900 Subject: [PATCH 09/11] =?UTF-8?q?feat=20:=20=ED=95=9C=EB=B2=88=EB=AF=B8?= =?UTF-8?q?=EC=85=98,=20=EB=B0=98=EB=B3=B5=EB=AF=B8=EC=85=98=20=ED=86=B5?= =?UTF-8?q?=ED=95=A9=EB=90=98=EB=8A=94=20=EB=B6=88=20=EB=8D=98=EC=A7=88=20?= =?UTF-8?q?=EC=82=AC=EB=9E=8C=20list?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/repository/FireCustomRepositoryImpl.java | 13 +++++++++++-- .../domain/fire/presentation/FireController.java | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java index 92b958eb..da51ece9 100644 --- a/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java +++ b/src/main/java/com/moing/backend/domain/fire/domain/repository/FireCustomRepositoryImpl.java @@ -18,6 +18,7 @@ import static com.moing.backend.domain.missionArchive.domain.entity.QMissionArchive.missionArchive; import static com.moing.backend.domain.team.domain.entity.QTeam.team; import static com.moing.backend.domain.teamMember.domain.entity.QTeamMember.teamMember; +import static com.querydsl.jpa.JPAExpressions.max; import static com.querydsl.jpa.JPAExpressions.select; public class FireCustomRepositoryImpl implements FireCustomRepository { @@ -55,11 +56,19 @@ public Optional> getFireReceivers(Long teamId, Long missionId) { teamMember.member.memberId.notIn( JPAExpressions .select(missionArchive.member.memberId) - .from(missionArchive) - .where(missionArchive.mission.id.eq(missionId)) + .from(missionArchive,mission) + .where(missionArchive.mission.id.eq(missionId), + mission.id.eq(missionId)) + .groupBy(missionArchive.member.memberId, missionArchive.mission.id, + missionArchive.count, mission.number) + .having( + missionArchive.mission.id.eq(missionId), + missionArchive.count.max().goe(mission.number) + ) ) ) .fetch()); } + } diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java index dcd03370..5a97a05c 100644 --- a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java +++ b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java @@ -25,7 +25,7 @@ public class FireController { /** * 불 던지기 - * [POST] {teamId}/mission/{missionId}/archive/{missionArchiveId} + * [POST] {teamId}/mission/{missionId}/fire/{receiveMemberId} * 작성자 : 정승연 */ @@ -36,8 +36,8 @@ public ResponseEntity> throwFire (@AuthenticationP } /** - * 불 던지기 - * [POST] {teamId}/mission/{missionId}/archive/{missionArchiveId} + * 불 던질 사람 조회 (receivers) + * [GET] {teamId}/mission/{missionId}/fire * 작성자 : 정승연 */ From 71f6b4dd7492dc7f5f698e617557e8f26e396f58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Mon, 16 Oct 2023 10:56:46 +0900 Subject: [PATCH 10/11] =?UTF-8?q?feat=20:=20=EB=B6=88=EB=8D=98=EC=A7=80?= =?UTF-8?q?=EA=B8=B0=20=EA=B4=80=EB=A0=A8=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../application/dto/res/FireReceiveRes.java | 2 +- .../fire/application/mapper/FireMapper.java | 2 +- .../fire/presentation/FireController.java | 3 +- .../constant/FireResponseMessage.java | 3 +- .../representation/FireControllerTest.java | 133 ++++++++++++++++++ 5 files changed, 139 insertions(+), 4 deletions(-) create mode 100644 src/test/java/com/moing/backend/domain/fire/representation/FireControllerTest.java diff --git a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java index d5e228a1..7b59d0a9 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java +++ b/src/main/java/com/moing/backend/domain/fire/application/dto/res/FireReceiveRes.java @@ -8,7 +8,7 @@ public class FireReceiveRes { private Long receiveMemberId; - private String nickName; + private String nickname; private String fireStatus; public void updateFireStatus(boolean status) { diff --git a/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java index 82b0853e..c8a803b7 100644 --- a/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java +++ b/src/main/java/com/moing/backend/domain/fire/application/mapper/FireMapper.java @@ -27,7 +27,7 @@ public static List mapToFireReceiversList(List members) public static FireReceiveRes mapToFireReceiveRes(Member member) { return FireReceiveRes.builder() .receiveMemberId(member.getMemberId()) - .nickName(member.getNickName()) + .nickname(member.getNickName()) .fireStatus("TRUE") .build(); } diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java index 5a97a05c..a9210481 100644 --- a/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java +++ b/src/main/java/com/moing/backend/domain/fire/presentation/FireController.java @@ -13,6 +13,7 @@ import java.util.List; +import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.GET_RECEIVERS_SUCCESS; import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.THROW_FIRE_SUCCESS; import static com.moing.backend.domain.mission.presentation.constant.MissionResponseMessage.READ_MISSION_SUCCESS; @@ -44,7 +45,7 @@ public ResponseEntity> throwFire (@AuthenticationP @GetMapping("") public ResponseEntity>> throwFireList (@AuthenticationPrincipal User user, @PathVariable("teamId") Long teamId, @PathVariable("missionId") Long missionId) { - return ResponseEntity.ok(SuccessResponse.create(THROW_FIRE_SUCCESS.getMessage(), this.fireThrowUseCase.getFireReceiveList(user.getSocialId(),teamId,missionId))); + return ResponseEntity.ok(SuccessResponse.create(GET_RECEIVERS_SUCCESS.getMessage(), this.fireThrowUseCase.getFireReceiveList(user.getSocialId(),teamId,missionId))); } diff --git a/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java b/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java index 81048fd0..8bbb91aa 100644 --- a/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java +++ b/src/main/java/com/moing/backend/domain/fire/presentation/constant/FireResponseMessage.java @@ -6,7 +6,8 @@ @Getter @RequiredArgsConstructor public enum FireResponseMessage { - THROW_FIRE_SUCCESS("불던지기를 완료 했습니다"); + THROW_FIRE_SUCCESS("불던지기를 완료 했습니다"), + GET_RECEIVERS_SUCCESS("불 던질사람 조회를 완료 했습니다"); private final String message; diff --git a/src/test/java/com/moing/backend/domain/fire/representation/FireControllerTest.java b/src/test/java/com/moing/backend/domain/fire/representation/FireControllerTest.java new file mode 100644 index 00000000..767f6e4c --- /dev/null +++ b/src/test/java/com/moing/backend/domain/fire/representation/FireControllerTest.java @@ -0,0 +1,133 @@ +package com.moing.backend.domain.fire.representation; + +import com.moing.backend.config.CommonControllerTest; +import com.moing.backend.domain.fire.application.dto.res.FireReceiveRes; +import com.moing.backend.domain.fire.application.dto.res.FireThrowRes; +import com.moing.backend.domain.fire.application.service.FireThrowUseCase; +import com.moing.backend.domain.fire.presentation.FireController; +import com.moing.backend.domain.mission.application.dto.req.MissionReq; +import com.moing.backend.domain.mission.application.dto.res.MissionCreateRes; +import org.assertj.core.util.Lists; +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.http.MediaType; +import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders; +import org.springframework.test.web.servlet.ResultActions; +import org.springframework.test.web.servlet.result.MockMvcResultMatchers; + +import java.util.List; + +import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.GET_RECEIVERS_SUCCESS; +import static com.moing.backend.domain.fire.presentation.constant.FireResponseMessage.THROW_FIRE_SUCCESS; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName; +import static org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders; +import static org.springframework.restdocs.payload.PayloadDocumentation.*; +import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName; +import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; + +@WebMvcTest(FireController.class) +public class FireControllerTest extends CommonControllerTest { + + @MockBean + private FireThrowUseCase fireThrowUseCase; + + + @Test + public void 불_던질_사람_조회() throws Exception { + //given + + List output = Lists.newArrayList(FireReceiveRes.builder() + .receiveMemberId(1L) + .nickname("receiver 닉네임") + .fireStatus("True/False") + .build()); + + given(fireThrowUseCase.getFireReceiveList(any(),any(),any())).willReturn(output); + + Long teamId = 1L; + Long missionId = 1L; + //when + ResultActions actions = mockMvc.perform(RestDocumentationRequestBuilders. + get("/api/{teamId}/missions/{missionId}/fire",teamId,missionId) + .header("Authorization", "Bearer ACCESS_TOKEN") + .contentType(MediaType.APPLICATION_JSON) + + ); + + //then + actions + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo( + restDocs.document( + requestHeaders( + headerWithName("Authorization").description("접근 토큰") + ), + pathParameters( + parameterWithName("teamId").description("팀 아이디"), + parameterWithName("missionId").description("미션 아이디") + ), + responseFields( + fieldWithPath("isSuccess").description("true"), + fieldWithPath("message").description(GET_RECEIVERS_SUCCESS.getMessage()), + fieldWithPath("data[].receiveMemberId").description("미션 아이디"), + fieldWithPath("data[].nickname").description("불 받을 사람 "), + fieldWithPath("data[].fireStatus").description("불 던질 수 있는 상태 리턴, 1시간 내 불 던진 내역에 따라 true[True/False]") + + ) + ) + ) + .andReturn(); + + } + + + @Test + public void 불_던지기() throws Exception { + //given + + FireThrowRes output = FireThrowRes.builder() + .receiveMemberId(1L) + .build(); + + given(fireThrowUseCase.createFireThrow(any(),any())).willReturn(output); + + Long teamId = 2L; + Long missionId = 2L; + Long receiveMemberId = 2L; + //when + ResultActions actions = mockMvc.perform(RestDocumentationRequestBuilders. + post("/api/{teamId}/missions/{missionId}/fire/{receiveMemberId}",teamId,missionId,receiveMemberId) + .header("Authorization", "Bearer ACCESS_TOKEN") + .contentType(MediaType.APPLICATION_JSON) + ); + + //then + actions + .andExpect(MockMvcResultMatchers.status().isOk()) + .andDo( + restDocs.document( + requestHeaders( + headerWithName("Authorization").description("접근 토큰") + ), + pathParameters( + parameterWithName("teamId").description("팀 아이디"), + parameterWithName("missionId").description("미션 아이디"), + parameterWithName("receiveMemberId").description("불 받을 사람 아이디") + ), + + responseFields( + fieldWithPath("isSuccess").description("true"), + fieldWithPath("message").description(THROW_FIRE_SUCCESS.getMessage()), + fieldWithPath("data.receiveMemberId").description("미션 아이디") + ) + ) + ) + .andReturn(); + + } + + +} From 25fbfb6deb0390c181c5a203940b5c704797e654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=20=EC=8A=B9=20=EC=97=B0?= <53565255+seungyeonnnnnni@users.noreply.github.com> Date: Mon, 16 Oct 2023 12:14:21 +0900 Subject: [PATCH 11/11] =?UTF-8?q?feat=20:=20Fire-API.adoc=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 --- src/docs/asciidoc/Fire-API.adoc | 16 ++++++++++++++++ src/docs/asciidoc/api.adoc | 2 ++ 2 files changed, 18 insertions(+) create mode 100644 src/docs/asciidoc/Fire-API.adoc diff --git a/src/docs/asciidoc/Fire-API.adoc b/src/docs/asciidoc/Fire-API.adoc new file mode 100644 index 00000000..3da56ea5 --- /dev/null +++ b/src/docs/asciidoc/Fire-API.adoc @@ -0,0 +1,16 @@ + + +[[Fire-API]] += Fire API + +[[Fire-불던지기]] +=== 불 던지기 +operation::fire-controller-test/불_던지기[snippets='http-request,path-parameters,request-fields,http-response,response-fields'] + +--- +[[Fire-불던질사람조회]] +=== 불 던질 사람 조회 +operation::fire-controller-test/불_던질_사람_조회[snippets='http-request,path-parameters,request-fields,http-response,response-fields'] + +--- + diff --git a/src/docs/asciidoc/api.adoc b/src/docs/asciidoc/api.adoc index d103ef14..12774927 100644 --- a/src/docs/asciidoc/api.adoc +++ b/src/docs/asciidoc/api.adoc @@ -22,6 +22,8 @@ include::MissionArchive-API.adoc[] include::MissionBoard-API.adoc[] +include::Fire-API.adoc[] + include::Board-API.adoc[] include::BoardComment_API.adoc[]