-
Notifications
You must be signed in to change notification settings - Fork 0
origin/develop #68
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
origin/develop #68
Changes from all commits
de7746a
ac381fe
38bd5c8
d11a50d
dc1e018
9964ff1
5df8467
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| package com.cw.vlainter.domain.interview.dto | ||
|
|
||
| import com.cw.vlainter.domain.interview.entity.TechQuestionReusePolicy | ||
| import java.time.OffsetDateTime | ||
|
|
||
| data class AdminInterviewSettingsResponse( | ||
| val techQuestionReusePolicy: TechQuestionReusePolicy, | ||
| val updatedAt: OffsetDateTime? | ||
| ) | ||
|
|
||
| data class UpdateAdminInterviewSettingsRequest( | ||
| val techQuestionReusePolicy: TechQuestionReusePolicy | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| @file:Suppress("JpaDataSourceORMInspection") | ||
|
|
||
| package com.cw.vlainter.domain.interview.entity | ||
|
|
||
| import jakarta.persistence.Column | ||
| import jakarta.persistence.Entity | ||
| import jakarta.persistence.Id | ||
| import jakarta.persistence.PrePersist | ||
| import jakarta.persistence.PreUpdate | ||
| import jakarta.persistence.Table | ||
| import java.time.OffsetDateTime | ||
|
|
||
| @Entity | ||
| @Table(name = "admin_interview_settings") | ||
| class AdminInterviewSetting( | ||
| @Suppress("unused") | ||
| @Id | ||
| @Column(name = "setting_key", nullable = false, length = 100) | ||
| val settingKey: String, | ||
|
|
||
| @Column(name = "setting_value", nullable = false, length = 100) | ||
| var settingValue: String, | ||
|
|
||
| @Column(name = "updated_at", nullable = false) | ||
| var updatedAt: OffsetDateTime = OffsetDateTime.now() | ||
| ) { | ||
| @PrePersist | ||
| fun prePersist() { | ||
| updatedAt = OffsetDateTime.now() | ||
| } | ||
|
|
||
| @PreUpdate | ||
| fun preUpdate() { | ||
| updatedAt = OffsetDateTime.now() | ||
| } | ||
| } | ||
|
Comment on lines
+15
to
+36
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
class AdminInterviewSetting(
@Suppress("unused")
@Id
@Column(name = "setting_key", nullable = false, length = 100)
val settingKey: String,
@Column(name = "setting_value", nullable = false, length = 100)
var settingValue: String
) {
@Column(name = "updated_at", nullable = false)
lateinit var updatedAt: OffsetDateTime
@PrePersist
fun prePersist() {
updatedAt = OffsetDateTime.now()
}
@PreUpdate
fun preUpdate() {
updatedAt = OffsetDateTime.now()
}
} |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.cw.vlainter.domain.interview.entity | ||
|
|
||
| enum class TechQuestionReusePolicy { | ||
| REUSE_MATCHING, | ||
| ALWAYS_GENERATE | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package com.cw.vlainter.domain.interview.repository | ||
|
|
||
| import com.cw.vlainter.domain.interview.entity.AdminInterviewSetting | ||
| import org.springframework.data.jpa.repository.JpaRepository | ||
|
|
||
| interface AdminInterviewSettingRepository : JpaRepository<AdminInterviewSetting, String> |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,85 @@ | ||
| package com.cw.vlainter.domain.interview.service | ||
|
|
||
| import com.cw.vlainter.domain.interview.dto.AdminInterviewSettingsResponse | ||
| import com.cw.vlainter.domain.interview.dto.UpdateAdminInterviewSettingsRequest | ||
| import com.cw.vlainter.domain.interview.entity.AdminInterviewSetting | ||
| import com.cw.vlainter.domain.interview.entity.TechQuestionReusePolicy | ||
| import com.cw.vlainter.domain.interview.repository.AdminInterviewSettingRepository | ||
| import com.cw.vlainter.domain.user.entity.UserRole | ||
| import com.cw.vlainter.global.security.AuthPrincipal | ||
| import org.slf4j.LoggerFactory | ||
| import org.springframework.http.HttpStatus | ||
| import org.springframework.stereotype.Service | ||
| import org.springframework.transaction.annotation.Transactional | ||
| import org.springframework.web.server.ResponseStatusException | ||
|
|
||
| @Service | ||
| class AdminInterviewSettingsService( | ||
| private val adminInterviewSettingRepository: AdminInterviewSettingRepository | ||
| ) { | ||
| private val logger = LoggerFactory.getLogger(javaClass) | ||
|
|
||
| @Transactional(readOnly = true) | ||
| fun getSettings(principal: AuthPrincipal): AdminInterviewSettingsResponse { | ||
| ensureAdmin(principal) | ||
| val saved = adminInterviewSettingRepository.findById(TECH_QUESTION_REUSE_POLICY_KEY).orElse(null) | ||
| return AdminInterviewSettingsResponse( | ||
| techQuestionReusePolicy = saved?.toTechQuestionReusePolicy() ?: DEFAULT_TECH_QUESTION_REUSE_POLICY, | ||
| updatedAt = saved?.updatedAt | ||
| ) | ||
| } | ||
|
|
||
| @Transactional | ||
| fun updateSettings( | ||
| principal: AuthPrincipal, | ||
| request: UpdateAdminInterviewSettingsRequest | ||
| ): AdminInterviewSettingsResponse { | ||
| ensureAdmin(principal) | ||
| val saved = adminInterviewSettingRepository.findById(TECH_QUESTION_REUSE_POLICY_KEY) | ||
| .map { | ||
| it.settingValue = request.techQuestionReusePolicy.name | ||
| it | ||
| } | ||
| .orElseGet { | ||
| AdminInterviewSetting( | ||
| settingKey = TECH_QUESTION_REUSE_POLICY_KEY, | ||
| settingValue = request.techQuestionReusePolicy.name | ||
| ) | ||
| } | ||
| val updated = adminInterviewSettingRepository.save(saved) | ||
| return AdminInterviewSettingsResponse( | ||
| techQuestionReusePolicy = updated.toTechQuestionReusePolicy(), | ||
| updatedAt = updated.updatedAt | ||
| ) | ||
| } | ||
|
|
||
| @Transactional(readOnly = true) | ||
| fun getTechQuestionReusePolicy(): TechQuestionReusePolicy { | ||
| val saved = adminInterviewSettingRepository.findById(TECH_QUESTION_REUSE_POLICY_KEY).orElse(null) | ||
| return saved?.toTechQuestionReusePolicy() ?: DEFAULT_TECH_QUESTION_REUSE_POLICY | ||
| } | ||
|
|
||
| private fun AdminInterviewSetting.toTechQuestionReusePolicy(): TechQuestionReusePolicy { | ||
| return runCatching { TechQuestionReusePolicy.valueOf(settingValue) } | ||
| .getOrElse { ex -> | ||
| logger.warn( | ||
| "Unknown tech question reuse policy settingValue={}, fallback={}", | ||
| settingValue, | ||
| DEFAULT_TECH_QUESTION_REUSE_POLICY, | ||
| ex | ||
| ) | ||
| DEFAULT_TECH_QUESTION_REUSE_POLICY | ||
| } | ||
| } | ||
|
|
||
| private fun ensureAdmin(principal: AuthPrincipal) { | ||
| if (principal.role != UserRole.ADMIN) { | ||
| throw ResponseStatusException(HttpStatus.FORBIDDEN, "๊ด๋ฆฌ์๋ง ์ ๊ทผํ ์ ์์ต๋๋ค.") | ||
| } | ||
| } | ||
|
|
||
| companion object { | ||
| const val TECH_QUESTION_REUSE_POLICY_KEY = "tech_question_reuse_policy" | ||
| val DEFAULT_TECH_QUESTION_REUSE_POLICY: TechQuestionReusePolicy = TechQuestionReusePolicy.ALWAYS_GENERATE | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| package com.cw.vlainter.domain.site.controller | ||
|
|
||
| import com.cw.vlainter.domain.site.dto.AdminPatchNoteResponse | ||
| import com.cw.vlainter.domain.site.dto.CreatePatchNoteRequest | ||
| import com.cw.vlainter.domain.site.dto.ReorderPatchNotesRequest | ||
| import com.cw.vlainter.domain.site.dto.UpdatePatchNoteRequest | ||
| import com.cw.vlainter.domain.site.service.PatchNoteService | ||
| import com.cw.vlainter.global.security.AuthPrincipal | ||
| import jakarta.validation.Valid | ||
| import org.springframework.http.ResponseEntity | ||
| 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 | ||
| 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 | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/admin/site/patch-notes") | ||
| class AdminPatchNoteController( | ||
| private val patchNoteService: PatchNoteService | ||
| ) { | ||
| @GetMapping | ||
| fun getPatchNotes( | ||
| @AuthenticationPrincipal principal: AuthPrincipal | ||
| ): ResponseEntity<List<AdminPatchNoteResponse>> { | ||
| return ResponseEntity.ok(patchNoteService.getAdminPatchNotes(principal)) | ||
| } | ||
|
|
||
| @PostMapping | ||
| fun createPatchNote( | ||
| @AuthenticationPrincipal principal: AuthPrincipal, | ||
| @Valid @RequestBody request: CreatePatchNoteRequest | ||
| ): ResponseEntity<AdminPatchNoteResponse> { | ||
| return ResponseEntity.ok(patchNoteService.createPatchNote(principal, request)) | ||
| } | ||
|
|
||
| @PatchMapping("/{patchNoteId}") | ||
| fun updatePatchNote( | ||
| @AuthenticationPrincipal principal: AuthPrincipal, | ||
| @PathVariable patchNoteId: Long, | ||
| @Valid @RequestBody request: UpdatePatchNoteRequest | ||
| ): ResponseEntity<AdminPatchNoteResponse> { | ||
| return ResponseEntity.ok(patchNoteService.updatePatchNote(principal, patchNoteId, request)) | ||
| } | ||
|
|
||
| @PatchMapping("/reorder") | ||
| fun reorderPatchNotes( | ||
| @AuthenticationPrincipal principal: AuthPrincipal, | ||
| @Valid @RequestBody request: ReorderPatchNotesRequest | ||
| ): ResponseEntity<List<AdminPatchNoteResponse>> { | ||
| return ResponseEntity.ok(patchNoteService.reorderPatchNotes(principal, request)) | ||
| } | ||
|
|
||
| @DeleteMapping("/{patchNoteId}") | ||
| fun deletePatchNote( | ||
| @AuthenticationPrincipal principal: AuthPrincipal, | ||
| @PathVariable patchNoteId: Long | ||
| ): ResponseEntity<Map<String, String>> { | ||
| patchNoteService.deletePatchNote(principal, patchNoteId) | ||
| return ResponseEntity.ok(mapOf("message" to "ํจ์น๋ ธํธ๊ฐ ์ญ์ ๋์์ต๋๋ค.")) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| package com.cw.vlainter.domain.site.controller | ||
|
|
||
| import com.cw.vlainter.domain.site.dto.AdminSiteSettingsResponse | ||
| import com.cw.vlainter.domain.site.dto.UpdateAdminSiteSettingsRequest | ||
| import com.cw.vlainter.domain.site.service.SiteSettingsService | ||
| import com.cw.vlainter.global.security.AuthPrincipal | ||
| import jakarta.validation.Valid | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | ||
| import org.springframework.web.bind.annotation.GetMapping | ||
| import org.springframework.web.bind.annotation.PatchMapping | ||
| import org.springframework.web.bind.annotation.RequestBody | ||
| import org.springframework.web.bind.annotation.RequestMapping | ||
| import org.springframework.web.bind.annotation.RestController | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/admin/site/settings") | ||
| class AdminSiteSettingsController( | ||
| private val siteSettingsService: SiteSettingsService | ||
| ) { | ||
| @GetMapping | ||
| fun getAdminSettings( | ||
| @AuthenticationPrincipal principal: AuthPrincipal | ||
| ): ResponseEntity<AdminSiteSettingsResponse> { | ||
| return ResponseEntity.ok(siteSettingsService.getAdminSettings(principal)) | ||
| } | ||
|
|
||
| @PatchMapping | ||
| fun updateAdminSettings( | ||
| @AuthenticationPrincipal principal: AuthPrincipal, | ||
| @Valid @RequestBody request: UpdateAdminSiteSettingsRequest | ||
| ): ResponseEntity<AdminSiteSettingsResponse> { | ||
| return ResponseEntity.ok(siteSettingsService.updateAdminSettings(principal, request)) | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| package com.cw.vlainter.domain.site.controller | ||
|
|
||
| import com.cw.vlainter.domain.site.dto.PublicPatchNoteResponse | ||
| import com.cw.vlainter.domain.site.service.PatchNoteService | ||
| import org.springframework.http.ResponseEntity | ||
| import org.springframework.web.bind.annotation.GetMapping | ||
| import org.springframework.web.bind.annotation.RequestMapping | ||
| import org.springframework.web.bind.annotation.RestController | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/site/patch-notes") | ||
| class PatchNoteController( | ||
| private val patchNoteService: PatchNoteService | ||
| ) { | ||
| @GetMapping | ||
| fun getPublishedPatchNotes(): ResponseEntity<List<PublicPatchNoteResponse>> { | ||
| return ResponseEntity.ok(patchNoteService.getPublishedPatchNotes()) | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This commit adds entities for
admin_interview_settingsandpatch_notes(AdminInterviewSettingandPatchNote) but does not include any schema migration/DDL in the repository, so deployments that use the defaultspring.jpa.hibernate.ddl-auto=${JPA_DDL_AUTO:validate}will fail at startup when those tables are not pre-created. Please add the corresponding migration in the same change set to avoid runtime schema-validation failures.Useful? React with ๐ย / ๐.