Skip to content

Commit 5f2b9c2

Browse files
committed
Merge branch 'develop'
2 parents b050e80 + 58b95cf commit 5f2b9c2

File tree

7 files changed

+139
-13
lines changed

7 files changed

+139
-13
lines changed

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ dependencies {
4747

4848
implementation("org.thymeleaf.extras:thymeleaf-extras-springsecurity6")
4949

50-
implementation("org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE")
50+
implementation("io.awspring.cloud:spring-cloud-starter-aws:2.4.4")
5151
implementation("javax.xml.bind:jaxb-api:2.3.1")
5252
implementation("org.apache.tika:tika-core:2.9.2")
5353
implementation("org.apache.tika:tika-parsers-standard-package:2.9.2")

src/main/kotlin/site/billilge/api/backend/domain/rental/controller/AdminRentalApi.kt

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import org.springframework.web.bind.annotation.ModelAttribute
1010
import org.springframework.web.bind.annotation.PathVariable
1111
import org.springframework.web.bind.annotation.RequestBody
1212
import org.springframework.web.bind.annotation.RequestParam
13+
import site.billilge.api.backend.domain.rental.dto.request.AdminRentalHistoryRequest
1314
import site.billilge.api.backend.domain.rental.dto.request.RentalStatusUpdateRequest
1415
import site.billilge.api.backend.domain.rental.dto.response.AdminRentalHistoryFindAllResponse
1516
import site.billilge.api.backend.domain.rental.dto.response.DashboardResponse
@@ -70,4 +71,34 @@ interface AdminRentalApi {
7071
@PathVariable rentalHistoryId: Long,
7172
@RequestBody request: RentalStatusUpdateRequest
7273
): ResponseEntity<Void>
74+
75+
@Operation(
76+
summary = "대여 기록 추가 (관리자용)",
77+
description = "임의로 대여 기록을 추가하는 관리자용 API"
78+
)
79+
@ApiResponses(
80+
value = [
81+
ApiResponse(
82+
responseCode = "200",
83+
description = "대여 기록 추가 성공"
84+
)
85+
]
86+
)
87+
fun addRentalHistory(
88+
@RequestBody request: AdminRentalHistoryRequest
89+
): ResponseEntity<Void>
90+
91+
@Operation(
92+
summary = "대여 기록 삭제 (관리자용)",
93+
description = "대여 기록을 임의로 삭제하는 관리자용 API"
94+
)
95+
@ApiResponses(
96+
value = [
97+
ApiResponse(
98+
responseCode = "200",
99+
description = "대여 기록 삭제 성공"
100+
)
101+
]
102+
)
103+
fun deleteRentalHistory(@PathVariable rentalHistoryId: Long): ResponseEntity<Void>
73104
}

src/main/kotlin/site/billilge/api/backend/domain/rental/controller/AdminRentalController.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package site.billilge.api.backend.domain.rental.controller
33
import org.springframework.http.ResponseEntity
44
import org.springframework.security.core.annotation.AuthenticationPrincipal
55
import org.springframework.web.bind.annotation.*
6+
import site.billilge.api.backend.domain.rental.dto.request.AdminRentalHistoryRequest
7+
import site.billilge.api.backend.domain.rental.dto.request.RentalHistoryRequest
68
import site.billilge.api.backend.domain.rental.dto.request.RentalStatusUpdateRequest
79
import site.billilge.api.backend.domain.rental.dto.response.AdminRentalHistoryFindAllResponse
810
import site.billilge.api.backend.domain.rental.dto.response.DashboardResponse
@@ -43,4 +45,18 @@ class AdminRentalController(
4345
rentalService.updateRentalStatus(userAuthInfo.memberId, rentalHistoryId, request)
4446
return ResponseEntity.ok().build()
4547
}
48+
49+
@PostMapping
50+
override fun addRentalHistory(
51+
@RequestBody request: AdminRentalHistoryRequest
52+
): ResponseEntity<Void> {
53+
rentalService.createRentalByAdmin(request)
54+
return ResponseEntity.ok().build()
55+
}
56+
57+
@DeleteMapping("/{rentalHistoryId}")
58+
override fun deleteRentalHistory(@PathVariable rentalHistoryId: Long): ResponseEntity<Void> {
59+
rentalService.deleteRentalHistory(rentalHistoryId)
60+
return ResponseEntity.ok().build()
61+
}
4662
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package site.billilge.api.backend.domain.rental.dto.request
2+
3+
import io.swagger.v3.oas.annotations.media.Schema
4+
5+
@Schema(description = "관리자 전용 대여기록 추가 DTO")
6+
data class AdminRentalHistoryRequest(
7+
@field:Schema(description = "대여자 ID", example = "1")
8+
val memberId: Long,
9+
10+
@field:Schema(description = "대여할 물품의 ID", example = "1")
11+
val itemId: Long,
12+
13+
@field:Schema(description = "대여할 물품의 수량", example = "1")
14+
val count: Int,
15+
16+
@field:Schema(description = "대여 시작 시간 정보")
17+
val rentalTime: RentalTime
18+
) {
19+
@Schema(description = "대여 시작 시간 (시간 및 분)")
20+
data class RentalTime(
21+
@field:Schema(description = "대여 시작 시각의 시간(24시간 기준)", example = "13")
22+
val hour: Int,
23+
24+
@field:Schema(description = "대여 시작 시각의 분", example = "0")
25+
val minute: Int
26+
)
27+
}

