Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

외래키 제거 완료 #390

Closed
wants to merge 49 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
db5491a
refactor(Slack): 젠킨스 빌드 메시지 개선
limehee Jun 19, 2024
a815832
fix(Slack): 슬랙 메시지 전송 메소드명 중복에 따른 오류 수정
limehee Jun 19, 2024
7547ef1
fix(Slack): 슬랙 메시지 전송 메소드명 중복 및 base url에 따른 오류 수정
limehee Jun 19, 2024
38d7d21
refactor(Jenkins): 헬스 체크 시간 4분->2.5분 축소
limehee Jun 19, 2024
97be3b2
refactor(Slack): 젠킨스 빌드 로그 대신 실패 단계를 출력하도록 변경
limehee Jun 19, 2024
7689d12
refactor(Slack): 젠킨스 빌드 로그 대신 실패 단계를 출력하도록 변경
limehee Jun 19, 2024
75535d5
refactor(Slack): 페이로드를 JSON 객체로 정의하여 쉽게 변경 가능하도록 함
limehee Jun 19, 2024
ed944e8
Merge pull request #382 from KGU-C-Lab/refactor/#381
limehee Jun 19, 2024
2288a52
fix(Jenkins): Jenkinsfile에 트리거를 추가하여 Github Webhook이 정상 작동하도록 수정
limehee Jun 19, 2024
05be456
Webhook test
limehee Jun 19, 2024
dc0be0f
개행 제거
limehee Jun 19, 2024
31aa4be
Merge pull request #384 from KGU-C-Lab/fix/#383
limehee Jun 19, 2024
8a3f65b
슬랙 알림 로직 분리 및 유동적 알림 설정 가능하도록 변경 완료 (#386)
limehee Jun 21, 2024
2725396
모집공고 상태 자동변경 로직 작성 완료 (#389)
SongJaeHoonn Jun 25, 2024
3d206ac
refactor(Member): MemberLookupService 분리
limehee Jun 22, 2024
67a7126
feat(Member): 도메인 이벤트, 공통 리스너, 이벤트 디스패처 추가
limehee Jun 23, 2024
de8b01e
feat(Member): 수정, 삭제시 도메인 이벤트가 발생하도록 함
limehee Jun 23, 2024
fb52e86
fix(Notification): MemberLookupService 의존성 주입 오류 수정
limehee Jun 23, 2024
59d79d3
refactor(Accuse): Accuse에서 Member를 외래키로 참조하지 않도록 변경
limehee Jun 23, 2024
d986c99
feat(Accuse): Member가 삭제되면, Accuse도 같이 삭제되도록 변경
limehee Jun 23, 2024
a88a474
feat(Accuse): 소프트 딜리트 추가
limehee Jun 23, 2024
61bb43a
feat(Award): Award에서 Member를 외래키로 참조하지 않도록 변경
limehee Jun 25, 2024
3a63c93
refactor(Blog): Blog에서 Member를 외래키로 참조하지 않도록 변경
limehee Jun 25, 2024
6d52b34
refactor(Board): Board에서 Member를 외래키로 참조하지 않도록 변경
limehee Jun 25, 2024
d5e5eba
refactor(Member): Member 정보 변경시 memberId가 아닌 Member 객체를 이벤트로 보내도록 수정
limehee Jun 25, 2024
d1f850b
refactor(Member): Book에서 Member를 외래키로 참조하지 않도록 변경
limehee Jun 25, 2024
faed6a9
refactor(Award): 삭제 및 권한 검사 로직 개선
limehee Jun 25, 2024
86a911c
refactor(Blog): 삭제 및 권한 검사 로직 개선
limehee Jun 25, 2024
20359e2
refactor(Blog): 도메인 객체가 직접적으로 Member 도메인 객체를 참조하지 않도록 변경
limehee Jun 25, 2024
8900225
refactor(Accuse): 신고 알림을 보낼 때 memberId를 이용하도록 변경
limehee Jun 25, 2024
226962c
refactor(Accuse): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 25, 2024
4ba27f3
refactor(Blog): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 25, 2024
fa0ffc4
refactor(Member): MemberInfoDto -> MemberBasicInfoDto 클래스명 변경
limehee Jun 25, 2024
ff63cd9
feat(Member): MemberDetailedInfoDto 추가
limehee Jun 25, 2024
369e1c3
refactor(Board): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 25, 2024
dc8107f
refactor(Award): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 25, 2024
7601e71
refactor(Book): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 25, 2024
0ec4056
refactor(Comment): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 26, 2024
9415198
feat(Comment): EventProcessor 추가
limehee Jun 26, 2024
7f620c9
refactor(Donation): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 26, 2024
fc2414c
refactor(AccountLockInfo): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고…
limehee Jun 26, 2024
b35782a
refactor(Login): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 26, 2024
ce7fa67
refactor(Event): 불필요한 코드 제거
limehee Jun 26, 2024
0fa465f
refactor(MembershipFee): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도…
limehee Jun 26, 2024
63a0448
refactor(Notification): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 26, 2024
2b4dee1
refactor(Position): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받도록 변경
limehee Jun 26, 2024
534f0b7
refactor(WorkExperience): Member 도메인 객체를 직접 참조하지 않고, 필요한 정보를 DTO로 주고받…
limehee Jun 26, 2024
6a719fe
refactor(EventProcessor): 메소드명 변경
limehee Jun 26, 2024
ac42e20
refactor(EventProcessor): Member 수정/삭제 이벤트에 대해 최소한의 정보만 전달하도록 변경
limehee Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 84 additions & 15 deletions jenkins/Jenkinsfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
pipeline {
agent any

triggers {
githubPush()
}

environment {
JENKINS_DOMAIN = credentials('jenkins_domain')

SLACK_WEBHOOK_URL = credentials('slack_webhook_url')
SLACK_COLOR_SUCCESS = credentials('slack_color_success')
SLACK_COLOR_FAILURE = credentials('slack_color_failure')
Expand Down Expand Up @@ -134,33 +140,96 @@ pipeline {
post {
failure {
script {
sendSlackNotification("${env.GIT_CHANGELOG}\n:scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE)
sendSlackBuildNotification(":scream_cat: Deployment failed.", env.SLACK_COLOR_FAILURE)
}
}

success {
script {
sendSlackNotification("${env.GIT_CHANGELOG}\n:rocket: Deployment completed successfully.", env.SLACK_COLOR_SUCCESS)
sendSlackBuildNotification(":rocket: Deployment completed successfully", env.SLACK_COLOR_SUCCESS)
}
}
}
}

def sendSlackNotification(message, color) {
withEnv([
"SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"
]) {
def payload = """{
"attachments": [
{
"color": "${color}",
"text": "${message.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')}"
}
def sendSlackNotification(String message, String color) {
def payload = [
attachments: [
[
color: color,
text: message.replaceAll('"', '\\"').replaceAll('\n', '\\\\n')
]
]
]

withEnv(["SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"]) {
def payloadJson = groovy.json.JsonOutput.toJson(payload)
sh """
curl -X POST -H 'Content-type: application/json' --data '${payloadJson}' ${SLACK_WEBHOOK_URL}
"""
}
}


def sendSlackBuildNotification(String message, String color) {
def jobUrl = "${env.JENKINS_DOMAIN}/job/${env.JOB_NAME}"
def consoleOutputUrl = "${jobUrl}/${env.BUILD_NUMBER}/console"

def payload = [
blocks: [
[
type: "section",
text: [
type: "mrkdwn",
text: message
]
]
],
attachments: [
[
color: color,
blocks: [
[
type: "section",
text: [
type: "mrkdwn",
text: "*Change Log:*\n${env.GIT_CHANGELOG}"
]
],
[
type: "actions",
elements: [
[
type: "button",
text: [
type: "plain_text",
text: "Job",
emoji: true
],
url: jobUrl,
value: "click_job"
],
[
type: "button",
text: [
type: "plain_text",
text: "Console Output",
emoji: true
],
url: consoleOutputUrl,
value: "click_console_output"
]
]
]
].findAll { it != null }
]
}"""
]
]

withEnv(["SLACK_WEBHOOK_URL=${env.SLACK_WEBHOOK_URL}"]) {
def payloadJson = groovy.json.JsonOutput.toJson(payload)
sh """
curl -X POST --data-urlencode 'payload=${payload}' ${SLACK_WEBHOOK_URL}
curl -X POST -H 'Content-type: application/json' --data '${payloadJson}' ${SLACK_WEBHOOK_URL}
"""
}
}
Expand Down Expand Up @@ -319,7 +388,7 @@ def performHealthCheck() {
echo "Public IP address: ${PUBLIC_IP}"

def start_time = System.currentTimeMillis()
def timeout = start_time + 240000 // 4 minutes
def timeout = start_time + 150000 // 2.5 minutes

while (System.currentTimeMillis() < timeout) {
def elapsed = (System.currentTimeMillis() - start_time) / 1000
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package page.clab.api.domain.accuse.application;

import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import page.clab.api.domain.accuse.dao.AccuseRepository;
import page.clab.api.domain.accuse.domain.Accuse;
import page.clab.api.domain.member.event.MemberEventProcessor;
import page.clab.api.domain.member.event.MemberEventProcessorRegistry;

import java.util.List;

@Component
@RequiredArgsConstructor
public class AccuseEventProcessor implements MemberEventProcessor {

private final AccuseRepository accuseRepository;

private final MemberEventProcessorRegistry processorRegistry;

@PostConstruct
public void init() {
processorRegistry.register(this);
}

@Override
@Transactional
public void processMemberDeleted(String memberId) {
List<Accuse> accuses = accuseRepository.findByMemberId(memberId);
accuses.forEach(Accuse::delete);
accuseRepository.saveAll(accuses);
}

@Override
public void processMemberUpdated(String memberId) {
// do nothing
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,15 @@
import page.clab.api.domain.accuse.domain.AccuseTargetId;
import page.clab.api.domain.accuse.domain.TargetType;
import page.clab.api.domain.accuse.dto.request.AccuseRequestDto;
import page.clab.api.domain.accuse.dto.response.AccuseMemberInfo;
import page.clab.api.domain.member.dto.shared.MemberBasicInfoDto;
import page.clab.api.domain.accuse.dto.response.AccuseMyResponseDto;
import page.clab.api.domain.accuse.dto.response.AccuseResponseDto;
import page.clab.api.domain.accuse.exception.AccuseTargetTypeIncorrectException;
import page.clab.api.domain.board.application.BoardService;
import page.clab.api.domain.board.domain.Board;
import page.clab.api.domain.comment.application.CommentService;
import page.clab.api.domain.comment.domain.Comment;
import page.clab.api.domain.member.application.MemberService;
import page.clab.api.domain.member.domain.Member;
import page.clab.api.domain.member.application.MemberLookupService;
import page.clab.api.domain.notification.application.NotificationService;
import page.clab.api.domain.review.application.ReviewService;
import page.clab.api.domain.review.domain.Review;
Expand All @@ -33,13 +32,15 @@
import page.clab.api.global.validation.ValidationService;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Slf4j
public class AccuseService {

private final MemberService memberService;
private final MemberLookupService memberLookupService;

private final BoardService boardService;

Expand All @@ -59,19 +60,19 @@ public class AccuseService {
public Long createAccuse(AccuseRequestDto requestDto) {
TargetType type = requestDto.getTargetType();
Long targetId = requestDto.getTargetId();
Member currentMember = memberService.getCurrentMember();
String memberId = memberLookupService.getCurrentMemberId();

validateAccuseRequest(type, targetId, currentMember);
validateAccuseRequest(type, targetId, memberId);

AccuseTarget target = getOrCreateAccuseTarget(requestDto, type, targetId);
validationService.checkValid(target);
accuseTargetRepository.save(target);

Accuse accuse = findOrCreateAccuse(requestDto, currentMember, target);
Accuse accuse = findOrCreateAccuse(requestDto, memberId, target);
validationService.checkValid(accuse);

notificationService.sendNotificationToMember(currentMember, "신고하신 내용이 접수되었습니다.");
notificationService.sendNotificationToSuperAdmins(currentMember.getName() + "님이 신고를 접수하였습니다. 확인해주세요.");
notificationService.sendNotificationToMember(memberId, "신고하신 내용이 접수되었습니다.");
notificationService.sendNotificationToSuperAdmins(memberId + "님이 신고를 접수하였습니다. 확인해주세요.");
return accuseRepository.save(accuse).getId();
}

Expand All @@ -84,8 +85,8 @@ public PagedResponseDto<AccuseResponseDto> getAccusesByConditions(TargetType typ

@Transactional(readOnly = true)
public PagedResponseDto<AccuseMyResponseDto> getMyAccuses(Pageable pageable) {
Member currentMember = memberService.getCurrentMember();
Page<Accuse> accuses = accuseRepository.findByMember(currentMember, pageable);
String currentMemberId = memberLookupService.getCurrentMemberId();
Page<Accuse> accuses = accuseRepository.findByMemberId(currentMemberId, pageable);
return new PagedResponseDto<>(accuses.map(AccuseMyResponseDto::toDto));
}

Expand All @@ -103,23 +104,23 @@ private AccuseTarget getAccuseTargetByIdOrThrow(TargetType type, Long targetId)
.orElseThrow(() -> new NotFoundException("존재하지 않는 신고 대상입니다."));
}

private void validateAccuseRequest(TargetType type, Long targetId, Member currentMember) {
private void validateAccuseRequest(TargetType type, Long targetId, String currentMemberId) {
switch (type) {
case BOARD:
Board board = boardService.getBoardByIdOrThrow(targetId);
if (board.isOwner(currentMember)) {
if (board.isOwner(currentMemberId)) {
throw new AccuseTargetTypeIncorrectException("자신의 게시글은 신고할 수 없습니다.");
}
break;
case COMMENT:
Comment comment = commentService.getCommentByIdOrThrow(targetId);
if (comment.isOwner(currentMember)) {
if (comment.isOwner(currentMemberId)) {
throw new AccuseTargetTypeIncorrectException("자신의 댓글은 신고할 수 없습니다.");
}
break;
case REVIEW:
Review review = reviewService.getReviewByIdOrThrow(targetId);
if (review.isOwner(currentMember)) {
if (review.isOwner(currentMemberId)) {
throw new AccuseTargetTypeIncorrectException("자신의 리뷰는 신고할 수 없습니다.");
}
break;
Expand All @@ -133,34 +134,40 @@ private AccuseTarget getOrCreateAccuseTarget(AccuseRequestDto requestDto, Target
.orElseGet(() -> AccuseRequestDto.toTargetEntity(requestDto));
}

private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, Member currentMember, AccuseTarget target) {
return accuseRepository.findByMemberAndTarget(currentMember, target)
private Accuse findOrCreateAccuse(AccuseRequestDto requestDto, String memberId, AccuseTarget target) {
return accuseRepository.findByMemberIdAndTarget(memberId, target.getTargetType(), target.getTargetReferenceId())
.map(existingAccuse -> {
existingAccuse.updateReason(requestDto.getReason());
return existingAccuse;
})
.orElseGet(() -> {
target.increaseAccuseCount();
return AccuseRequestDto.toEntity(requestDto, currentMember, target);
return AccuseRequestDto.toEntity(requestDto, memberId, target);
});
}

@NotNull
private List<AccuseResponseDto> convertTargetsToResponseDtos(Page<AccuseTarget> accuseTargets) {
return accuseTargets.stream()
.map(accuseTarget -> {
List<Accuse> accuses = accuseRepository.findByTargetOrderByCreatedAtDesc(accuseTarget);
List<AccuseMemberInfo> members = AccuseMemberInfo.create(accuses);
List<Accuse> accuses = accuseRepository.findByTargetOrderByCreatedAtDesc(accuseTarget.getTargetType(), accuseTarget.getTargetReferenceId());
if (accuses.isEmpty()) {
return null;
}
List<MemberBasicInfoDto> members = accuses.stream()
.map(accuse -> memberLookupService.getMemberBasicInfoById(accuse.getMemberId()))
.toList();
return AccuseResponseDto.toDto(accuses.getFirst(), members);
})
.filter(Objects::nonNull)
.toList();
}

private void sendStatusUpdateNotifications(AccuseStatus status, AccuseTarget target) {
List<Member> members = accuseRepository.findByTarget(target).stream()
.map(Accuse::getMember)
.toList();
notificationService.sendNotificationToMembers(members, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
List<String> memberIds = accuseRepository.findByTarget(target.getTargetType(), target.getTargetReferenceId()).stream()
.map(Accuse::getMemberId)
.collect(Collectors.toList());
notificationService.sendNotificationToMembers(memberIds, "신고 상태가 " + status.getDescription() + "(으)로 변경되었습니다.");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,30 @@
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import page.clab.api.domain.accuse.domain.Accuse;
import page.clab.api.domain.accuse.domain.AccuseTarget;
import page.clab.api.domain.member.domain.Member;
import page.clab.api.domain.accuse.domain.TargetType;

import java.util.List;
import java.util.Optional;

@Repository
public interface AccuseRepository extends JpaRepository<Accuse, Long> {

Page<Accuse> findAllByOrderByCreatedAtDesc(Pageable pageable);
@Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId")
Optional<Accuse> findByMemberIdAndTarget(String memberId, TargetType targetType, Long targetReferenceId);

Optional<Accuse> findByMemberAndTarget(Member member, AccuseTarget target);
@Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId ORDER BY a.createdAt DESC")
List<Accuse> findByTargetOrderByCreatedAtDesc(TargetType targetType, Long targetReferenceId);

List<Accuse> findByTargetOrderByCreatedAtDesc(AccuseTarget accuseTarget);
@Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId ORDER BY a.createdAt DESC")
Page<Accuse> findByMemberId(String memberId, Pageable pageable);

Page<Accuse> findByMember(Member currentMember, Pageable pageable);
@Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.memberId = :memberId")
List<Accuse> findByMemberId(String memberId);

List<Accuse> findByTarget(AccuseTarget target);
@Query("SELECT a FROM Accuse a WHERE a.isDeleted = false AND a.target.targetType = :targetType AND a.target.targetReferenceId = :targetReferenceId")
List<Accuse> findByTarget(TargetType targetType, Long targetReferenceId);

}
Loading