Skip to content

Commit

Permalink
Merge pull request #77 from KUSITMS-29th-TEAM-B/feat/flight-63
Browse files Browse the repository at this point in the history
feat: JD 조회 API 구현
  • Loading branch information
isprogrammingfun authored May 19, 2024
2 parents b749a1d + 1bec550 commit 7ffcd45
Show file tree
Hide file tree
Showing 20 changed files with 327 additions and 9 deletions.
4 changes: 3 additions & 1 deletion Api-Module/src/docs/asciidoc/JobDescription.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@ operation::JobDescriptionControllerTest/createApplyWithEmptyTitle[snippets='http

operation::JobDescriptionControllerTest/createApplyWithEmptyContent[snippets='http-request,request-headers,http-response']

[[GET-JD-LIST]]
=== JD 목록 조회


operation::JobDescriptionControllerTest/getJobDescription[snippets='http-request,request-headers,query-parameters,http-response,response-fields']


Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.bamyanggang.apimodule.common.dto

import com.bamyanggang.domainmodule.common.pagination.SliceDomain

data class SliceResponse<T>(
val content: List<T>,
val page: Int,
val size: Int,
val hasNext: Boolean
) {
companion object {
fun <T> from(slice: SliceDomain<T>): SliceResponse<T> {
return SliceResponse(
content = slice.content,
page = slice.pageNumber,
size = slice.pageSize,
hasNext = slice.hasNext
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.bamyanggang.apimodule.domain.jobDescription.application.dto

import com.bamyanggang.domainmodule.domain.jobDescription.enums.WriteStatus
import java.time.LocalDateTime
import java.util.UUID

class GetJobDescriptionInfo {

data class Response(
val jobDescriptionId: UUID,
val remainingDate: Int,
val enterpriseName: String,
val title: String,
val writeStatus: WriteStatus,
val createdAt: LocalDateTime,
val startedAt: LocalDateTime,
val endedAt: LocalDateTime,
)

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.bamyanggang.apimodule.domain.jobDescription.application.service

import com.bamyanggang.apimodule.common.dto.SliceResponse
import com.bamyanggang.apimodule.common.getAuthenticationPrincipal
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.GetJobDescriptionInfo
import com.bamyanggang.domainmodule.common.pagination.SliceDomain
import com.bamyanggang.domainmodule.domain.jobDescription.enums.SortType
import com.bamyanggang.domainmodule.domain.jobDescription.enums.WriteStatus
import com.bamyanggang.domainmodule.domain.jobDescription.service.ApplyReader
import com.bamyanggang.domainmodule.domain.jobDescription.service.JobDescriptionReader
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.SliceImpl
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional


@Service
class JobDescriptionInfoGetService(
private val jobDescriptionReader: JobDescriptionReader,
private val applyReader: ApplyReader
) {

@Transactional(readOnly = true)
fun getJobDescriptionInfo(pageable: Pageable, writeStatus: WriteStatus?, sortType: SortType?): SliceResponse<GetJobDescriptionInfo.Response> {
return getAuthenticationPrincipal().let{ userId ->
val jobDescriptions = jobDescriptionReader.readJobDescriptionByUserIdAndSortType(userId, pageable.pageNumber, pageable.pageSize, sortType)

val jobDescriptionInfoResponses = jobDescriptions.content.map{ jobDescription ->
val apply = applyReader.readApplyByJobDescriptionId(jobDescription.id)
GetJobDescriptionInfo.Response(
jobDescription.id,
jobDescription.getRemainingDate(),
jobDescription.enterpriseName,
jobDescription.title,
apply?.writeStatus?: WriteStatus.NOT_APPLIED,
jobDescription.createdAt,
jobDescription.startedAt,
jobDescription.endedAt
)
}

val jobDescriptionsSlice = SliceDomain(jobDescriptionInfoResponses, jobDescriptions.pageNumber, jobDescriptions.pageSize, jobDescriptions.hasNext)
SliceResponse.from(jobDescriptionsSlice)
}
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package com.bamyanggang.apimodule.domain.jobDescription.presentation
object JobDescriptionApi {
const val BASE_URL = "/api/job-description"
const val APPLY = "${BASE_URL}/apply/{jobDescriptionId}"

}
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package com.bamyanggang.apimodule.domain.jobDescription.presentation

import com.bamyanggang.apimodule.common.dto.SliceResponse
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.CreateApply
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.CreateJobDescription
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.GetJobDescriptionInfo
import com.bamyanggang.apimodule.domain.jobDescription.application.service.ApplyCreateService
import com.bamyanggang.apimodule.domain.jobDescription.application.service.JobDescriptionCreateService
import com.bamyanggang.apimodule.domain.jobDescription.application.service.JobDescriptionInfoGetService
import com.bamyanggang.domainmodule.domain.jobDescription.enums.SortType
import com.bamyanggang.domainmodule.domain.jobDescription.enums.WriteStatus
import org.springframework.data.domain.Pageable
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.RequestBody
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.util.UUID

@RestController
class JobDescriptionController(
private val jobDescriptionCreateService: JobDescriptionCreateService,
private val applyCreateService: ApplyCreateService
private val applyCreateService: ApplyCreateService,
private val jobDescriptionInfoGetService: JobDescriptionInfoGetService
) {

@PostMapping(JobDescriptionApi.BASE_URL)
Expand All @@ -27,4 +36,13 @@ class JobDescriptionController(
@RequestBody request: CreateApply.Request
) = applyCreateService.createApply(request, jobDescriptionId)

@GetMapping(JobDescriptionApi.BASE_URL)
fun getJobDescription(
pageable: Pageable,
@RequestParam writeStatus: WriteStatus?,
@RequestParam sortType: SortType?
): SliceResponse<GetJobDescriptionInfo.Response> {
return jobDescriptionInfoGetService.getJobDescriptionInfo(pageable, writeStatus, sortType)
}

}
Original file line number Diff line number Diff line change
@@ -1,37 +1,52 @@
package com.bamyanggang.apimodule.domain.jobDescription.presentation

import com.bamyanggang.apimodule.BaseRestDocsTest
import com.bamyanggang.apimodule.common.dto.SliceResponse
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.CreateApply
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.CreateApplyContent
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.CreateJobDescription
import com.bamyanggang.apimodule.domain.jobDescription.application.dto.GetJobDescriptionInfo
import com.bamyanggang.apimodule.domain.jobDescription.application.service.ApplyCreateService
import com.bamyanggang.apimodule.domain.jobDescription.application.service.JobDescriptionCreateService
import com.bamyanggang.apimodule.domain.jobDescription.application.service.JobDescriptionInfoGetService
import com.bamyanggang.commonmodule.exception.ExceptionHandler
import com.bamyanggang.commonmodule.fixture.generateFixture
import com.bamyanggang.domainmodule.common.pagination.SliceDomain
import com.bamyanggang.domainmodule.domain.jobDescription.enums.SortType
import com.bamyanggang.domainmodule.domain.jobDescription.enums.WriteStatus
import io.kotest.assertions.print.printWithType
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import org.mockito.BDDMockito.given
import org.springframework.http.MediaType
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
import org.springframework.boot.test.mock.mockito.MockBean
import org.springframework.context.annotation.Import
import org.springframework.data.domain.PageRequest
import org.springframework.data.domain.Slice
import org.springframework.data.domain.SliceImpl
import org.springframework.http.MediaType
import org.springframework.restdocs.headers.HeaderDocumentation.headerWithName
import org.springframework.restdocs.headers.HeaderDocumentation.requestHeaders
import org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders
import org.springframework.restdocs.payload.PayloadDocumentation.*
import org.springframework.restdocs.request.RequestDocumentation
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status
import java.time.LocalDateTime
import java.util.UUID
import java.util.*

@WebMvcTest(JobDescriptionController::class)
@Import(ExceptionHandler::class)
class JobDescriptionControllerTest : BaseRestDocsTest() {

@MockBean
private lateinit var jobDescriptionCreateService: JobDescriptionCreateService

@MockBean
private lateinit var applyCreateService: ApplyCreateService

@MockBean
private lateinit var jobDescriptionInfoGetService: JobDescriptionInfoGetService

@Test
@DisplayName("직무 공고를 등록한다.")
fun createJobDescription() {
Expand All @@ -48,7 +63,9 @@ class JobDescriptionControllerTest : BaseRestDocsTest() {
it.set("jobDescriptionId", UUID.randomUUID())
}

given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willReturn(createJobDescriptionResponse)
given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willReturn(
createJobDescriptionResponse
)

val request = RestDocumentationRequestBuilders.post(JobDescriptionApi.BASE_URL)
.header("Authorization", "Bearer Access Token")
Expand Down Expand Up @@ -91,7 +108,9 @@ class JobDescriptionControllerTest : BaseRestDocsTest() {
it.set("endedAt", LocalDateTime.now())
}

given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willThrow(IllegalArgumentException("내용은 필수입니다."))
given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willThrow(
IllegalArgumentException("내용은 필수입니다.")
)

val request = RestDocumentationRequestBuilders.post(JobDescriptionApi.BASE_URL)
.header("Authorization", "Bearer Access Token")
Expand Down Expand Up @@ -129,7 +148,9 @@ class JobDescriptionControllerTest : BaseRestDocsTest() {
it.set("endedAt", LocalDateTime.now().minusDays(1))
}

given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willThrow(IllegalArgumentException("시작일은 종료일보다 빨라야 합니다."))
given(jobDescriptionCreateService.createJobDescription(createJobDescriptionRequest)).willThrow(
IllegalArgumentException("시작일은 종료일보다 빨라야 합니다.")
)

val request = RestDocumentationRequestBuilders.post(JobDescriptionApi.BASE_URL)
.header("Authorization", "Bearer Access Token")
Expand Down Expand Up @@ -224,4 +245,67 @@ class JobDescriptionControllerTest : BaseRestDocsTest() {
)
}

@Test
@DisplayName("JD 공고를 조회한다")
fun getJobDescription() {
// given
val pageRequest = PageRequest.of(0, 6)
val getJobDescriptionInfoResponse: GetJobDescriptionInfo.Response = generateFixture {
it.set("jobDescriptionId", UUID.randomUUID())
it.set("remainingDate", 1)
it.set("enterpriseName", "기업 이름")
it.set("title", "직무 공고 제목")
it.set("writeStatus", WriteStatus.WRITING)
it.set("createdAt", LocalDateTime.now())
it.set("startedAt", LocalDateTime.now())
it.set("endedAt", LocalDateTime.now()) }

val slice = SliceDomain(listOf(getJobDescriptionInfoResponse), 0, 1, true)
val sliceResponse = SliceResponse.from(slice)
given(jobDescriptionInfoGetService.getJobDescriptionInfo(pageRequest, WriteStatus.WRITING, SortType.ENDED)).willReturn(sliceResponse)


val request = RestDocumentationRequestBuilders.get(JobDescriptionApi.BASE_URL)
.header("Authorization", "Bearer Access Token")
.contentType(MediaType.APPLICATION_JSON_VALUE)
.queryParam("page", pageRequest.pageNumber.toString())
.queryParam("size", pageRequest.pageSize.toString())
.queryParam("writeStatus", WriteStatus.WRITING.toString())
.queryParam("sortType", SortType.ENDED.toString())

//when
val result = mockMvc.perform(request)

//then
result.andExpect(status().isOk)
.andDo(
resultHandler.document(
requestHeaders(
headerWithName("Authorization").description("엑세스 토큰")
),
RequestDocumentation.queryParameters(
RequestDocumentation.parameterWithName("page").description("요청 페이지"),
RequestDocumentation.parameterWithName("size").description("요청 사이즈"),
RequestDocumentation.parameterWithName("writeStatus")
.description("작성 상태. NOT_APPLIED(칩 없음, 작성 전), WRITING(작성 중), WRITTEN(작성 완료), CLOSED(마감)"),
RequestDocumentation.parameterWithName("sortType")
.description("정렬 타입 CREATED(등록순), ENDED(마감순)"),
),
responseFields(
fieldWithPath("content[].jobDescriptionId").description("직무 공고 ID"),
fieldWithPath("content[].remainingDate").description("디데이"),
fieldWithPath("content[].enterpriseName").description("기업 이름"),
fieldWithPath("content[].title").description("직무 공고 제목"),
fieldWithPath("content[].writeStatus").description("작성 상태. NOT_APPLIED(칩 없음, 작성 전), WRITING(작성 중), WRITTEN(작성 완료), CLOSED(마감)"),
fieldWithPath("content[].createdAt").description("생성일"),
fieldWithPath("content[].startedAt").description("시작일"),
fieldWithPath("content[].endedAt").description("종료일"),
fieldWithPath("page").description("요청 페이지"),
fieldWithPath("size").description("요청 사이즈"),
fieldWithPath("hasNext").description("다음 데이터 존재 여부")
)
)
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.bamyanggang.domainmodule.common.pagination

data class SliceDomain<T>(
val content: List<T>,
val pageNumber: Int,
val pageSize: Int,
val hasNext: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ data class JobDescription(
require(startedAt.isBefore(endedAt)) { "시작일은 종료일보다 빨라야 합니다." }
}

fun getRemainingDate(): Int {
return LocalDateTime.now().until(endedAt, java.time.temporal.ChronoUnit.DAYS).toInt()
}

companion object {
fun create(
enterpriseName: String,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.bamyanggang.domainmodule.domain.jobDescription.enums

enum class SortType {
CREATED, // 등록순
ENDED // 마감순
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ package com.bamyanggang.domainmodule.domain.jobDescription.enums
enum class WriteStatus {
WRITING,
WRITTEN,
CLOSED
CLOSED,
NOT_APPLIED
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.bamyanggang.domainmodule.domain.jobDescription.repository

import com.bamyanggang.domainmodule.domain.jobDescription.aggregate.Apply
import java.util.*

interface ApplyRepository {
fun save(apply: Apply)

fun findByJobDescriptionId(jobDescriptionId: UUID): Apply?

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.bamyanggang.domainmodule.domain.jobDescription.repository

import com.bamyanggang.domainmodule.common.pagination.SliceDomain
import com.bamyanggang.domainmodule.domain.jobDescription.aggregate.JobDescription
import java.util.*

interface JobDescriptionRepository {

fun save(jobDescription: JobDescription)

fun findAllByUserIdAndSortByCreatedAt(userId: UUID, page: Int, size: Int): SliceDomain<JobDescription>

fun findAllByUserId(userId: UUID, page: Int, size: Int): SliceDomain<JobDescription>

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.bamyanggang.domainmodule.domain.jobDescription.service

import com.bamyanggang.domainmodule.domain.jobDescription.aggregate.Apply
import com.bamyanggang.domainmodule.domain.jobDescription.repository.ApplyRepository
import org.springframework.stereotype.Service
import java.util.*

@Service
class ApplyReader(
private val applyRepository: ApplyRepository
) {

fun readApplyByJobDescriptionId(JobDescriptionId: UUID): Apply?{
return applyRepository.findByJobDescriptionId(JobDescriptionId)
}

}
Loading

0 comments on commit 7ffcd45

Please sign in to comment.