diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/StudentActivityNotFoundException.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/StudentActivityNotFoundException.kt new file mode 100644 index 000000000..582603248 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/StudentActivityNotFoundException.kt @@ -0,0 +1,9 @@ +package team.msg.domain.student.exception + +import team.msg.domain.student.exception.constant.StudentActivityErrorCode +import team.msg.global.error.exception.BitgouelException + +class StudentActivityNotFoundException( + message: String +) : BitgouelException(message, StudentActivityErrorCode.STUDENT_ACTIVITY_NOT_FOUND.status) { +} \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/constant/StudentActivityErrorCode.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/constant/StudentActivityErrorCode.kt new file mode 100644 index 000000000..9b5e70d11 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/exception/constant/StudentActivityErrorCode.kt @@ -0,0 +1,8 @@ +package team.msg.domain.student.exception.constant + +enum class StudentActivityErrorCode( + val message: String, + val status: Int +) { + STUDENT_ACTIVITY_NOT_FOUND("학생 활동을 찾을 수 없습니다", 404) +} \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapper.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapper.kt index 81d47fbfe..f4db65c43 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapper.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapper.kt @@ -1,8 +1,11 @@ package team.msg.domain.student.mapper import team.msg.domain.student.presentation.data.request.CreateStudentActivityRequest +import team.msg.domain.student.presentation.data.request.UpdateStudentActivityRequest import team.msg.domain.student.presentation.data.web.CreateStudentActivityWebRequest +import team.msg.domain.student.presentation.data.web.UpdateStudentActivityWebRequest interface StudentActivityMapper { fun createStudentActivityWebRequestToDto(webRequest: CreateStudentActivityWebRequest): CreateStudentActivityRequest + fun updateStudentActivityWebRequestToDto(webRequest: UpdateStudentActivityWebRequest): UpdateStudentActivityRequest } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapperImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapperImpl.kt index cf62c681d..c60e98fcf 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapperImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/mapper/StudentActivityMapperImpl.kt @@ -2,13 +2,15 @@ package team.msg.domain.student.mapper import org.springframework.stereotype.Component import team.msg.domain.student.presentation.data.request.CreateStudentActivityRequest +import team.msg.domain.student.presentation.data.request.UpdateStudentActivityRequest import team.msg.domain.student.presentation.data.web.CreateStudentActivityWebRequest +import team.msg.domain.student.presentation.data.web.UpdateStudentActivityWebRequest @Component class StudentActivityMapperImpl : StudentActivityMapper { /** - * StudentActivity 생성 Web Request 를 애플리케이션 영역에서 사용될 Dto 로 매핑합니다. + * StudentActivity 생성 Web Request를 애플리케이션 영역에서 사용될 Dto 로 매핑합니다. */ override fun createStudentActivityWebRequestToDto(webRequest: CreateStudentActivityWebRequest): CreateStudentActivityRequest = CreateStudentActivityRequest( @@ -17,4 +19,15 @@ class StudentActivityMapperImpl : StudentActivityMapper { credit = webRequest.credit, activityDate = webRequest.activityDate ) + + /** + * StudentActivity 생성 Web Request를 애플리케이션 영역에서 사용될 Dto로 매핑합니다. + */ + override fun updateStudentActivityWebRequestToDto(webRequest: UpdateStudentActivityWebRequest): UpdateStudentActivityRequest = + UpdateStudentActivityRequest( + title = webRequest.title, + content = webRequest.content, + credit = webRequest.credit, + activityDate = webRequest.activityDate + ) } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/StudentActivityController.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/StudentActivityController.kt index 790f1c647..51e14a329 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/StudentActivityController.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/StudentActivityController.kt @@ -3,13 +3,17 @@ package team.msg.domain.student.presentation import javax.validation.Valid import org.springframework.http.HttpStatus import org.springframework.http.ResponseEntity +import org.springframework.web.bind.annotation.PatchMapping +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.RequestMapping import org.springframework.web.bind.annotation.RestController import team.msg.domain.student.mapper.StudentActivityMapper import team.msg.domain.student.presentation.data.web.CreateStudentActivityWebRequest +import team.msg.domain.student.presentation.data.web.UpdateStudentActivityWebRequest import team.msg.domain.student.service.StudentActivityService +import java.util.UUID @RestController @RequestMapping("/activity") @@ -23,4 +27,11 @@ class StudentActivityController( studentActivityService.createStudentActivity(request) return ResponseEntity.status(HttpStatus.CREATED).build() } + + @PatchMapping("/{id}") + fun updateStudentActivity(@PathVariable id: UUID, @RequestBody @Valid webRequest: UpdateStudentActivityWebRequest): ResponseEntity { + val request = studentActivityMapper.updateStudentActivityWebRequestToDto(webRequest) + studentActivityService.updateStudentActivity(id, request) + return ResponseEntity.status(HttpStatus.NO_CONTENT).build() + } } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/request/UpdateStudentActivityRequest.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/request/UpdateStudentActivityRequest.kt new file mode 100644 index 000000000..6046a5027 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/request/UpdateStudentActivityRequest.kt @@ -0,0 +1,10 @@ +package team.msg.domain.student.presentation.data.request + +import java.time.LocalDateTime + +data class UpdateStudentActivityRequest( + val title: String, + val content: String, + val credit: Int, + val activityDate: LocalDateTime +) \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/web/UpdateStudentActivityWebRequest.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/web/UpdateStudentActivityWebRequest.kt new file mode 100644 index 000000000..aa1324077 --- /dev/null +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/presentation/data/web/UpdateStudentActivityWebRequest.kt @@ -0,0 +1,22 @@ +package team.msg.domain.student.presentation.data.web + +import javax.validation.constraints.Max +import javax.validation.constraints.NotBlank +import javax.validation.constraints.NotNull +import java.time.LocalDateTime + +data class UpdateStudentActivityWebRequest( + @field:NotBlank + @field:Max(100) + val title: String, + + @field:NotBlank + @field:Max(1000) + val content: String, + + @field:NotNull + val credit: Int, + + @field:NotNull + val activityDate: LocalDateTime +) \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityService.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityService.kt index 71682afd0..030e3ec10 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityService.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityService.kt @@ -1,7 +1,10 @@ package team.msg.domain.student.service import team.msg.domain.student.presentation.data.request.CreateStudentActivityRequest +import team.msg.domain.student.presentation.data.request.UpdateStudentActivityRequest +import java.util.UUID interface StudentActivityService { fun createStudentActivity(request: CreateStudentActivityRequest) + fun updateStudentActivity(id: UUID, request: UpdateStudentActivityRequest) } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityServiceImpl.kt b/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityServiceImpl.kt index c3674ef50..ce4439ae6 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityServiceImpl.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/domain/student/service/StudentActivityServiceImpl.kt @@ -4,9 +4,11 @@ import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional import team.msg.common.enum.ApproveStatus import team.msg.common.util.UserUtil +import team.msg.domain.student.exception.StudentActivityNotFoundException import team.msg.domain.student.exception.StudentNotFoundException import team.msg.domain.student.model.StudentActivity import team.msg.domain.student.presentation.data.request.CreateStudentActivityRequest +import team.msg.domain.student.presentation.data.request.UpdateStudentActivityRequest import team.msg.domain.student.repository.StudentActivityRepository import team.msg.domain.student.repository.StudentRepository import team.msg.domain.teacher.exception.TeacherNotFoundException @@ -48,4 +50,33 @@ class StudentActivityServiceImpl( studentActivityRepository.save(studentActivity) } + + + /** + * 학생 활동을 업데이트하는 비지니스 로직입니다 + * @param UpdateStudentActivityRequest + */ + @Transactional(rollbackFor = [Exception::class]) + override fun updateStudentActivity(id: UUID, request: UpdateStudentActivityRequest) { + val user = userUtil.queryCurrentUser() + + val student = studentRepository.findByUser(user) + ?: throw StudentNotFoundException("학생을 찾을 수 없습니다. info : [ userId = ${user.id}, username = ${user.name} ]") + + val studentActivity = studentActivityRepository.findByIdAndStudent(id, student) + ?: throw StudentActivityNotFoundException("학생 활동을 찾을 수 없습니다. info : [ studentActivityId = $id ]") + + val updatedStudentActivity = StudentActivity( + id = studentActivity.id, + title = request.title, + content = request.content, + credit = request.credit, + activityDate = request.activityDate, + approveStatus = ApproveStatus.PENDING, + student = studentActivity.student, + teacher = studentActivity.teacher, + ) + + studentActivityRepository.save(updatedStudentActivity) + } } \ No newline at end of file diff --git a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt index a7175dc3e..0bb71f4d7 100644 --- a/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt +++ b/bitgouel-api/src/main/kotlin/team/msg/global/security/SecurityConfig.kt @@ -55,6 +55,7 @@ class SecurityConfig( // activity .mvcMatchers(HttpMethod.POST, "/activity").hasRole(STUDENT) + .mvcMatchers(HttpMethod.PATCH, "/activity/{id}").hasRole(STUDENT) // lecture .mvcMatchers(HttpMethod.POST, "/lecture").hasAnyRole(PROFESSOR, COMPANY_INSTRUCTOR, GOVERNMENT) diff --git a/bitgouel-domain/src/main/kotlin/team/msg/common/entity/BaseTimeEntity.kt b/bitgouel-domain/src/main/kotlin/team/msg/common/entity/BaseTimeEntity.kt index 39611c085..b1376c2c0 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/common/entity/BaseTimeEntity.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/common/entity/BaseTimeEntity.kt @@ -2,10 +2,19 @@ package team.msg.common.entity import javax.persistence.Column import javax.persistence.MappedSuperclass +import org.springframework.data.annotation.CreatedDate +import org.springframework.data.annotation.LastModifiedDate import java.time.LocalDateTime @MappedSuperclass -abstract class BaseTimeEntity( +abstract class BaseTimeEntity { + @CreatedDate @Column(nullable = false, updatable = false, columnDefinition = "DATETIME(6)") - open val createdAt: LocalDateTime = LocalDateTime.now() -) \ No newline at end of file + var createdAt: LocalDateTime = LocalDateTime.now() + protected set + + @LastModifiedDate + @Column(nullable = false, columnDefinition = "DATETIME(6)") + var modifiedAt: LocalDateTime = LocalDateTime.now() + protected set +} \ No newline at end of file diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/student/model/StudentActivity.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/student/model/StudentActivity.kt index 37f32be97..05d7b5982 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/student/model/StudentActivity.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/student/model/StudentActivity.kt @@ -1,6 +1,5 @@ package team.msg.domain.student.model -import team.msg.common.entity.BaseUUIDEntity import javax.persistence.Column import javax.persistence.Entity import javax.persistence.EnumType @@ -8,10 +7,11 @@ import javax.persistence.Enumerated import javax.persistence.FetchType import javax.persistence.JoinColumn import javax.persistence.ManyToOne +import team.msg.common.entity.BaseUUIDEntity import team.msg.common.enum.ApproveStatus import team.msg.domain.teacher.model.Teacher import java.time.LocalDateTime -import java.util.UUID +import java.util.* @Entity class StudentActivity( @@ -31,7 +31,7 @@ class StudentActivity( @Column(columnDefinition = "VARCHAR(10)", nullable = false) val approveStatus: ApproveStatus, - @Column(nullable = false, updatable = false, columnDefinition = "DATETIME(6)") + @Column(nullable = false, columnDefinition = "DATETIME(6)") val activityDate: LocalDateTime, @ManyToOne(fetch = FetchType.LAZY) diff --git a/bitgouel-domain/src/main/kotlin/team/msg/domain/student/repository/StudentActivityRepository.kt b/bitgouel-domain/src/main/kotlin/team/msg/domain/student/repository/StudentActivityRepository.kt index ef39f342d..6afeeccb9 100644 --- a/bitgouel-domain/src/main/kotlin/team/msg/domain/student/repository/StudentActivityRepository.kt +++ b/bitgouel-domain/src/main/kotlin/team/msg/domain/student/repository/StudentActivityRepository.kt @@ -1,7 +1,10 @@ package team.msg.domain.student.repository import org.springframework.data.jpa.repository.JpaRepository +import team.msg.domain.student.model.Student import team.msg.domain.student.model.StudentActivity -import java.util.UUID +import java.util.* -interface StudentActivityRepository : JpaRepository \ No newline at end of file +interface StudentActivityRepository : JpaRepository { + fun findByIdAndStudent(id: UUID, student: Student): StudentActivity? +} \ No newline at end of file