From 986d72f54b2fe0ed6aeadbd0e594da5ee50b2650 Mon Sep 17 00:00:00 2001 From: pswaao88 <66404498+pswaao88@users.noreply.github.com> Date: Thu, 14 Nov 2024 23:54:54 +0900 Subject: [PATCH 01/23] feat: add Qna structure and add Entities --- .../mypage/controller/rest/QnAController.java | 5 ++++ .../devcard/mypage/dto/QnAListResponse.java | 7 +++++ .../devcard/mypage/dto/QnARequest.java | 5 ++++ .../devcard/mypage/dto/QnAResponse.java | 12 ++++++++ .../devcard/devcard/mypage/entity/QnA.java | 30 +++++++++++++++++++ .../mypage/repository/QnARepository.java | 9 ++++++ .../devcard/mypage/service/QnAService.java | 5 ++++ 7 files changed, 73 insertions(+) create mode 100644 src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java create mode 100644 src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java create mode 100644 src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java create mode 100644 src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java create mode 100644 src/main/java/com/devcard/devcard/mypage/entity/QnA.java create mode 100644 src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java create mode 100644 src/main/java/com/devcard/devcard/mypage/service/QnAService.java diff --git a/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java new file mode 100644 index 0000000..cb06aff --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java @@ -0,0 +1,5 @@ +package com.devcard.devcard.mypage.controller.rest; + +public class QnAController { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java new file mode 100644 index 0000000..a9c253b --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java @@ -0,0 +1,7 @@ +package com.devcard.devcard.mypage.dto; + +import java.time.LocalDateTime; + +public record QnAListResponse(Long id, String name, String questionTitle, boolean answerCompleted, LocalDateTime questionTimestamp) { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java b/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java new file mode 100644 index 0000000..5a8edd1 --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java @@ -0,0 +1,5 @@ +package com.devcard.devcard.mypage.dto; + +public record QnARequest(String questionTitle, String questionContent) { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java new file mode 100644 index 0000000..5679426 --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java @@ -0,0 +1,12 @@ +package com.devcard.devcard.mypage.dto; + +import java.time.LocalDateTime; + +public record QnAResponse(String name, + String questionTitle, + String questionContent, + String answer, + LocalDateTime questionTimestamp, + LocalDateTime answerTimestamp, + boolean answerCompleted) { +} diff --git a/src/main/java/com/devcard/devcard/mypage/entity/QnA.java b/src/main/java/com/devcard/devcard/mypage/entity/QnA.java new file mode 100644 index 0000000..613352a --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/entity/QnA.java @@ -0,0 +1,30 @@ +package com.devcard.devcard.mypage.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import java.time.LocalDateTime; + +@Entity(name = "qna") +public class QnA { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private String name; + + private String questionTitle; + + private String questionContent; + + private String answer; + + private LocalDateTime questionTimestamp; + + private LocalDateTime answerTimestamp; + + private boolean answerCompleted; + +} diff --git a/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java b/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java new file mode 100644 index 0000000..840966d --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java @@ -0,0 +1,9 @@ +package com.devcard.devcard.mypage.repository; + + +import com.devcard.devcard.mypage.entity.QnA; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface QnARepository extends JpaRepository { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/service/QnAService.java b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java new file mode 100644 index 0000000..5055ce1 --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java @@ -0,0 +1,5 @@ +package com.devcard.devcard.mypage.service; + +public class QnAService { + +} From 43b7db3d6e726652aa27a8613c0ce46399ecd636 Mon Sep 17 00:00:00 2001 From: pswaao88 <66404498+pswaao88@users.noreply.github.com> Date: Fri, 15 Nov 2024 00:02:41 +0900 Subject: [PATCH 02/23] feat: add qnaController --- .../mypage/controller/rest/QnAController.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java index cb06aff..7e5e5bd 100644 --- a/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java +++ b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java @@ -1,5 +1,52 @@ package com.devcard.devcard.mypage.controller.rest; +import com.devcard.devcard.mypage.dto.NoticeRequest; +import com.devcard.devcard.mypage.dto.NoticeResponse; +import com.devcard.devcard.mypage.dto.NoticeUpdateRequest; +import com.devcard.devcard.mypage.service.QnAService; +import java.util.List; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/qna") public class QnAController { + private final QnAService qnAService; + + public QnAController(QnAService qnAService) { + this.qnAService = qnAService; + } + + @GetMapping + public ResponseEntity> getAllQnA(){ + return ResponseEntity.status(201).body(qnAService.getNoticeList()); + } + + @GetMapping("/{id}") + public ResponseEntity getQnA(@PathVariable("id") Long id){ + return ResponseEntity.ok(qnAService.getNotice(id)); + } + + @PostMapping("") + public ResponseEntity addQnA(@RequestBody NoticeRequest noticeRequest){ + return ResponseEntity.ok(qnAService.addNotice(noticeRequest)); + } + + @PutMapping("") + public ResponseEntity updateQnA(@RequestBody NoticeUpdateRequest noticeUpdateRequest){ + return ResponseEntity.ok(qnAService.updateNotice(noticeUpdateRequest)); + } + + @DeleteMapping("/{id}") + public ResponseEntity deleteQnA(@PathVariable("id") Long id){ + return ResponseEntity.ok(qnAService.deleteNotice(id)); + } } From 1a92e64fd971704d19ce282919a8d246869c643e Mon Sep 17 00:00:00 2001 From: pswaao88 <66404498+pswaao88@users.noreply.github.com> Date: Fri, 15 Nov 2024 01:32:06 +0900 Subject: [PATCH 03/23] feat: add qnaService and logics --- .../devcard/auth/config/SecurityConfig.java | 4 +- .../mypage/controller/rest/QnAController.java | 24 ++++---- .../devcard/mypage/dto/QnAAnswerDTO.java | 5 ++ .../devcard/mypage/dto/QnAListResponse.java | 6 +- .../devcard/mypage/dto/QnARequest.java | 2 +- .../devcard/mypage/dto/QnAUpdateRequest.java | 5 ++ .../devcard/devcard/mypage/entity/QnA.java | 54 ++++++++++++++++++ .../mypage/repository/QnARepository.java | 4 +- .../devcard/mypage/service/QnAService.java | 57 +++++++++++++++++++ 9 files changed, 147 insertions(+), 14 deletions(-) create mode 100644 src/main/java/com/devcard/devcard/mypage/dto/QnAAnswerDTO.java create mode 100644 src/main/java/com/devcard/devcard/mypage/dto/QnAUpdateRequest.java diff --git a/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java b/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java index 42d1b7d..b8e6ee3 100644 --- a/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java +++ b/src/main/java/com/devcard/devcard/auth/config/SecurityConfig.java @@ -36,7 +36,9 @@ SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { "/walletList", "/qrcodes/**", "/notice", - "/notice/**" + "/notice/**", + "/qna", + "/qna/**" ).permitAll(); // 그 외의 모든 요청은 인증 필요 auth.anyRequest().authenticated(); diff --git a/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java index 7e5e5bd..97c6e8f 100644 --- a/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java +++ b/src/main/java/com/devcard/devcard/mypage/controller/rest/QnAController.java @@ -3,6 +3,10 @@ import com.devcard.devcard.mypage.dto.NoticeRequest; import com.devcard.devcard.mypage.dto.NoticeResponse; import com.devcard.devcard.mypage.dto.NoticeUpdateRequest; +import com.devcard.devcard.mypage.dto.QnAListResponse; +import com.devcard.devcard.mypage.dto.QnARequest; +import com.devcard.devcard.mypage.dto.QnAResponse; +import com.devcard.devcard.mypage.dto.QnAUpdateRequest; import com.devcard.devcard.mypage.service.QnAService; import java.util.List; import org.springframework.http.ResponseEntity; @@ -26,27 +30,27 @@ public QnAController(QnAService qnAService) { } @GetMapping - public ResponseEntity> getAllQnA(){ - return ResponseEntity.status(201).body(qnAService.getNoticeList()); + public ResponseEntity> getAllQnA(){ + return ResponseEntity.status(201).body(qnAService.getQnAList()); } @GetMapping("/{id}") - public ResponseEntity getQnA(@PathVariable("id") Long id){ - return ResponseEntity.ok(qnAService.getNotice(id)); + public ResponseEntity getQnA(@PathVariable("id") Long id){ + return ResponseEntity.ok(qnAService.getQnA(id)); } @PostMapping("") - public ResponseEntity addQnA(@RequestBody NoticeRequest noticeRequest){ - return ResponseEntity.ok(qnAService.addNotice(noticeRequest)); + public ResponseEntity addQnA(@RequestBody QnARequest qnARequest){ + return ResponseEntity.ok(qnAService.addQnA(qnARequest)); } @PutMapping("") - public ResponseEntity updateQnA(@RequestBody NoticeUpdateRequest noticeUpdateRequest){ - return ResponseEntity.ok(qnAService.updateNotice(noticeUpdateRequest)); + public ResponseEntity updateQnA(@RequestBody QnAUpdateRequest qnAUpdateRequest){ + return ResponseEntity.ok(qnAService.updateQnA(qnAUpdateRequest)); } @DeleteMapping("/{id}") - public ResponseEntity deleteQnA(@PathVariable("id") Long id){ - return ResponseEntity.ok(qnAService.deleteNotice(id)); + public ResponseEntity deleteQnA(@PathVariable("id") Long id){ + return ResponseEntity.ok(qnAService.deleteQnA(id)); } } diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAAnswerDTO.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAAnswerDTO.java new file mode 100644 index 0000000..a7acd3f --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAAnswerDTO.java @@ -0,0 +1,5 @@ +package com.devcard.devcard.mypage.dto; + +public record QnAAnswerDTO(Long id, String answer) { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java index a9c253b..d3c0cd4 100644 --- a/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAListResponse.java @@ -2,6 +2,10 @@ import java.time.LocalDateTime; -public record QnAListResponse(Long id, String name, String questionTitle, boolean answerCompleted, LocalDateTime questionTimestamp) { +public record QnAListResponse(Long id, + String name, + String questionTitle, + boolean answerCompleted, + LocalDateTime questionTimestamp) { } diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java b/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java index 5a8edd1..81decbd 100644 --- a/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnARequest.java @@ -1,5 +1,5 @@ package com.devcard.devcard.mypage.dto; -public record QnARequest(String questionTitle, String questionContent) { +public record QnARequest(String name, String questionTitle, String questionContent) { } diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAUpdateRequest.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAUpdateRequest.java new file mode 100644 index 0000000..98487f1 --- /dev/null +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAUpdateRequest.java @@ -0,0 +1,5 @@ +package com.devcard.devcard.mypage.dto; + +public record QnAUpdateRequest(Long id, String questionTitle, String questionContent) { + +} diff --git a/src/main/java/com/devcard/devcard/mypage/entity/QnA.java b/src/main/java/com/devcard/devcard/mypage/entity/QnA.java index 613352a..757ceb0 100644 --- a/src/main/java/com/devcard/devcard/mypage/entity/QnA.java +++ b/src/main/java/com/devcard/devcard/mypage/entity/QnA.java @@ -27,4 +27,58 @@ public class QnA { private boolean answerCompleted; + public QnA(){ + + } + + public QnA(String name, String questionTitle, String questionContent, LocalDateTime questionTimestamp) { + this.name = name; + this.questionTitle = questionTitle; + this.questionContent = questionContent; + this.questionTimestamp = questionTimestamp; + this.answerCompleted = false; + } + + public Long getId() { + return id; + } + + public String getName() { + return name; + } + + public String getQuestionTitle() { + return questionTitle; + } + + public String getQuestionContent() { + return questionContent; + } + + public String getAnswer() { + return answer; + } + + public LocalDateTime getQuestionTimestamp() { + return questionTimestamp; + } + + public LocalDateTime getAnswerTimestamp() { + return answerTimestamp; + } + + public boolean isAnswerCompleted() { + return answerCompleted; + } + + public void updateByRequest(String updateQuestionTitle, String updateQuestionContent){ + this.questionTitle = updateQuestionTitle; + this.questionContent = updateQuestionContent; + } + + public void updateAnswer(String updateAnswer){ + this.answer = updateAnswer; + this.answerTimestamp = LocalDateTime.now(); + this.answerCompleted = true; + } } diff --git a/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java b/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java index 840966d..6bfbeaa 100644 --- a/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java +++ b/src/main/java/com/devcard/devcard/mypage/repository/QnARepository.java @@ -1,9 +1,11 @@ package com.devcard.devcard.mypage.repository; - import com.devcard.devcard.mypage.entity.QnA; +import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; public interface QnARepository extends JpaRepository { + List findAllByOrderByQuestionTimestampDesc(); + QnA findQnAById(Long id); } diff --git a/src/main/java/com/devcard/devcard/mypage/service/QnAService.java b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java index 5055ce1..9752885 100644 --- a/src/main/java/com/devcard/devcard/mypage/service/QnAService.java +++ b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java @@ -1,5 +1,62 @@ package com.devcard.devcard.mypage.service; +import com.devcard.devcard.mypage.dto.QnAAnswerDTO; +import com.devcard.devcard.mypage.dto.QnAListResponse; +import com.devcard.devcard.mypage.dto.QnARequest; +import com.devcard.devcard.mypage.dto.QnAResponse; +import com.devcard.devcard.mypage.dto.QnAUpdateRequest; +import com.devcard.devcard.mypage.entity.QnA; +import com.devcard.devcard.mypage.repository.QnARepository; +import java.time.LocalDateTime; +import java.util.List; +import org.springframework.stereotype.Service; + +@Service public class QnAService { + private final QnARepository qnARepository; + + public QnAService(QnARepository qnARepository) { + this.qnARepository = qnARepository; + } + public List getQnAList() { + List qnaList = qnARepository.findAllByOrderByQuestionTimestampDesc(); + return qnaList.stream().map(qna -> new QnAListResponse( + qna.getId(), + qna.getName(), + qna.getQuestionTitle(), + qna.isAnswerCompleted(), + qna.getQuestionTimestamp() + )).toList(); + } + + public QnAResponse getQnA(Long id) { + QnA qna = qnARepository.findQnAById(id); + return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + } + + public QnAResponse addQnA(QnARequest qnARequest) { + QnA qna = qnARepository.save(new QnA(qnARequest.name(), qnARequest.questionTitle(), qnARequest.questionContent(), + LocalDateTime.now())); + return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + } + + public QnAResponse updateQnA(QnAUpdateRequest qnAUpdateRequest) { + QnA qna = qnARepository.findQnAById(qnAUpdateRequest.id()); + qna.updateByRequest(qnAUpdateRequest.questionTitle(), qnAUpdateRequest.questionContent()); + qnARepository.save(qna); + return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + } + + public QnAResponse deleteQnA(Long id) { + QnA qna = qnARepository.findQnAById(id); + qnARepository.delete(qna); + return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + } + + public QnAResponse updateAnswer(QnAAnswerDTO qnAAnswerDTO) { + QnA qna = qnARepository.findQnAById(qnAAnswerDTO.id()); + qna.updateAnswer(qnAAnswerDTO.answer()); + return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + } } From 1b236554a57eda0dd6326224d46a34fde9bf7011 Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 02:14:25 +0900 Subject: [PATCH 04/23] feat: Add chat room creation and deletion logic when adding/removing cards in group --- .../devcard/card/service/GroupService.java | 49 +++++++++++++------ .../devcard/chat/dto/CreateRoomRequest.java | 7 ++- .../chat/repository/ChatRoomRepository.java | 4 ++ .../devcard/chat/service/ChatRoomService.java | 21 +++++++- .../devcard/devcard/chat/util/Constants.java | 1 + 5 files changed, 63 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/devcard/devcard/card/service/GroupService.java b/src/main/java/com/devcard/devcard/card/service/GroupService.java index 23574f1..1054034 100644 --- a/src/main/java/com/devcard/devcard/card/service/GroupService.java +++ b/src/main/java/com/devcard/devcard/card/service/GroupService.java @@ -6,23 +6,31 @@ import com.devcard.devcard.card.entity.Group; import com.devcard.devcard.card.repository.CardRepository; import com.devcard.devcard.card.repository.GroupRepository; +import com.devcard.devcard.chat.dto.CreateRoomRequest; +import com.devcard.devcard.chat.service.ChatRoomService; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.server.ResponseStatusException; -import java.util.List; -import java.util.stream.Collectors; - @Service public class GroupService { private final GroupRepository groupRepository; private final CardRepository cardRepository; + private final ChatRoomService chatRoomService; - public GroupService(GroupRepository groupRepository, CardRepository cardRepository) { + public GroupService( + GroupRepository groupRepository, + CardRepository cardRepository, + ChatRoomService chatRoomService + ) { this.groupRepository = groupRepository; this.cardRepository = cardRepository; + this.chatRoomService = chatRoomService; } @Transactional @@ -37,22 +45,29 @@ public List getGroupsByMember(Member member) { List groups = groupRepository.findByMember(member); return groups.stream() - .map(group -> new GroupResponseDto(group.getId(), group.getName(), group.getCount())) - .collect(Collectors.toList()); + .map(group -> new GroupResponseDto(group.getId(), group.getName(), group.getCount())) + .collect(Collectors.toList()); } @Transactional public void addCardToGroup(Long groupId, Long cardId, Member member) { Group group = groupRepository.findByIdAndMember(groupId, member) - .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); Card card = cardRepository.findById(cardId) - .orElseThrow(() -> new IllegalArgumentException("해당 ID의 명함이 존재하지 않습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 ID의 명함이 존재하지 않습니다.")); if (group.getCards().contains(card)) { throw new IllegalArgumentException("이미 해당 그룹에 추가되어 있는 명함입니다."); } + // 채팅방 생성 + CreateRoomRequest createRoomRequest = new CreateRoomRequest(Arrays.asList( + member.getId(), + card.getMember().getId() + )); + chatRoomService.createChatRoom(createRoomRequest); + group.addCard(card); groupRepository.save(group); } @@ -60,10 +75,16 @@ public void addCardToGroup(Long groupId, Long cardId, Member member) { @Transactional public void deleteCardFromGroup(Long groupId, Long cardId, Member member) { Group group = groupRepository.findByIdAndMember(groupId, member) - .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); Card card = cardRepository.findById(cardId) - .orElseThrow(() -> new IllegalArgumentException("해당 ID의 명함이 존재하지 않습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 ID의 명함이 존재하지 않습니다.")); + + // 채팅방 제거 + chatRoomService.deleteChatRoomByParticipants(Arrays.asList( + member.getId(), + card.getMember().getId() + )); group.removeCard(card); // 그룹에서 명함을 제거 groupRepository.save(group); // 변경사항 저장 @@ -72,9 +93,9 @@ public void deleteCardFromGroup(Long groupId, Long cardId, Member member) { @Transactional public void updateGroupName(Long groupId, String newName, Member member) { Group group = groupRepository.findByIdAndMember(groupId, member) - .orElseThrow(() -> new ResponseStatusException( - HttpStatus.BAD_REQUEST, "해당 그룹이 존재하지 않거나 접근 권한이 없습니다." - )); + .orElseThrow(() -> new ResponseStatusException( + HttpStatus.BAD_REQUEST, "해당 그룹이 존재하지 않거나 접근 권한이 없습니다." + )); group.setName(newName); groupRepository.save(group); @@ -83,7 +104,7 @@ public void updateGroupName(Long groupId, String newName, Member member) { @Transactional public void deleteGroup(Long groupId, Member member) { Group group = groupRepository.findByIdAndMember(groupId, member) - .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); + .orElseThrow(() -> new IllegalArgumentException("해당 그룹이 존재하지 않거나 접근 권한이 없습니다.")); groupRepository.delete(group); // 그룹 삭제 } diff --git a/src/main/java/com/devcard/devcard/chat/dto/CreateRoomRequest.java b/src/main/java/com/devcard/devcard/chat/dto/CreateRoomRequest.java index 26d6705..67b6a49 100644 --- a/src/main/java/com/devcard/devcard/chat/dto/CreateRoomRequest.java +++ b/src/main/java/com/devcard/devcard/chat/dto/CreateRoomRequest.java @@ -6,11 +6,10 @@ public class CreateRoomRequest { private List participantsId; - public List getParticipantsId() { - return participantsId; + public CreateRoomRequest(List list) { } - public void setParticipantsId(List participantsId) { - this.participantsId = participantsId; + public List getParticipantsId() { + return participantsId; } } diff --git a/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java b/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java index 31672af..d945a2c 100644 --- a/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java +++ b/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java @@ -1,7 +1,9 @@ package com.devcard.devcard.chat.repository; import com.devcard.devcard.chat.model.ChatRoom; +import java.util.Collection; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; @@ -10,4 +12,6 @@ public interface ChatRoomRepository extends JpaRepository { @Query("SELECT cr FROM ChatRoom cr JOIN cr.participants p WHERE p.id = :userId") List findByParticipantId(@Param("userId") String userId); + + Optional findByParticipantsIdIn(Collection> participantsId); } diff --git a/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java b/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java index acaa062..c4f9c76 100644 --- a/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java +++ b/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java @@ -1,6 +1,7 @@ package com.devcard.devcard.chat.service; import static com.devcard.devcard.chat.util.Constants.CHAT_ROOM_NOT_FOUND; +import static com.devcard.devcard.chat.util.Constants.CHAT_ROOM_NOT_FOUND_BY_PARTICIPANTS; import com.devcard.devcard.auth.entity.Member; import com.devcard.devcard.chat.dto.ChatMessageResponse; @@ -15,6 +16,7 @@ import com.devcard.devcard.chat.repository.ChatRoomRepository; import com.devcard.devcard.chat.repository.ChatUserRepository; import java.time.LocalDateTime; +import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import org.springframework.stereotype.Service; @@ -122,7 +124,7 @@ public ChatRoomResponse getChatRoomById(String chatId) { } /** - * 채팅방 삭제 + * chatId를 이용해 채팅방 삭제 * @param chatId 채팅방 ID */ public void deleteChatRoom(String chatId) { @@ -139,6 +141,23 @@ public void deleteChatRoom(String chatId) { chatRoomRepository.deleteById(chatRoomId); } + /** + * 참여자 ID를 이용해 채팅방 삭제 + * @param participantsId 채팅방에 참여하는 모든 유저의 ID List + */ + public void deleteChatRoomByParticipants(List participantsId) { + // 참여자 ID 목록으로 채팅방 조회 + ChatRoom chatRoom = chatRoomRepository.findByParticipantsIdIn(Collections.singleton(participantsId)) + .orElseThrow(() -> new ChatRoomNotFoundException( + CHAT_ROOM_NOT_FOUND_BY_PARTICIPANTS + participantsId.toString())); + + // 관련된 메시지 삭제 + chatRepository.deleteByChatRoomId(chatRoom.getId()); + + // 채팅방 삭제 + chatRoomRepository.delete(chatRoom); + } + /** * 특정 채팅방이 존재하는지 확인 * @param chatId 채팅방 ID diff --git a/src/main/java/com/devcard/devcard/chat/util/Constants.java b/src/main/java/com/devcard/devcard/chat/util/Constants.java index c78cdaf..5ce5a15 100644 --- a/src/main/java/com/devcard/devcard/chat/util/Constants.java +++ b/src/main/java/com/devcard/devcard/chat/util/Constants.java @@ -4,6 +4,7 @@ public class Constants { // 채팅방 상수 public static final String CHAT_ROOM_NOT_FOUND = "채팅방을 다음의 id로 찾을 수 없습니다. id: "; + public static final String CHAT_ROOM_NOT_FOUND_BY_PARTICIPANTS = "해당 참여자 ID 목록으로 채팅방을 찾을 수 없습니다. 참여자 ID 목록: "; public static final String USER_NOT_IN_CHAT_ROOM = "사용자가 해당 채팅방의 참여자가 아닙니다."; public static final String EMPTY_MESSAGE = "메시지가 비어 있습니다."; From b3a4dee199c53d288a6f97ac9e1f5600e178216f Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 02:18:59 +0900 Subject: [PATCH 05/23] fix: Correct participant ID handling in chat room deletion logic --- .../com/devcard/devcard/chat/repository/ChatRoomRepository.java | 2 +- .../java/com/devcard/devcard/chat/service/ChatRoomService.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java b/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java index d945a2c..78ea99b 100644 --- a/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java +++ b/src/main/java/com/devcard/devcard/chat/repository/ChatRoomRepository.java @@ -13,5 +13,5 @@ public interface ChatRoomRepository extends JpaRepository { @Query("SELECT cr FROM ChatRoom cr JOIN cr.participants p WHERE p.id = :userId") List findByParticipantId(@Param("userId") String userId); - Optional findByParticipantsIdIn(Collection> participantsId); + Optional findByParticipants_IdIn(Collection participantsId); } diff --git a/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java b/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java index c4f9c76..ea58167 100644 --- a/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java +++ b/src/main/java/com/devcard/devcard/chat/service/ChatRoomService.java @@ -147,7 +147,7 @@ public void deleteChatRoom(String chatId) { */ public void deleteChatRoomByParticipants(List participantsId) { // 참여자 ID 목록으로 채팅방 조회 - ChatRoom chatRoom = chatRoomRepository.findByParticipantsIdIn(Collections.singleton(participantsId)) + ChatRoom chatRoom = chatRoomRepository.findByParticipants_IdIn(participantsId) .orElseThrow(() -> new ChatRoomNotFoundException( CHAT_ROOM_NOT_FOUND_BY_PARTICIPANTS + participantsId.toString())); From 6cc7c5fe268c57a47f82ca6e4af9030287632f76 Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 02:22:14 +0900 Subject: [PATCH 06/23] chore: Update all localhost references to production deployment URL --- .../devcard/devcard/chat/service/ChatService.java | 2 +- src/main/resources/application.properties | 4 ++-- src/main/resources/static/js/card/card-share.js | 8 ++++---- src/main/resources/static/js/chat.js | 2 +- .../devcard/chat/service/ChatServiceTest.java | 12 ++++++------ 5 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/devcard/devcard/chat/service/ChatService.java b/src/main/java/com/devcard/devcard/chat/service/ChatService.java index 1f012fe..6f2c58b 100644 --- a/src/main/java/com/devcard/devcard/chat/service/ChatService.java +++ b/src/main/java/com/devcard/devcard/chat/service/ChatService.java @@ -212,7 +212,7 @@ public Long extractUserIdFromSession(WebSocketSession session) { * @return 해당 파라미터 값 (존재하지 않으면 null 반환) */ private Long extractParamFromUri(String uri, String paramName) { - // e.g. `ws://localhost:8080/ws?chatId=1&userId=1` 입력의 경우, + // e.g. `ws://3.34.144.148:8080/ws?chatId=1&userId=1` 입력의 경우, try { // "?"로 나누어 쿼리 파라미터 부분만 가져옴 (e.g. `chatId=1&userId=1`) String[] parts = uri.split("\\?"); diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1968c83..7b1d23d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -18,7 +18,7 @@ spring.jpa.show-sql=true #spring.datasource.url=jdbc:h2:mem:test;MODE=MYSQL;DB_CLOSE_DELAY=-1 #spring.datasource.username=sa #spring.datasource.password= -spring.datasource.url=jdbc:mysql://localhost:3306/devcard_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=UTF-8 +spring.datasource.url=jdbc:mysql://3.34.144.148:3306/devcard_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=UTF-8 spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver @@ -37,7 +37,7 @@ logging.level.org.springframework.security=DEBUG # QR Code Service -qr.domain.uri=http://localhost:8080/ +qr.domain.uri=http://3.34.144.148:8080/ qr.code.directory=src/main/resources/static/qrcodes/ # Kakao Service diff --git a/src/main/resources/static/js/card/card-share.js b/src/main/resources/static/js/card/card-share.js index e5ac237..6f0758c 100644 --- a/src/main/resources/static/js/card/card-share.js +++ b/src/main/resources/static/js/card/card-share.js @@ -8,16 +8,16 @@ document.getElementById('kakao-share-btn').addEventListener('click', function () description: '회사: [[${card.company}]], 직책: [[${card.position}]]', imageUrl: 'https://developers.kakao.com/assets/img/about/logos/kakaolink/kakaolink_btn_medium.png', link: { - mobileWebUrl: 'http://localhost:8080/cards/' + cardId + '/view', - webUrl: 'http://localhost:8080/cards/' + cardId + '/view' + mobileWebUrl: 'http://3.34.144.148:8080/cards/' + cardId + '/view', + webUrl: 'http://3.34.144.148:8080/cards/' + cardId + '/view' } }, buttons: [ { title: '명함 보기', link: { - mobileWebUrl: 'http://localhost:8080/cards/' + cardId + '/view', - webUrl: 'http://localhost:8080/cards/' + cardId + '/view' + mobileWebUrl: 'http://3.34.144.148:8080/cards/' + cardId + '/view', + webUrl: 'http://3.34.144.148:8080/cards/' + cardId + '/view' } } ], diff --git a/src/main/resources/static/js/chat.js b/src/main/resources/static/js/chat.js index 697f1ce..ccaa441 100644 --- a/src/main/resources/static/js/chat.js +++ b/src/main/resources/static/js/chat.js @@ -148,7 +148,7 @@ $(document).ready(function () { fetchChatRoom(chatId); // 임시로 로컬 서버 설정 - const socket = new WebSocket(`ws://localhost:8080/ws?chatId=${chatId}&userId=${memberId}`); + const socket = new WebSocket(`ws://3.34.144.148:8080/ws?chatId=${chatId}&userId=${memberId}`); // 웹소켓 연결 socket.addEventListener("open", () => { diff --git a/src/test/java/com/devcard/devcard/chat/service/ChatServiceTest.java b/src/test/java/com/devcard/devcard/chat/service/ChatServiceTest.java index b23d1c5..489a95d 100644 --- a/src/test/java/com/devcard/devcard/chat/service/ChatServiceTest.java +++ b/src/test/java/com/devcard/devcard/chat/service/ChatServiceTest.java @@ -370,7 +370,7 @@ void testExtractMessage_EmptyJsonObject() { @DisplayName("유효한 chatId 추출 성공") void testExtractChatIdFromSession_ValidChatId() { // 유효한 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws?chatId=1&userId=1")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws?chatId=1&userId=1")); // chatId가 1로 반환되는지 확인 Long chatId = chatService.extractChatIdFromSession(session); @@ -381,7 +381,7 @@ void testExtractChatIdFromSession_ValidChatId() { @DisplayName("쿼리 파라미터 없음 - chatId") void testExtractChatIdFromSession_NoChatId() { // chatId가 없는 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws")); // chatId가 없으므로 IllegalArgumentException 발생 Exception exception = assertThrows( @@ -395,7 +395,7 @@ void testExtractChatIdFromSession_NoChatId() { @DisplayName("chatId 숫자 형식 오류") void testExtractChatIdFromSession_InvalidChatIdFormat() { // chatId가 잘못된 형식인 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws?chatId=abc&userId=1")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws?chatId=abc&userId=1")); // chatId가 숫자 형식이 아니므로 IllegalArgumentException 발생 Exception exception = assertThrows( @@ -409,7 +409,7 @@ void testExtractChatIdFromSession_InvalidChatIdFormat() { @DisplayName("유효한 userId 추출 성공") void testExtractUserIdFromSession_ValidUserId() { // 유효한 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws?chatId=1&userId=1")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws?chatId=1&userId=1")); // userId가 1로 반환되는지 확인 Long userId = chatService.extractUserIdFromSession(session); @@ -420,7 +420,7 @@ void testExtractUserIdFromSession_ValidUserId() { @DisplayName("쿼리 파라미터 없음 - userId") void testExtractUserIdFromSession_NoUserId() { // userId가 없는 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws")); // userId가 없으므로 IllegalArgumentException 발생 Exception exception = assertThrows( @@ -434,7 +434,7 @@ void testExtractUserIdFromSession_NoUserId() { @DisplayName("userId 숫자 형식 오류") void testExtractUserIdFromSession_InvalidUserIdFormat() { // userId가 잘못된 형식인 URI 설정 - when(session.getUri()).thenReturn(URI.create("ws://localhost:8080/ws?chatId=1&userId=abc")); + when(session.getUri()).thenReturn(URI.create("ws://3.34.144.148:8080/ws?chatId=1&userId=abc")); // userId가 숫자 형식이 아니므로 IllegalArgumentException 발생 Exception exception = assertThrows( From 6d8e60dab51e8bc1c73e0593335b6239a82e16fb Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 02:28:16 +0900 Subject: [PATCH 07/23] build: Restrict deployment workflow only on merges to main repository --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a65112a..ecb4372 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -5,7 +5,7 @@ on: # push: # branches: [ "weekly/11"] pull_request: - branches: [ "Master" ] +# branches: [ "Master" ] types: [closed] # AWS 관련 값 변수로 설정 @@ -18,6 +18,7 @@ env: jobs: deploy: runs-on: ubuntu-latest + if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true permissions: contents: read packages: write From 637327d0c04e9dc821be80f46615d7656ae6a2f2 Mon Sep 17 00:00:00 2001 From: cussle <109949453+cussle@users.noreply.github.com> Date: Fri, 15 Nov 2024 02:43:10 +0900 Subject: [PATCH 08/23] Update main.yml --- .github/workflows/main.yml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ecb4372..c06e400 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,8 +2,8 @@ name: Build and Deploy to EC2 # 워크플로우가 언제 실행될 것인지 조건 명시 on: -# push: -# branches: [ "weekly/11"] + push: + branches: [ "weekly/11"] pull_request: # branches: [ "Master" ] types: [closed] @@ -40,13 +40,23 @@ jobs: touch ./application-secret.properties echo "${{ secrets.APPLICATION_SECRET }}" > ./application-secret.properties + - name: Check environment variables + run: | + echo "AWS_REGION: ${{ env.AWS_REGION }}" + echo "AWS_S3_BUCKET: ${{ env.AWS_S3_BUCKET }}" + echo "AWS_CODE_DEPLOY_APPLICATION: ${{ env.AWS_CODE_DEPLOY_APPLICATION }}" + echo "AWS_CODE_DEPLOY_GROUP: ${{ env.AWS_CODE_DEPLOY_GROUP }}" + echo "DATABASE_URL: ${{ secrets.DATABASE_URL }}" + echo "APPLICATION_SECRET is set" $([ -n "${{ secrets.APPLICATION_SECRET }}" ] && echo "Yes" || echo "No") + + # 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash - name: Build and Test - run: ./gradlew build test + run: ./gradlew build test --info # 빌드 파일을 zip 형식으로 압축 - name: Make zip file From ed98ce239b33daa30ba5d962ac3b5055bb5d63c2 Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 02:47:28 +0900 Subject: [PATCH 09/23] =?UTF-8?q?build:=20CI/CD=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ecb4372..d543e46 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,8 +2,8 @@ name: Build and Deploy to EC2 # 워크플로우가 언제 실행될 것인지 조건 명시 on: -# push: -# branches: [ "weekly/11"] + push: + branches: [ "weekly/11"] pull_request: # branches: [ "Master" ] types: [closed] @@ -18,7 +18,7 @@ env: jobs: deploy: runs-on: ubuntu-latest - if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true +# if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true permissions: contents: read packages: write @@ -46,7 +46,7 @@ jobs: shell: bash - name: Build and Test - run: ./gradlew build test + run: ./gradlew build test --info # 빌드 파일을 zip 형식으로 압축 - name: Make zip file From 30dd7300b8da7fc220327bdcbd3c9499e3a209a8 Mon Sep 17 00:00:00 2001 From: Thornappl2 <51190068+Thornappl2@users.noreply.github.com> Date: Fri, 15 Nov 2024 03:21:46 +0900 Subject: [PATCH 10/23] Update main.yml --- .github/workflows/main.yml | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 813bf30..54eaeae 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -32,13 +32,18 @@ jobs: java-version: '21' distribution: 'temurin' - # 공개되면 안되는 정보를 담은 .properties 파일을 생성 + # 개별 환경 변수를 사용하여 application-secret.properties 파일 생성 - name: make application-secret.properties run: | mkdir -p ./src/main/resources cd ./src/main/resources touch ./application-secret.properties - echo "${{ secrets.APPLICATION_SECRET }}" > ./application-secret.properties + echo "kakao.javascript.key=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> ./application-secret.properties + echo "GITHUB_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> ./application-secret.properties + echo "GITHUB_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> ./application-secret.properties + echo "GITHUB_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> ./application-secret.properties + echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> ./application-secret.properties + echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> ./application-secret.properties - name: Check environment variables run: | @@ -47,7 +52,6 @@ jobs: echo "AWS_CODE_DEPLOY_APPLICATION: ${{ env.AWS_CODE_DEPLOY_APPLICATION }}" echo "AWS_CODE_DEPLOY_GROUP: ${{ env.AWS_CODE_DEPLOY_GROUP }}" echo "DATABASE_URL: ${{ secrets.DATABASE_URL }}" - echo "APPLICATION_SECRET is set" $([ -n "${{ secrets.APPLICATION_SECRET }}" ] && echo "Yes" || echo "No") # 권한 부여 From 20c73fdedbe038f39e9f76abb3ab5afd1c1e0444 Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 03:24:56 +0900 Subject: [PATCH 11/23] =?UTF-8?q?properties:=20github=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20=EA=B0=92=20=EB=B3=80=EC=88=98=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application.properties | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 1968c83..4e9377d 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -24,9 +24,9 @@ spring.datasource.password=${DB_PASSWORD} spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # GitHub OAuth2 -spring.security.oauth2.client.registration.github.client-id=${GITHUB_CLIENT_ID} -spring.security.oauth2.client.registration.github.client-secret=${GITHUB_CLIENT_SECRET} -spring.security.oauth2.client.registration.github.redirect-uri=${GITHUB_REDIRECT_URI} +spring.security.oauth2.client.registration.github.client-id=${GH_CLIENT_ID} +spring.security.oauth2.client.registration.github.client-secret=${GH_CLIENT_SECRET} +spring.security.oauth2.client.registration.github.redirect-uri=${GH_REDIRECT_URI} spring.security.oauth2.client.registration.github.scope=user:email spring.security.oauth2.client.provider.github.token-uri=https://github.com/login/oauth/access_token spring.security.oauth2.client.provider.github.user-info-uri=https://api.github.com/user From 8dad395ab17fe1fec6df447092bcd004a5a7e3ae Mon Sep 17 00:00:00 2001 From: cussle <109949453+cussle@users.noreply.github.com> Date: Fri, 15 Nov 2024 03:27:37 +0900 Subject: [PATCH 12/23] =?UTF-8?q?build:=20CI/CD=20=ED=8C=8C=EC=9D=B4?= =?UTF-8?q?=ED=94=84=EB=9D=BC=EC=9D=B8=20=EC=B5=9C=EC=A2=85=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 54eaeae..aa0d3b7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,8 +2,8 @@ name: Build and Deploy to EC2 # 워크플로우가 언제 실행될 것인지 조건 명시 on: - push: - branches: [ "weekly/11"] +# push: +# branches: [ "weekly/11"] pull_request: # branches: [ "Master" ] types: [closed] @@ -18,7 +18,7 @@ env: jobs: deploy: runs-on: ubuntu-latest -# if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true + if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true permissions: contents: read packages: write From 59030fabaa6485b5aa814c48b026f25c4aea9ff5 Mon Sep 17 00:00:00 2001 From: cussle Date: Fri, 15 Nov 2024 03:47:36 +0900 Subject: [PATCH 13/23] =?UTF-8?q?build:=20=EB=B0=B0=ED=8F=AC=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A6=BD=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index aa0d3b7..ed1c41c 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -39,9 +39,9 @@ jobs: cd ./src/main/resources touch ./application-secret.properties echo "kakao.javascript.key=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> ./application-secret.properties - echo "GITHUB_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> ./application-secret.properties - echo "GITHUB_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> ./application-secret.properties - echo "GITHUB_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> ./application-secret.properties + echo "GH_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> ./application-secret.properties + echo "GH_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> ./application-secret.properties + echo "GH_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> ./application-secret.properties echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> ./application-secret.properties echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> ./application-secret.properties From fcd5867f8bc83740bae99d7429bda76f36a5d38d Mon Sep 17 00:00:00 2001 From: pswaao88 <66404498+pswaao88@users.noreply.github.com> Date: Fri, 15 Nov 2024 04:39:55 +0900 Subject: [PATCH 14/23] feat: add front code of Q n A --- .../controller/page/MyPageController.java | 42 +++++- .../devcard/mypage/dto/QnAResponse.java | 3 +- .../devcard/mypage/service/QnAService.java | 10 +- .../static/css/mypage/qna-common.css | 35 +++++ .../static/css/mypage/qna-create.css | 107 ++++++++++++++ .../static/css/mypage/qna-detail.css | 107 ++++++++++++++ .../resources/static/css/mypage/qna-list.css | 133 ++++++++++++++++++ src/main/resources/templates/mypage.html | 2 +- .../resources/templates/qna-create-new.html | 92 ++++++++++++ .../templates/qna-create-update.html | 91 ++++++++++++ src/main/resources/templates/qna-detail.html | 93 ++++++++++++ src/main/resources/templates/qna-list.html | 93 ++++++++++++ 12 files changed, 800 insertions(+), 8 deletions(-) create mode 100644 src/main/resources/static/css/mypage/qna-common.css create mode 100644 src/main/resources/static/css/mypage/qna-create.css create mode 100644 src/main/resources/static/css/mypage/qna-detail.css create mode 100644 src/main/resources/static/css/mypage/qna-list.css create mode 100644 src/main/resources/templates/qna-create-new.html create mode 100644 src/main/resources/templates/qna-create-update.html create mode 100644 src/main/resources/templates/qna-detail.html create mode 100644 src/main/resources/templates/qna-list.html diff --git a/src/main/java/com/devcard/devcard/mypage/controller/page/MyPageController.java b/src/main/java/com/devcard/devcard/mypage/controller/page/MyPageController.java index 5b2fc20..63a959f 100644 --- a/src/main/java/com/devcard/devcard/mypage/controller/page/MyPageController.java +++ b/src/main/java/com/devcard/devcard/mypage/controller/page/MyPageController.java @@ -1,6 +1,11 @@ package com.devcard.devcard.mypage.controller.page; +import com.devcard.devcard.auth.entity.Member; +import com.devcard.devcard.auth.repository.MemberRepository; import com.devcard.devcard.mypage.service.NoticeService; +import com.devcard.devcard.mypage.service.QnAService; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.core.user.OAuth2User; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -9,9 +14,14 @@ @Controller public class MyPageController { private final NoticeService noticeService; + private final QnAService qnAService; + private final MemberRepository memberRepository; - public MyPageController(NoticeService noticeService) { + public MyPageController(NoticeService noticeService, QnAService qnAService, + MemberRepository memberRepository) { this.noticeService = noticeService; + this.qnAService = qnAService; + this.memberRepository = memberRepository; } @GetMapping("/mypage/notice") @@ -25,4 +35,34 @@ public String getNotice(@PathVariable(name = "id") Long id, Model model) { model.addAttribute("notice", noticeService.getNotice(id)); return "notice-detail"; } + + @GetMapping("/mypage/qna") + public String getQnAList(Model model) { + model.addAttribute("qnaList", qnAService.getQnAList()); + return "qna-list"; + } + + @GetMapping("/mypage/qna/{id}") + public String getQnA(@PathVariable(name = "id") Long id, Model model) { + model.addAttribute("qna", qnAService.getQnA(id)); + return "qna-detail"; + } + + @GetMapping("/mypage/qna/create") + public String getQnACreate(Model model, Authentication authentication) { + OAuth2User oAuth2User = (OAuth2User) authentication.getPrincipal(); + String githubId = String.valueOf(oAuth2User.getAttributes().get("id")); + + // GitHub ID로 사용자를 찾아서 등록 여부 확인 + Member member = memberRepository.findByGithubId(githubId); + + model.addAttribute("member", member); + return "qna-create-new"; + } + + @GetMapping("/mypage/qna/create/{id}") + public String getQnAUpdate(@PathVariable(name = "id") Long id, Model model) { + model.addAttribute("qna", qnAService.getQnA(id)); + return "qna-create-update"; + } } diff --git a/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java b/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java index 5679426..5a120a1 100644 --- a/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java +++ b/src/main/java/com/devcard/devcard/mypage/dto/QnAResponse.java @@ -2,7 +2,8 @@ import java.time.LocalDateTime; -public record QnAResponse(String name, +public record QnAResponse(Long id, + String name, String questionTitle, String questionContent, String answer, diff --git a/src/main/java/com/devcard/devcard/mypage/service/QnAService.java b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java index 9752885..a527c7b 100644 --- a/src/main/java/com/devcard/devcard/mypage/service/QnAService.java +++ b/src/main/java/com/devcard/devcard/mypage/service/QnAService.java @@ -31,32 +31,32 @@ public List getQnAList() { public QnAResponse getQnA(Long id) { QnA qna = qnARepository.findQnAById(id); - return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + return new QnAResponse(qna.getId(), qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); } public QnAResponse addQnA(QnARequest qnARequest) { QnA qna = qnARepository.save(new QnA(qnARequest.name(), qnARequest.questionTitle(), qnARequest.questionContent(), LocalDateTime.now())); - return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + return new QnAResponse(qna.getId(), qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); } public QnAResponse updateQnA(QnAUpdateRequest qnAUpdateRequest) { QnA qna = qnARepository.findQnAById(qnAUpdateRequest.id()); qna.updateByRequest(qnAUpdateRequest.questionTitle(), qnAUpdateRequest.questionContent()); qnARepository.save(qna); - return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + return new QnAResponse(qna.getId(), qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); } public QnAResponse deleteQnA(Long id) { QnA qna = qnARepository.findQnAById(id); qnARepository.delete(qna); - return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + return new QnAResponse(qna.getId(), qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); } public QnAResponse updateAnswer(QnAAnswerDTO qnAAnswerDTO) { QnA qna = qnARepository.findQnAById(qnAAnswerDTO.id()); qna.updateAnswer(qnAAnswerDTO.answer()); - return new QnAResponse(qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); + return new QnAResponse(qna.getId(), qna.getName(), qna.getQuestionTitle(), qna.getQuestionContent(), qna.getAnswer(), qna.getQuestionTimestamp(), qna.getAnswerTimestamp(), qna.isAnswerCompleted()); } } diff --git a/src/main/resources/static/css/mypage/qna-common.css b/src/main/resources/static/css/mypage/qna-common.css new file mode 100644 index 0000000..24097cc --- /dev/null +++ b/src/main/resources/static/css/mypage/qna-common.css @@ -0,0 +1,35 @@ +:root { + --cnu-light-blue: #3f8efc; + --text-color-dark: #333; + --text-color-light: #888; + --background-color: #f9f9f9; + --border-radius: 8px; + --box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1); +} + +body { + margin: 0; + font-family: Arial, sans-serif; + color: var(--text-color-dark); + background-color: var(--background-color); +} + +/* 공통 헤더 스타일 */ +.header { + display: flex; + align-items: center; + font-size: 1.5rem; + padding: 1rem; + color: white; + background-color: var(--cnu-light-blue); + gap: 0.5rem; +} + +.header #backButton { + cursor: pointer; + font-size: 1.5rem; +} + +.header .title { + margin-left: 1.5rem; +} diff --git a/src/main/resources/static/css/mypage/qna-create.css b/src/main/resources/static/css/mypage/qna-create.css new file mode 100644 index 0000000..c63a874 --- /dev/null +++ b/src/main/resources/static/css/mypage/qna-create.css @@ -0,0 +1,107 @@ +/* 전체 배경 색상 */ +html, body { + background-color: white; + font-family: Arial, sans-serif; + margin: 0; /* 상하 여백 제거 */ + padding: 0; /* 상하 여백 제거 */ + width: 100%; /* 화면 전체 너비 사용 */ + height: 100%; /* 화면 전체 높이 사용 */ + box-sizing: border-box; /* padding과 border가 전체 크기 안에 포함되도록 설정 */ +} + +/* QnA 상세 헤더 */ +.header { + background-color: #3f8efc; + color: white; + display: flex; + align-items: center; + padding: 10px 15px; /* 헤더 영역의 상하 여백 줄임 */ + width: 100%; /* 화면 너비 꽉 채우기 */ + box-sizing: border-box; +} + +/* 뒤로 가기 버튼 */ +#backButton { + font-size: 24px; + cursor: pointer; + margin-right: 10px; +} + +/* 제목 중앙 정렬 */ +.title { + font-size: 1.5rem; + flex-grow: 1; + text-align: center; + margin: 0; /* 제목과 상단 바 간격을 없앰 */ + padding: 0; /* 기본 여백 제거 */ +} + +/* QnA 작성 폼 */ +.qna-create-container { + width: 100%; /* 화면 너비 100% 사용 */ + max-width: 100%; /* 최대 너비 제한 해제 */ + margin: 0 auto; /* 자동 여백으로 화면 가운데 배치 */ + padding: 1rem; /* 충분한 여백을 주어 화면이 꽉 차게 */ + box-sizing: border-box; + display: flex; + flex-direction: column; + justify-content: space-between; + margin-top: 0; /* 헤더와의 간격을 없앰 */ +} + +/* 폼 그룹 */ +.form-group { + margin-bottom: 1.5rem; /* 각 입력 항목 사이 여백 증가 */ +} + +label { + font-size: 1.1rem; + font-weight: bold; + display: block; + margin-bottom: 0.5rem; +} + +/* 입력 필드 */ +input[type="text"], textarea { + width: 100%; + padding: 12px; /* 입력 필드 크기 확장 */ + font-size: 1rem; + border: 1px solid #ccc; + border-radius: 4px; + box-sizing: border-box; /* 패딩과 border를 포함한 크기 계산 */ +} + +textarea { + resize: vertical; + min-height: 150px; /* 최소 높이 설정 */ +} + +/* 제출 버튼 */ +.submit-button { + background-color: #3f8efc; + color: white; + border: none; + padding: 15px 20px; /* 더 큰 버튼 크기 */ + border-radius: 5px; + cursor: pointer; + font-size: 1rem; + width: 100%; + box-sizing: border-box; /* 버튼 크기 포함 */ +} + +.submit-button:hover { + background-color: #3371d1; +} + +/* Toast 알림 */ +.toast { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background-color: #333; + color: white; + padding: 10px 20px; + border-radius: 5px; + display: none; +} diff --git a/src/main/resources/static/css/mypage/qna-detail.css b/src/main/resources/static/css/mypage/qna-detail.css new file mode 100644 index 0000000..75801fa --- /dev/null +++ b/src/main/resources/static/css/mypage/qna-detail.css @@ -0,0 +1,107 @@ +/* 전체 배경 색상 */ +body { + background-color: white; + font-family: Arial, sans-serif; + margin: 0; + padding: 0; +} + +/* QnA 상세 헤더 */ +.header { + background-color: #3f8efc; + color: white; + display: flex; + align-items: center; + padding: 10px; +} + +#backButton { + font-size: 24px; + cursor: pointer; + margin-right: 10px; +} + +.title { + font-size: 1.5rem; + flex-grow: 1; + text-align: center; /* 제목만 중앙 정렬 */ +} + +/* QnA 세부 내용 */ +.qna-detail { + max-width: 800px; + margin: 1.5rem auto; + padding: 1rem; +} + +.qna-header { + margin-bottom: 1rem; +} + +#detail-title { + font-size: 1.5rem; + font-weight: bold; + text-align: left; /* 제목을 왼쪽으로 정렬 */ +} + +#detail-info { + color: #888; + font-size: 0.9rem; + text-align: left; /* 작성자, 시간도 왼쪽 정렬 */ +} + +#detail-content { + margin-bottom: 1.5rem; + font-size: 1rem; + color: #333; + text-align: left; /* 질문 내용 왼쪽 정렬 */ +} + +/* 답변 섹션 */ +.answer-section { + margin-top: 2rem; + text-align: left; /* 답변 섹션 왼쪽 정렬 */ +} + +.answer-section h3 { + font-size: 1.2rem; + color: #333; + margin-bottom: 0.5rem; +} + +#answer-content { + font-size: 1rem; + color: #555; +} + +#answer-content span { + font-style: italic; + color: #999; +} + +/* 기본 스타일을 위한 Toast 알림 */ +.toast { + position: fixed; + bottom: 20px; + left: 50%; + transform: translateX(-50%); + background-color: #333; + color: white; + padding: 10px 20px; + border-radius: 5px; + display: none; +} + +.create-button { + background-color: white; /* 버튼 배경색 */ + color: black; /* 버튼 텍스트 색상 */ + padding: 10px 20px; /* 패딩 */ + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 16px; +} + +.create-button:hover { + background-color: white; /* 버튼 호버 시 색상 */ +} diff --git a/src/main/resources/static/css/mypage/qna-list.css b/src/main/resources/static/css/mypage/qna-list.css new file mode 100644 index 0000000..d601186 --- /dev/null +++ b/src/main/resources/static/css/mypage/qna-list.css @@ -0,0 +1,133 @@ +/* 전체 배경 스타일 */ +body { + font-family: Arial, sans-serif; + margin: 0; + padding: 0; + background-color: #f5f5f5; +} + +/* 헤더 스타일 */ +.header { + display: flex; + align-items: center; + padding: 10px; + background-color: #3f8efc; + color: white; +} + +/* 뒤로가기 버튼 스타일 */ +#backButton { + font-size: 24px; + cursor: pointer; + margin-right: 10px; + margin-top: 5px; +} + +/* 제목 스타일 */ +.title { + font-size: 1.5rem; + font-weight: bold; + flex-grow: 1; + text-align: center; +} + +/* Q & A 리스트 컨테이너 */ +.qna-list-container { + max-width: 800px; + margin: 1.5rem auto; + padding: 0 1rem; +} + +/* Q & A 항목 스타일 */ +.qna-item { + list-style: none; + padding: 1rem 1.5rem; + margin: 0.75rem 0; + background-color: white; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + display: flex; + align-items: center; + justify-content: space-between; /* 항목들을 양옆에 배치 */ + transition: background-color 0.2s; +} + +.qna-item:hover { + background-color: #f0f5ff; +} + +/* 각 항목 스타일: ID, 작성자, 제목, 날짜, 상태 */ +.qna-id, +.qna-name, +.qna-title, +.qna-date, +.qna-status { + font-size: 0.9rem; + color: #888; +} + +.qna-id { + font-weight: bold; + margin-right: 10px; +} + +.qna-name { + color: #3f8efc; +} + +.qna-title { + font-size: 1.1rem; + font-weight: bold; + color: #3f8efc; + margin-left: 1rem; +} + +.qna-date { + color: #888; + margin-left: auto; /* 오른쪽 끝으로 날짜 배치 */ +} + +.qna-status { + color: #888; + margin-left: 1rem; /* 상태는 제목 오른쪽에 배치 */ +} + +/* Q & A 리스트 항목에 간격 추가 */ +.qna-item > div { + margin-right: 1rem; +} + +/* Q & A 제목, 상태, 날짜 등을 정렬하는 스타일 */ +.qna-item > .qna-id { + width: 50px; /* ID의 고정 너비 */ +} + +.qna-item > .qna-name { + width: 150px; /* 작성자의 고정 너비 */ +} + +.qna-item > .qna-title { + flex-grow: 2; /* 제목은 남은 공간을 차지 */ +} + +.qna-item > .qna-date { + width: 120px; /* 날짜의 고정 너비 */ +} + +.qna-item > .qna-status { + width: 100px; /* 상태의 고정 너비 */ +} + +.create-button { + background-color: white; /* 버튼 배경색 */ + color: black; /* 버튼 텍스트 색상 */ + padding: 10px 20px; /* 패딩 */ + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 16px; +} + +.create-button:hover { + background-color: white; /* 버튼 호버 시 색상 */ +} diff --git a/src/main/resources/templates/mypage.html b/src/main/resources/templates/mypage.html index 77e5eec..151b754 100644 --- a/src/main/resources/templates/mypage.html +++ b/src/main/resources/templates/mypage.html @@ -36,7 +36,7 @@

- + Customer Service Icon

Q & A

diff --git a/src/main/resources/templates/qna-create-new.html b/src/main/resources/templates/qna-create-new.html new file mode 100644 index 0000000..a5e18f8 --- /dev/null +++ b/src/main/resources/templates/qna-create-new.html @@ -0,0 +1,92 @@ + + + + + + + QnA 작성 + + + + + + + + + + + + + + + +
+
+ arrow_back_ios +

QnA 작성

+
+ + +
+ +
+ + +
+ +
+ + +
+ + + + +
+
+ + +
+ + +
+ + + + + diff --git a/src/main/resources/templates/qna-create-update.html b/src/main/resources/templates/qna-create-update.html new file mode 100644 index 0000000..1bd1b17 --- /dev/null +++ b/src/main/resources/templates/qna-create-update.html @@ -0,0 +1,91 @@ + + + + + + + QnA 작성 + + + + + + + + + + + + + + +
+
+ arrow_back_ios +

QnA 작성

+
+ + +
+ + +
+ + +
+ +
+ + +
+ + +
+
+ + + +
+ + +
+ + + + + diff --git a/src/main/resources/templates/qna-detail.html b/src/main/resources/templates/qna-detail.html new file mode 100644 index 0000000..d85fa70 --- /dev/null +++ b/src/main/resources/templates/qna-detail.html @@ -0,0 +1,93 @@ + + + + + + + QnA 상세 + + + + + + + + + + + + + +
+ arrow_back_ios +

QnA 상세

+ + +
+ + +
+ +
+

[[${qna.questionTitle}]]

+

+ 작성자: [[${qna.name}]] / 시간: [[${qna.questionTimestamp}]] +

+
+ + +
+ [[${qna.questionContent}]] +
+ + +
+

답변

+
+ 답변 내용 + 아직 답변이 달리지 않았습니다. +
+
+
+ + +
+ + +
+ + + + + diff --git a/src/main/resources/templates/qna-list.html b/src/main/resources/templates/qna-list.html new file mode 100644 index 0000000..9fb4f9e --- /dev/null +++ b/src/main/resources/templates/qna-list.html @@ -0,0 +1,93 @@ + + + + + + + QnA 목록 + + + + + + + + + + + + + + + +
+
+ arrow_back_ios +

QnA 목록

+ +
+ + + +
+ + +
+ + +
+ + + + + + + + + + + + From aa9bdce740c9a3a2d3f47c988ecf3396eb33ed89 Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:21:47 +0900 Subject: [PATCH 15/23] =?UTF-8?q?fix:=20DB=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=A0=84=EB=8B=AC=20=EA=B3=BC=EC=A0=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 37 +++++++++++------------ src/main/resources/application.properties | 6 +--- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ed1c41c..085e836 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,10 +2,7 @@ name: Build and Deploy to EC2 # 워크플로우가 언제 실행될 것인지 조건 명시 on: -# push: -# branches: [ "weekly/11"] pull_request: -# branches: [ "Master" ] types: [closed] # AWS 관련 값 변수로 설정 @@ -25,41 +22,43 @@ jobs: steps: - uses: actions/checkout@v4 - # JDK 17 설치 + # JDK 21 설치 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' - # 개별 환경 변수를 사용하여 application-secret.properties 파일 생성 - - name: make application-secret.properties - run: | - mkdir -p ./src/main/resources - cd ./src/main/resources - touch ./application-secret.properties - echo "kakao.javascript.key=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> ./application-secret.properties - echo "GH_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> ./application-secret.properties - echo "GH_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> ./application-secret.properties - echo "GH_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> ./application-secret.properties - echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> ./application-secret.properties - echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> ./application-secret.properties + # `application-secret.properties` 생성 단계 제거 + # 환경 변수 확인 단계 수정 - name: Check environment variables run: | echo "AWS_REGION: ${{ env.AWS_REGION }}" echo "AWS_S3_BUCKET: ${{ env.AWS_S3_BUCKET }}" echo "AWS_CODE_DEPLOY_APPLICATION: ${{ env.AWS_CODE_DEPLOY_APPLICATION }}" echo "AWS_CODE_DEPLOY_GROUP: ${{ env.AWS_CODE_DEPLOY_GROUP }}" - echo "DATABASE_URL: ${{ secrets.DATABASE_URL }}" - + echo "DB_USERNAME: ${{ secrets.DB_USERNAME }}" + echo "DB_PASSWORD: ${{ secrets.DB_PASSWORD }}" + echo "KAKAO_JAVASCRIPT_KEY: ${{ secrets.KAKAO_JAVASCRIPT_KEY }}" + echo "GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}" + echo "GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }}" + echo "GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }}" # 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash + # Build and Test 단계에서 환경 변수 사용 - name: Build and Test + env: + DB_USERNAME: ${{ secrets.DB_USERNAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + KAKAO_JAVASCRIPT_KEY: ${{ secrets.KAKAO_JAVASCRIPT_KEY }} + GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }} + GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }} + GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }} run: ./gradlew build test --info # 빌드 파일을 zip 형식으로 압축 @@ -67,7 +66,7 @@ jobs: run: zip -r ./$GITHUB_SHA.zip . shell: bash - # AWS 권한 + # AWS 자격 증명 설정 - name: AWS credential 설정 uses: aws-actions/configure-aws-credentials@v1 with: diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 445a46b..5c4ee58 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -8,16 +8,11 @@ spring.sql.init.data-locations=classpath:data.sql # JPA spring.jpa.hibernate.ddl-auto=update spring.jpa.defer-datasource-initialization=true -#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.H2Dialect spring.jpa.database-platform=org.hibernate.dialect.MySQL8Dialect spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true # db -#spring.h2.console.enabled=true -#spring.datasource.url=jdbc:h2:mem:test;MODE=MYSQL;DB_CLOSE_DELAY=-1 -#spring.datasource.username=sa -#spring.datasource.password= spring.datasource.url=jdbc:mysql://3.34.144.148:3306/devcard_db?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC&characterEncoding=UTF-8 spring.datasource.username=${DB_USERNAME} spring.datasource.password=${DB_PASSWORD} @@ -41,4 +36,5 @@ qr.domain.uri=http://3.34.144.148:8080/ qr.code.directory=src/main/resources/static/qrcodes/ # Kakao Service +# application-secret.properties? ????? ?? spring.config.import=optional:classpath:application-secret.properties From 1ce21c306370dc76c3fa2fa4ee893307cfde6e5b Mon Sep 17 00:00:00 2001 From: Thornappl2 <51190068+Thornappl2@users.noreply.github.com> Date: Fri, 15 Nov 2024 05:38:17 +0900 Subject: [PATCH 16/23] Update main.yml --- .github/workflows/main.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 085e836..283aa97 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,7 +29,15 @@ jobs: java-version: '21' distribution: 'temurin' - # `application-secret.properties` 생성 단계 제거 + # `application-secret.properties` 생성 + - name: Create application-secret.properties + run: | + echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> src/main/resources/application-secret.properties + echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> src/main/resources/application-secret.properties + echo "GH_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> src/main/resources/application-secret.properties + echo "GH_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> src/main/resources/application-secret.properties + echo "GH_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> src/main/resources/application-secret.properties + echo "KAKAO_JAVASCRIPT_KEY=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> src/main/resources/application-secret.properties # 환경 변수 확인 단계 수정 - name: Check environment variables From ec9065900def0cf418c3d2c02de8f6a686c4b4da Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:46:37 +0900 Subject: [PATCH 17/23] =?UTF-8?q?fix:=20DB=20=ED=99=98=EA=B2=BD=20?= =?UTF-8?q?=EB=B3=80=EC=88=98=20=EC=A0=84=EB=8B=AC=20=EA=B3=BC=EC=A0=95=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 085e836..bf89c53 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,8 +29,6 @@ jobs: java-version: '21' distribution: 'temurin' - # `application-secret.properties` 생성 단계 제거 - # 환경 변수 확인 단계 수정 - name: Check environment variables run: | From 1b6d6a2a908729b3332a1c5bd170c5c76e6f1dfb Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:48:55 +0900 Subject: [PATCH 18/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index bf89c53..efbfb50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,6 +29,8 @@ jobs: java-version: '21' distribution: 'temurin' + # `application-secret.properties` 생성 단계 제거 + # 환경 변수 확인 단계 수정 - name: Check environment variables run: | @@ -42,7 +44,6 @@ jobs: echo "GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}" echo "GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }}" echo "GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }}" - # 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew From a05419ebd02a38333ca69abc0fc2ffc3298ff39b Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:49:55 +0900 Subject: [PATCH 19/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 283aa97..085e836 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -29,15 +29,7 @@ jobs: java-version: '21' distribution: 'temurin' - # `application-secret.properties` 생성 - - name: Create application-secret.properties - run: | - echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> src/main/resources/application-secret.properties - echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> src/main/resources/application-secret.properties - echo "GH_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> src/main/resources/application-secret.properties - echo "GH_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> src/main/resources/application-secret.properties - echo "GH_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> src/main/resources/application-secret.properties - echo "KAKAO_JAVASCRIPT_KEY=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> src/main/resources/application-secret.properties + # `application-secret.properties` 생성 단계 제거 # 환경 변수 확인 단계 수정 - name: Check environment variables From 652a91c78d786b7801e5b16d499570e9082de55e Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:48:55 +0900 Subject: [PATCH 20/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 085e836..efbfb50 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -44,7 +44,6 @@ jobs: echo "GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}" echo "GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }}" echo "GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }}" - # 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew From d5995ac21fb4a24bc239edc37b1b85f44f234ef5 Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:55:38 +0900 Subject: [PATCH 21/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index efbfb50..3642af7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -2,7 +2,10 @@ name: Build and Deploy to EC2 # 워크플로우가 언제 실행될 것인지 조건 명시 on: + push: + branches: [ "weekly/11"] pull_request: + # branches: [ "Master" ] types: [closed] # AWS 관련 값 변수로 설정 @@ -15,49 +18,44 @@ env: jobs: deploy: runs-on: ubuntu-latest - if: github.repository == 'kakao-tech-campus-2nd-step3/Team28_BE' && github.event.pull_request.merged == true permissions: contents: read packages: write steps: - uses: actions/checkout@v4 - # JDK 21 설치 + # JDK 17 설치 - name: Set up JDK 21 uses: actions/setup-java@v4 with: java-version: '21' distribution: 'temurin' - # `application-secret.properties` 생성 단계 제거 - - # 환경 변수 확인 단계 수정 + # 개별 환경 변수를 사용하여 application-secret.properties 파일 생성 + - name: make application-secret.properties + run: | + mkdir -p ./src/main/resources + cd ./src/main/resources + touch ./application-secret.properties + echo "kakao.javascript.key=${{ secrets.KAKAO_JAVASCRIPT_KEY }}" >> ./application-secret.properties + echo "GITHUB_CLIENT_ID=${{ secrets.GH_CLIENT_ID }}" >> ./application-secret.properties + echo "GITHUB_CLIENT_SECRET=${{ secrets.GH_CLIENT_SECRET }}" >> ./application-secret.properties + echo "GITHUB_REDIRECT_URI=${{ secrets.GH_REDIRECT_URI }}" >> ./application-secret.properties + echo "DB_USERNAME=${{ secrets.DB_USERNAME }}" >> ./application-secret.properties + echo "DB_PASSWORD=${{ secrets.DB_PASSWORD }}" >> ./application-secret.properties - name: Check environment variables run: | echo "AWS_REGION: ${{ env.AWS_REGION }}" echo "AWS_S3_BUCKET: ${{ env.AWS_S3_BUCKET }}" echo "AWS_CODE_DEPLOY_APPLICATION: ${{ env.AWS_CODE_DEPLOY_APPLICATION }}" echo "AWS_CODE_DEPLOY_GROUP: ${{ env.AWS_CODE_DEPLOY_GROUP }}" - echo "DB_USERNAME: ${{ secrets.DB_USERNAME }}" - echo "DB_PASSWORD: ${{ secrets.DB_PASSWORD }}" - echo "KAKAO_JAVASCRIPT_KEY: ${{ secrets.KAKAO_JAVASCRIPT_KEY }}" - echo "GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }}" - echo "GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }}" - echo "GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }}" + echo "DATABASE_URL: ${{ secrets.DATABASE_URL }}" # 권한 부여 - name: Grant execute permission for gradlew run: chmod +x ./gradlew shell: bash - # Build and Test 단계에서 환경 변수 사용 - name: Build and Test - env: - DB_USERNAME: ${{ secrets.DB_USERNAME }} - DB_PASSWORD: ${{ secrets.DB_PASSWORD }} - KAKAO_JAVASCRIPT_KEY: ${{ secrets.KAKAO_JAVASCRIPT_KEY }} - GH_CLIENT_ID: ${{ secrets.GH_CLIENT_ID }} - GH_CLIENT_SECRET: ${{ secrets.GH_CLIENT_SECRET }} - GH_REDIRECT_URI: ${{ secrets.GH_REDIRECT_URI }} run: ./gradlew build test --info # 빌드 파일을 zip 형식으로 압축 @@ -65,7 +63,7 @@ jobs: run: zip -r ./$GITHUB_SHA.zip . shell: bash - # AWS 자격 증명 설정 + # AWS 권한 - name: AWS credential 설정 uses: aws-actions/configure-aws-credentials@v1 with: From 548284b6373474653aed4114544534eeeafaa8b9 Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 05:58:54 +0900 Subject: [PATCH 22/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3642af7..e2609c4 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -4,9 +4,6 @@ name: Build and Deploy to EC2 on: push: branches: [ "weekly/11"] - pull_request: - # branches: [ "Master" ] - types: [closed] # AWS 관련 값 변수로 설정 env: From ddcdf1ad41ecfb6af9a2f8267636163b1edeb398 Mon Sep 17 00:00:00 2001 From: donghyeon Date: Fri, 15 Nov 2024 06:00:34 +0900 Subject: [PATCH 23/23] =?UTF-8?q?fix:=20main.yml=20=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e2609c4..c93f561 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,6 +1,6 @@ name: Build and Deploy to EC2 -# 워크플로우가 언제 실행될 것인지 조건 명시 +# 워크플로우가 언제 실행될 것인지 조건 설정 on: push: branches: [ "weekly/11"]