generated from pricelees/issue-pr-template
refactor: 초기 더미 데이터 생성 처리 로직 수정
This commit is contained in:
parent
9bc5b50a8f
commit
f04d521029
@ -8,11 +8,13 @@ import com.sangdol.roomescape.admin.infrastructure.persistence.AdminType
|
||||
import com.sangdol.roomescape.payment.infrastructure.common.*
|
||||
import com.sangdol.roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import com.sangdol.roomescape.schedule.infrastructure.persistence.ScheduleStatus
|
||||
import com.sangdol.roomescape.store.infrastructure.persistence.StoreEntity
|
||||
import com.sangdol.roomescape.supports.AdminFixture
|
||||
import com.sangdol.roomescape.supports.FunSpecSpringbootTest
|
||||
import com.sangdol.roomescape.supports.randomPhoneNumber
|
||||
import com.sangdol.roomescape.supports.randomString
|
||||
import com.sangdol.roomescape.theme.infrastructure.persistence.Difficulty
|
||||
import com.sangdol.roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import com.sangdol.roomescape.user.business.SIGNUP
|
||||
import com.sangdol.roomescape.user.infrastructure.persistence.UserEntity
|
||||
import com.sangdol.roomescape.user.infrastructure.persistence.UserStatus
|
||||
@ -21,6 +23,7 @@ import io.kotest.core.test.TestCaseOrder
|
||||
import jakarta.persistence.EntityManager
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.joinAll
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
@ -151,22 +154,22 @@ class DefaultDataInitializer : AbstractDataInitializer() {
|
||||
AdminPermissionLevel.READ_SUMMARY to 3
|
||||
)
|
||||
|
||||
val storeIds: List<Long> = transactionExecutionUtil.withNewTransaction(isReadOnly = true) {
|
||||
val stores: List<StoreEntity> = transactionExecutionUtil.withNewTransaction(isReadOnly = true) {
|
||||
entityManager.createQuery(
|
||||
"SELECT s.id FROM StoreEntity s",
|
||||
Long::class.java
|
||||
"SELECT s FROM StoreEntity s",
|
||||
StoreEntity::class.java
|
||||
).resultList
|
||||
}!!.map { it as Long }
|
||||
}!!
|
||||
|
||||
transactionExecutionUtil.withNewTransaction(isReadOnly = false) {
|
||||
storeIds.forEach { storeId ->
|
||||
stores.forEach { store ->
|
||||
// StoreManager 1명 생성
|
||||
val storeManager = AdminFixture.create(
|
||||
account = "$storeId",
|
||||
account = store.name,
|
||||
name = randomKoreanName(),
|
||||
phone = randomPhoneNumber(),
|
||||
type = AdminType.STORE,
|
||||
storeId = storeId,
|
||||
storeId = store.id,
|
||||
permissionLevel = AdminPermissionLevel.FULL_ACCESS
|
||||
).apply {
|
||||
this.createdBy = superHQAdmin.id
|
||||
@ -178,11 +181,11 @@ class DefaultDataInitializer : AbstractDataInitializer() {
|
||||
storeAdminCountsByPermissionLevel.forEach { (permissionLevel, count) ->
|
||||
repeat(count) { index ->
|
||||
AdminFixture.create(
|
||||
account = randomString(),
|
||||
account = "${store.name}-${permissionLevel.ordinal}${index}",
|
||||
name = randomKoreanName(),
|
||||
phone = randomPhoneNumber(),
|
||||
type = AdminType.STORE,
|
||||
storeId = storeId,
|
||||
storeId = store.id,
|
||||
permissionLevel = permissionLevel
|
||||
).apply {
|
||||
this.createdBy = storeManager.id
|
||||
@ -217,7 +220,7 @@ class DefaultDataInitializer : AbstractDataInitializer() {
|
||||
val batchArgs = mutableListOf<Array<Any>>()
|
||||
|
||||
repeat(500) { i ->
|
||||
val randomDay = if (i <= 9) (1..30).random() else (1..365 * 2).random()
|
||||
val randomDay = if (i <= 9) (7..30).random() else (30..365 * 2).random()
|
||||
val randomCreatedAt: LocalDateTime = LocalDateTime.now().minusDays(randomDay.toLong())
|
||||
val randomThemeName =
|
||||
(1..7).random().let { repeat -> (1..repeat).joinToString("") { randomKoreanName() } }
|
||||
@ -417,25 +420,49 @@ class UserDataInitializer : AbstractDataInitializer() {
|
||||
class ScheduleDataInitializer : AbstractDataInitializer() {
|
||||
init {
|
||||
context("일정 초기 데이터 생성") {
|
||||
test("테마 생성일 기준으로 다음 3일차, 매일 5개의 일정을 모든 매장에 생성") {
|
||||
test("테마 생성일 기준으로 다음 3일차, 매일 최대 10개의 일정을 모든 매장에 생성") {
|
||||
val stores: List<Pair<Long, Long>> = getStoreWithManagers()
|
||||
val themes: List<Triple<Long, Short, LocalDateTime>> = getThemes()
|
||||
val maxAvailableMinutes = themes.maxOf { it.second.toInt() }
|
||||
val scheduleCountPerDay = 5
|
||||
|
||||
val themes: List<ThemeEntity> = getThemes()
|
||||
val maxScheduleCountPerDay = 10
|
||||
val startTime = LocalTime.of(10, 0)
|
||||
var lastTime = startTime
|
||||
val times = mutableListOf<LocalTime>()
|
||||
|
||||
repeat(scheduleCountPerDay) {
|
||||
times.add(lastTime)
|
||||
lastTime = lastTime.plusMinutes(maxAvailableMinutes.toLong() + 10L)
|
||||
val themeWithTimes: Map<ThemeEntity, List<LocalTime>> = themes.associateWith { theme ->
|
||||
val times = mutableListOf<LocalTime>()
|
||||
val themeAvailableMinutes = theme.availableMinutes
|
||||
var lastTime = startTime
|
||||
|
||||
while (times.size <= maxScheduleCountPerDay && lastTime.hour in (10..23)) {
|
||||
times.add(lastTime)
|
||||
lastTime = lastTime.plusMinutes(themeAvailableMinutes + 10L)
|
||||
}
|
||||
|
||||
times
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
themes.forEach { theme ->
|
||||
stores.map { store ->
|
||||
launch(Dispatchers.IO) {
|
||||
processTheme(theme, stores, times)
|
||||
processTheme(store, themeWithTimes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test("내일 ~ 일주일 뒤 까지의 일정 생성") {
|
||||
// val stores: List<Pair<Long, Long>> = getStoreWithManagers()
|
||||
// val availableThemes: List<ThemeEntity> = transactionExecutionUtil.withNewTransaction(isReadOnly = true) {
|
||||
// entityManager.createQuery(
|
||||
// "SELECT t FROM ThemeEntity t WHERE t.isActive = true AND t.createdAt >", ThemeEntity::class.java
|
||||
// ).resultList
|
||||
// }!!.take(10)
|
||||
|
||||
coroutineScope {
|
||||
val jobs = (1..100).map { i ->
|
||||
launch(Dispatchers.IO) {
|
||||
val threadName = Thread.currentThread().name
|
||||
println("[$i] 시작: $threadName")
|
||||
delay(1)
|
||||
println("[$i] 완료: $threadName")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -444,9 +471,8 @@ class ScheduleDataInitializer : AbstractDataInitializer() {
|
||||
}
|
||||
|
||||
private suspend fun processTheme(
|
||||
theme: Triple<Long, Short, LocalDateTime>,
|
||||
stores: List<Pair<Long, Long>>,
|
||||
times: List<LocalTime>
|
||||
store: Pair<Long, Long>,
|
||||
themeWithTimes: Map<ThemeEntity, List<LocalTime>>
|
||||
) {
|
||||
val sql = """
|
||||
INSERT INTO schedule (
|
||||
@ -457,24 +483,22 @@ class ScheduleDataInitializer : AbstractDataInitializer() {
|
||||
|
||||
val batchArgs = mutableListOf<Array<Any>>()
|
||||
|
||||
val now = LocalDateTime.now()
|
||||
stores.forEach { (storeId, adminId) ->
|
||||
(1..3).forEach { dayOffset ->
|
||||
val date = theme.third.toLocalDate().plusDays(dayOffset.toLong())
|
||||
|
||||
val status = ScheduleStatus.RESERVED.name
|
||||
themeWithTimes.forEach { (theme, times) ->
|
||||
val themeCreatedAt = theme.createdAt
|
||||
(1..3).forEach {
|
||||
val date = themeCreatedAt.toLocalDate().plusDays(it.toLong())
|
||||
times.forEach { time ->
|
||||
val scheduledAt = LocalDateTime.of(date, time)
|
||||
val status =
|
||||
if (scheduledAt.isAfter(now)) ScheduleStatus.AVAILABLE.name else ScheduleStatus.RESERVED.name
|
||||
|
||||
val storeId = store.first
|
||||
val storeAdminId = store.second
|
||||
batchArgs.add(
|
||||
arrayOf(
|
||||
idGenerator.create(), storeId, theme.first, date, time,
|
||||
status, adminId, adminId, Timestamp.valueOf(now), Timestamp.valueOf(now)
|
||||
idGenerator.create(), storeId, theme.id, date, time,
|
||||
status, storeAdminId, storeAdminId, themeCreatedAt.plusHours(1), themeCreatedAt.plusHours(1)
|
||||
)
|
||||
)
|
||||
|
||||
if (batchArgs.size >= 500) {
|
||||
if (batchArgs.size >= 300) {
|
||||
executeBatch(sql, batchArgs).also { batchArgs.clear() }
|
||||
}
|
||||
}
|
||||
@ -500,17 +524,13 @@ class ScheduleDataInitializer : AbstractDataInitializer() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getThemes(): List<Triple<Long, Short, LocalDateTime>> {
|
||||
private fun getThemes(): List<ThemeEntity> {
|
||||
return transactionExecutionUtil.withNewTransaction(isReadOnly = true) {
|
||||
entityManager.createQuery(
|
||||
"SELECT t._id, t.availableMinutes, t.createdAt FROM ThemeEntity t",
|
||||
List::class.java
|
||||
)
|
||||
.resultList
|
||||
}!!.map {
|
||||
val array = it as List<*>
|
||||
Triple(array[0] as Long, array[1] as Short, array[2] as LocalDateTime)
|
||||
}
|
||||
"SELECT t FROM ThemeEntity t",
|
||||
ThemeEntity::class.java
|
||||
).resultList
|
||||
}!!
|
||||
}
|
||||
}
|
||||
|
||||
@ -528,10 +548,10 @@ class ReservationDataInitializer : AbstractDataInitializer() {
|
||||
init {
|
||||
context("예약 초기 데이터 생성") {
|
||||
test("${ScheduleStatus.RESERVED}인 모든 일정에 예약을 1개씩 배정한다.") {
|
||||
val chunkSize = 10_000
|
||||
val chunkSize = 500
|
||||
|
||||
val chunkedSchedules: List<List<ScheduleWithThemeParticipants>> = entityManager.createQuery(
|
||||
"SELECT new com.sangdol.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status",
|
||||
"SELECT new com.sangdol.roomescape.data.ScheduleWithThemeParticipants(s._id, t.minParticipants, t.maxParticipants) FROM ScheduleEntity s JOIN ThemeEntity t ON s.themeId = t.id WHERE s.status = :status",
|
||||
ScheduleWithThemeParticipants::class.java
|
||||
).setParameter("status", ScheduleStatus.RESERVED).resultList.chunked(chunkSize)
|
||||
|
||||
@ -587,10 +607,6 @@ class ReservationDataInitializer : AbstractDataInitializer() {
|
||||
user.id,
|
||||
)
|
||||
)
|
||||
|
||||
if (batchArgs.size >= 1_000) {
|
||||
executeBatch(sql, batchArgs).also { batchArgs.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
if (batchArgs.isNotEmpty()) executeBatch(sql, batchArgs).also { batchArgs.clear() }
|
||||
@ -671,7 +687,7 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
}
|
||||
|
||||
coroutineScope {
|
||||
allReservations.chunked(10_000).forEach { reservations ->
|
||||
allReservations.chunked(500).forEach { reservations ->
|
||||
launch(Dispatchers.IO) {
|
||||
processPaymentAndDefaultDetail(reservations)
|
||||
}
|
||||
@ -681,12 +697,12 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
|
||||
test("기존 결제 데이터에 상세 정보(계좌이체, 카드, 간편결제) 데이터를 생성한다.") {
|
||||
val allPayments: List<PaymentWithMethods> = entityManager.createQuery(
|
||||
"SELECT new com.sangdol.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId",
|
||||
"SELECT new com.sangdol.roomescape.data.PaymentWithMethods(pd._id, p.totalAmount, p.method) FROM PaymentEntity p JOIN PaymentDetailEntity pd ON p._id = pd.paymentId",
|
||||
PaymentWithMethods::class.java
|
||||
).resultList
|
||||
|
||||
coroutineScope {
|
||||
allPayments.chunked(10_000).forEach { payments ->
|
||||
allPayments.chunked(500).forEach { payments ->
|
||||
launch(Dispatchers.IO) {
|
||||
processPaymentDetail(payments)
|
||||
}
|
||||
@ -731,9 +747,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
approvedAtCache,
|
||||
)
|
||||
)
|
||||
if (paymentBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentSql, paymentBatchArgs).also { paymentBatchArgs.clear() }
|
||||
}
|
||||
|
||||
val suppliedAmount: Int = (totalPrice * 0.9).toInt()
|
||||
val vat: Int = (totalPrice - suppliedAmount)
|
||||
@ -746,10 +759,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
vat
|
||||
)
|
||||
)
|
||||
|
||||
if (detailBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentDetailSql, detailBatchArgs).also { detailBatchArgs.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
if (paymentBatchArgs.isNotEmpty()) {
|
||||
@ -780,9 +789,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
settlementStatus
|
||||
)
|
||||
)
|
||||
if (transferBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentBankTransferDetailSql, transferBatchArgs).also { transferBatchArgs.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
PaymentMethod.EASY_PAY -> {
|
||||
@ -803,10 +809,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
)
|
||||
)
|
||||
|
||||
if (cardBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() }
|
||||
}
|
||||
|
||||
} else {
|
||||
easypayPrepaidBatchArgs.add(
|
||||
arrayOf(
|
||||
@ -816,10 +818,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
randomDiscountAmount,
|
||||
)
|
||||
)
|
||||
|
||||
if (easypayPrepaidBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentEasypayPrepaidDetailSql, easypayPrepaidBatchArgs).also { easypayPrepaidBatchArgs.clear() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -839,10 +837,6 @@ class PaymentDataInitializer : AbstractDataInitializer() {
|
||||
0,
|
||||
)
|
||||
)
|
||||
|
||||
if (cardBatchArgs.size >= 1_000) {
|
||||
executeBatch(paymentCardDetailSql, cardBatchArgs).also { cardBatchArgs.clear() }
|
||||
}
|
||||
}
|
||||
|
||||
else -> return@forEach
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user