Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,19 @@

import com.jobdri.jobdri_api.domain.analysis.entity.Analysis;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.Optional;

public interface AnalysisRepository extends JpaRepository<Analysis, Long> {
Optional<Analysis> findByMockApplyId(Long mockApplyId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from Analysis a
where a.mockApply.jobPosting.id = :jobPostingId
""")
void deleteAllByJobPostingId(@Param("jobPostingId") Long jobPostingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.jobdri.jobdri_api.domain.analysis.entity.QuestionAnalysis;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

Expand All @@ -10,4 +13,15 @@ public interface QuestionAnalysisRepository extends JpaRepository<QuestionAnalys
List<QuestionAnalysis> findAllByAnalysisId(Long analysisId);
List<QuestionAnalysis> findAllByAnalysisIdOrderByQuestionIdAscIdAsc(Long analysisId);
void deleteAllByAnalysisId(Long analysisId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from QuestionAnalysis qa
where qa.analysis.id in (
select a.id
from Analysis a
where a.mockApply.jobPosting.id = :jobPostingId
)
""")
void deleteAllByJobPostingId(@Param("jobPostingId") Long jobPostingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

import com.jobdri.jobdri_api.domain.analysis.entity.Question;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

import java.util.List;

Expand All @@ -10,4 +13,11 @@ public interface QuestionRepository extends JpaRepository<Question, Long> {
List<Question> findAllByMockApplyIdOrderByIdAsc(Long mockApplyId);
boolean existsByMockApplyIdAndContent(Long mockApplyId, String content);
void deleteAllByMockApplyId(Long mockApplyId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from Question q
where q.mockApply.jobPosting.id = :jobPostingId
""")
void deleteAllByJobPostingId(@Param("jobPostingId") Long jobPostingId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
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.PatchMapping;
Expand Down Expand Up @@ -139,6 +140,20 @@ public ApiResponse<List<JobPostingResponse>> getMyJobPostings(
);
}

@Operation(
summary = "채용 공고 및 모의 서류 결과 전체 삭제",
description = "채용 공고와 연결된 모의 서류 지원, 문항, 분석 결과를 함께 삭제합니다."
)
@DeleteMapping("/{jobPostingId}")
public ApiResponse<Void> deleteJobPosting(
@AuthenticationPrincipal UserDetailsImpl userDetails,
@PathVariable Long jobPostingId
) {
var user = validateAuthenticatedUser(userDetails);
jobPostingService.deleteJobPosting(user, jobPostingId);
return ApiResponse.onSuccess("채용 공고와 모의 서류 결과가 삭제되었습니다.", null);
}

private com.jobdri.jobdri_api.domain.user.entity.User validateAuthenticatedUser(UserDetailsImpl userDetails) {
return userService.validateUser(userDetails == null ? null : userDetails.getUser());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.jobdri.jobdri_api.domain.jobposting.service;

import com.jobdri.jobdri_api.domain.analysis.repository.AnalysisRepository;
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionAnalysisRepository;
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionRepository;
import com.jobdri.jobdri_api.domain.classification.entity.DetailClassification;
import com.jobdri.jobdri_api.domain.classification.repository.DetailClassificationRepository;
import com.jobdri.jobdri_api.domain.audit.annotation.AuditLogEvent;
Expand All @@ -10,6 +13,8 @@
import com.jobdri.jobdri_api.domain.jobposting.dto.response.JobPostingResponse;
import com.jobdri.jobdri_api.domain.jobposting.entity.JobPosting;
import com.jobdri.jobdri_api.domain.jobposting.repository.JobPostingRepository;
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplyRepository;
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplySequenceRepository;
import com.jobdri.jobdri_api.domain.user.entity.User;
import com.jobdri.jobdri_api.domain.user.service.UserService;
import com.jobdri.jobdri_api.global.apiPayload.code.GeneralErrorCode;
Expand All @@ -29,6 +34,11 @@ public class JobPostingService {
private final CompanyRepository companyRepository;
private final DetailClassificationRepository detailClassificationRepository;
private final UserService userService;
private final MockApplySequenceRepository mockApplySequenceRepository;
private final MockApplyRepository mockApplyRepository;
private final QuestionRepository questionRepository;
private final AnalysisRepository analysisRepository;
private final QuestionAnalysisRepository questionAnalysisRepository;

@Transactional
@AuditLogEvent(action = "JOB_POSTING_CREATE", targetType = "JOB_POSTING", targetId = "#result.getJobPostingId()")
Expand Down Expand Up @@ -89,6 +99,20 @@ public List<JobPostingResponse> getJobPostingsByCompany(User user, Long companyI
.toList();
}

@Transactional
@AuditLogEvent(action = "JOB_POSTING_DELETE", targetType = "JOB_POSTING", targetId = "#arg1")
public void deleteJobPosting(User user, Long jobPostingId) {
User validatedUser = userService.validateUser(user);
JobPosting jobPosting = getOwnedJobPosting(validatedUser, jobPostingId);

questionAnalysisRepository.deleteAllByJobPostingId(jobPostingId);
questionRepository.deleteAllByJobPostingId(jobPostingId);
analysisRepository.deleteAllByJobPostingId(jobPostingId);
mockApplyRepository.deleteAllByJobPostingId(jobPostingId);
mockApplySequenceRepository.deleteAllByUserIdAndJobPostingId(validatedUser.getId(), jobPostingId);
jobPostingRepository.delete(jobPosting);
}

public JobPosting getOwnedJobPosting(User user, Long jobPostingId) {
JobPosting jobPosting = jobPostingRepository.findById(jobPostingId)
.orElseThrow(() -> new GeneralException(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.jobdri.jobdri_api.domain.mockapply.entity.MockApply;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

Expand All @@ -14,6 +15,13 @@ public interface MockApplyRepository extends JpaRepository<MockApply, Long> {
List<MockApply> findAllByUserIdAndJobPostingIdOrderByIdAsc(Long userId, Long jobPostingId);
long countByUserIdAndJobPostingId(Long userId, Long jobPostingId);

@Modifying(clearAutomatically = true, flushAutomatically = true)
@Query("""
delete from MockApply ma
where ma.jobPosting.id = :jobPostingId
""")
void deleteAllByJobPostingId(Long jobPostingId);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

@Query("""
select ma
from MockApply ma
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ Optional<MockApplySequence> findByKeyForUpdate(
@Param("userId") Long userId,
@Param("jobPostingId") Long jobPostingId
);

void deleteAllByUserIdAndJobPostingId(Long userId, Long jobPostingId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
package com.jobdri.jobdri_api.domain.jobposting.service;

import com.jobdri.jobdri_api.domain.analysis.entity.Analysis;
import com.jobdri.jobdri_api.domain.analysis.entity.Question;
import com.jobdri.jobdri_api.domain.analysis.entity.QuestionAnalysis;
import com.jobdri.jobdri_api.domain.analysis.entity.QuestionAnalysisStatus;
import com.jobdri.jobdri_api.domain.analysis.repository.AnalysisRepository;
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionAnalysisRepository;
import com.jobdri.jobdri_api.domain.analysis.repository.QuestionRepository;
import com.jobdri.jobdri_api.domain.classification.entity.Classification;
import com.jobdri.jobdri_api.domain.classification.entity.DetailClassification;
import com.jobdri.jobdri_api.domain.classification.entity.MiddleClassification;
import com.jobdri.jobdri_api.domain.classification.repository.ClassificationRepository;
import com.jobdri.jobdri_api.domain.classification.repository.DetailClassificationRepository;
import com.jobdri.jobdri_api.domain.company.entity.Company;
import com.jobdri.jobdri_api.domain.company.entity.CompanySize;
import com.jobdri.jobdri_api.domain.company.repository.CompanyRepository;
import com.jobdri.jobdri_api.domain.jobposting.entity.JobPosting;
import com.jobdri.jobdri_api.domain.jobposting.repository.JobPostingRepository;
import com.jobdri.jobdri_api.domain.mockapply.entity.ApplyType;
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApply;
import com.jobdri.jobdri_api.domain.mockapply.entity.MockApplySequence;
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplyRepository;
import com.jobdri.jobdri_api.domain.mockapply.repository.MockApplySequenceRepository;
import com.jobdri.jobdri_api.domain.user.entity.User;
import com.jobdri.jobdri_api.domain.user.repository.UserRepository;
import com.jobdri.jobdri_api.global.apiPayload.code.GeneralErrorCode;
import com.jobdri.jobdri_api.global.apiPayload.exception.GeneralException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.transaction.annotation.Transactional;

import java.util.UUID;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;

@SpringBootTest
@ActiveProfiles("test")
@Transactional
class JobPostingServiceTest {

@Autowired
private JobPostingService jobPostingService;

@Autowired
private JobPostingRepository jobPostingRepository;

@Autowired
private MockApplyRepository mockApplyRepository;

@Autowired
private MockApplySequenceRepository mockApplySequenceRepository;

@Autowired
private QuestionRepository questionRepository;

@Autowired
private AnalysisRepository analysisRepository;

@Autowired
private QuestionAnalysisRepository questionAnalysisRepository;

@Autowired
private UserRepository userRepository;

@Autowired
private CompanyRepository companyRepository;

@Autowired
private ClassificationRepository classificationRepository;

@Autowired
private DetailClassificationRepository detailClassificationRepository;

@Test
@DisplayName("채용 공고를 삭제하면 연결된 모의 서류 결과도 함께 삭제한다")
void deleteJobPostingDeletesMockApplyResults() {
User user = saveUser("job-posting-delete@example.com");
JobPosting jobPosting = saveJobPosting(user);
MockApply mockApply = mockApplyRepository.save(MockApply.create(user, jobPosting, ApplyType.MOCK, 1));
Question question = questionRepository.save(Question.create(mockApply, "지원 동기", 1000, "답변입니다."));
Analysis analysis = analysisRepository.save(Analysis.create(mockApply, 80, 81, 82, 83, "분석 결과입니다."));
QuestionAnalysis questionAnalysis = questionAnalysisRepository.save(QuestionAnalysis.create(
question,
analysis,
"답변입니다.",
"근거가 부족합니다.",
"구체적인 성과를 포함해 답변했습니다.",
QuestionAnalysisStatus.MENTIONED,
0,
5
));
mockApplySequenceRepository.save(MockApplySequence.create(user.getId(), jobPosting.getId(), 1));

jobPostingService.deleteJobPosting(user, jobPosting.getId());
jobPostingRepository.flush();

assertThat(jobPostingRepository.findById(jobPosting.getId())).isEmpty();
assertThat(mockApplyRepository.findById(mockApply.getId())).isEmpty();
assertThat(questionRepository.findById(question.getId())).isEmpty();
assertThat(analysisRepository.findById(analysis.getId())).isEmpty();
assertThat(questionAnalysisRepository.findById(questionAnalysis.getId())).isEmpty();
assertThat(mockApplySequenceRepository.findByKeyForUpdate(user.getId(), jobPosting.getId())).isEmpty();
}

@Test
@DisplayName("다른 사용자의 채용 공고는 삭제할 수 없다")
void deleteJobPostingThrowsWhenUserDoesNotOwnJobPosting() {
User owner = saveUser("job-posting-delete-owner@example.com");
User other = saveUser("job-posting-delete-other@example.com");
JobPosting jobPosting = saveJobPosting(owner);

assertThatThrownBy(() -> jobPostingService.deleteJobPosting(other, jobPosting.getId()))
.isInstanceOf(GeneralException.class)
.extracting("code")
.isEqualTo(GeneralErrorCode.FORBIDDEN);

assertThat(jobPostingRepository.findById(jobPosting.getId())).isPresent();
}

private User saveUser(String email) {
return userRepository.save(User.signup("테스트 사용자", email, "encoded-password"));
}

private JobPosting saveJobPosting(User user) {
Company company = companyRepository.save(Company.create("삭제 테스트 기업 " + UUID.randomUUID(), CompanySize.MEDIUM));
DetailClassification detailClassification = saveDetailClassification();
return jobPostingRepository.save(JobPosting.create(
user,
company,
detailClassification,
"주요 업무",
"자격 요건",
"우대 사항"
));
}

private DetailClassification saveDetailClassification() {
Classification classification = Classification.create("삭제 테스트 대분류 " + UUID.randomUUID());
MiddleClassification middleClassification = classification.addMiddleClassification("삭제 테스트 중분류");
DetailClassification detailClassification = middleClassification.addDetailClassification("삭제 테스트 소분류");
classificationRepository.save(classification);
return detailClassificationRepository.findById(detailClassification.getId()).orElseThrow();
}
}
Loading