src/main/kotlin/site/billilge/api/backend/domain/rental/service/RentalService.kt

Lines changed: 57 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import site.billilge.api.backend.domain.member.exception.MemberErrorCode
1313
import site.billilge.api.backend.domain.member.repository.MemberRepository
1414
import site.billilge.api.backend.domain.notification.enums.NotificationStatus
1515
import site.billilge.api.backend.domain.notification.service.NotificationService
16+
import site.billilge.api.backend.domain.rental.dto.request.AdminRentalHistoryRequest
1617
import site.billilge.api.backend.domain.rental.dto.request.RentalHistoryRequest
1718
import site.billilge.api.backend.domain.rental.dto.request.RentalStatusUpdateRequest
1819
import site.billilge.api.backend.domain.rental.dto.response.*
@@ -129,16 +130,63 @@ class RentalService(
129130
true
130131
)
131132

132-
notificationService.sendNotificationToAdmin(
133-
NotificationStatus.ADMIN_RENTAL_APPLY,
134-
listOf(
135-
rentUser.name,
136-
rentUser.studentId,
137-
"${String.format("%02d", rentalHour)}:${String.format("%02d", rentalMinute)}",
138-
item.name
139-
),
140-
true
133+
if (!isDevMode) {
134+
notificationService.sendNotificationToAdmin(
135+
NotificationStatus.ADMIN_RENTAL_APPLY,
136+
listOf(
137+
rentUser.name,
138+
rentUser.studentId,
139+
"${String.format("%02d", rentalHour)}:${String.format("%02d", rentalMinute)}",
140+
item.name
141+
),
142+
true
143+
)
144+
}
145+
}
146+
147+
@Transactional
148+
fun createRentalByAdmin(request: AdminRentalHistoryRequest) {
149+
150+
val item = itemRepository.findById(request.itemId)
151+
.orElseThrow { ApiException(RentalErrorCode.ITEM_NOT_FOUND) }
152+
val rentedCount = request.count
153+
154+
if (rentedCount > item.count)
155+
throw ApiException(RentalErrorCode.ITEM_OUT_OF_STOCK)
156+
157+
val rentUser = memberRepository.findById(request.memberId)
158+
.orElseThrow { ApiException(RentalErrorCode.MEMBER_NOT_FOUND) }
159+
160+
if (!rentUser.isFeePaid)
161+
throw ApiException(RentalErrorCode.MEMBER_IS_NOT_PAYER)
162+
163+
val koreanZone = ZoneId.of("Asia/Seoul")
164+
val today = LocalDate.now(koreanZone)
165+
val requestedRentalDateTime = LocalDateTime.of(
166+
today,
167+
LocalTime.of(request.rentalTime.hour, request.rentalTime.minute)
141168
)
169+
170+
val rentAt = requestedRentalDateTime.atZone(koreanZone).toLocalDateTime()
171+
172+
val newRental = RentalHistory(
173+
member = rentUser,
174+
item = item,
175+
rentalStatus = if (item.type == ItemType.RENTAL) RentalStatus.RENTAL else RentalStatus.RETURNED,
176+
rentedCount = rentedCount,
177+
rentAt = rentAt
178+
).apply {
179+
if (rentalStatus == RentalStatus.RETURNED) {
180+
returnedAt = LocalDateTime.now()
181+
}
182+
}
183+
184+
rentalRepository.save(newRental)
185+
}
186+
187+
@Transactional
188+
fun deleteRentalHistory(rentalHistoryId: Long) {
189+
rentalRepository.deleteById(rentalHistoryId)
142190
}
143191

144192
fun getMemberRentalHistory(memberId: Long?, rentalStatus: RentalStatus?): RentalHistoryFindAllResponse {

src/main/kotlin/site/billilge/api/backend/global/config/SecurityConfig.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ class SecurityConfig (
7474
fun apiConfigurationSource(): CorsConfigurationSource {
7575
val configuration = CorsConfiguration()
7676
configuration.allowedOrigins = allowedOrigins.toList()
77-
configuration.allowedMethods = mutableListOf("GET", "POST", "PATCH", "PUT", "DELETE")
77+
configuration.allowedMethods = mutableListOf("GET", "POST", "PATCH", "PUT", "DELETE", "OPTIONS", "HEAD")
7878
configuration.allowedHeaders = mutableListOf("Content-Type", "Authorization")
7979

8080
val source = UrlBasedCorsConfigurationSource()

src/main/kotlin/site/billilge/api/backend/global/external/s3/S3Service.kt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@ class S3Service(
1717
@Value("\${cloud.aws.s3.bucket}")
1818
val bucket: String,
1919
@Value("\${cloud.aws.s3.base-url}")
20-
val baseUrl: String
20+
val baseUrl: String,
21+
@Value("\${spring.profiles.active}")
22+
val activeProfile: String,
2123
) {
2224
fun uploadImageFile(imageFile: MultipartFile, newFileName: String = "items/${UUID.randomUUID()}"): String? {
2325
val originalName = imageFile.originalFilename ?: return null
2426

27+
2528
val ext = originalName.substring(originalName.lastIndexOf("."))
26-
val changedName = newFileName + ext
29+
val fileNameByProfile = if (activeProfile == "dev") "dev/${newFileName}" else newFileName
30+
val changedName = fileNameByProfile + ext
2731

2832
val metadata = ObjectMetadata().apply {
2933
contentType = imageFile.contentType

0 commit comments

Comments
 (0)