Skip to content

Commit 8b78091

Browse files
authored
[chore] Seed Data 시나리오 생성 API - 개발용
1 parent 34cab7e commit 8b78091

10 files changed

Lines changed: 497 additions & 12 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.back.api.seed.controller;
2+
3+
import java.util.List;
4+
5+
import org.springframework.context.annotation.Profile;
6+
import org.springframework.web.bind.annotation.PostMapping;
7+
import org.springframework.web.bind.annotation.RequestMapping;
8+
import org.springframework.web.bind.annotation.RequestParam;
9+
import org.springframework.web.bind.annotation.RestController;
10+
11+
import com.back.api.seed.dto.ScenarioSeeder;
12+
import com.back.api.seed.dto.response.SeedResult;
13+
import com.back.global.response.ApiResponse;
14+
15+
import lombok.RequiredArgsConstructor;
16+
17+
@RestController
18+
@RequiredArgsConstructor
19+
@Profile({"perf", "dev"})
20+
@RequestMapping("/internal/seed")
21+
public class SeedController {
22+
23+
private final List<ScenarioSeeder> seeders;
24+
25+
@PostMapping
26+
public ApiResponse<SeedResult> run(@RequestParam String scenario) {
27+
ScenarioSeeder seeder = seeders.stream()
28+
.filter(seed -> seed.key().equals(scenario))
29+
.findFirst()
30+
.orElseThrow(() -> new IllegalArgumentException("Unknown scenario: " + scenario));
31+
32+
seeder.reset();
33+
return ApiResponse.ok(seeder.seed());
34+
}
35+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.back.api.seed.dto;
2+
3+
import com.back.api.seed.dto.response.SeedResult;
4+
5+
public interface ScenarioSeeder {
6+
String key();
7+
8+
void reset();
9+
10+
SeedResult seed();
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package com.back.api.seed.dto.response;
2+
3+
public record SeedResult(
4+
String scenario,
5+
Long eventId,
6+
int users,
7+
int preRegisteredUsers,
8+
boolean queueShuffled,
9+
String note
10+
) {
11+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.back.domain.seed.scenario;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.security.crypto.password.PasswordEncoder;
8+
import org.springframework.stereotype.Component;
9+
10+
import com.back.api.queue.service.QueueShuffleService;
11+
import com.back.api.seed.dto.ScenarioSeeder;
12+
import com.back.api.seed.dto.response.SeedResult;
13+
import com.back.domain.event.entity.Event;
14+
import com.back.domain.event.entity.EventStatus;
15+
import com.back.domain.event.repository.EventRepository;
16+
import com.back.domain.preregister.repository.PreRegisterRepository;
17+
import com.back.domain.seed.scenario.base.BaseSeedScenario;
18+
import com.back.domain.seed.util.SeedResetSupport;
19+
import com.back.domain.user.entity.User;
20+
import com.back.domain.user.repository.UserRepository;
21+
22+
@Component
23+
@Profile({"perf", "dev"})
24+
public class SeedScenario1 extends BaseSeedScenario implements ScenarioSeeder {
25+
26+
public SeedScenario1(
27+
SeedResetSupport resetSupport,
28+
UserRepository userRepository,
29+
PasswordEncoder passwordEncoder,
30+
EventRepository eventRepository,
31+
PreRegisterRepository preRegisterRepository,
32+
QueueShuffleService queueShuffleService
33+
) {
34+
super(
35+
resetSupport,
36+
userRepository,
37+
passwordEncoder,
38+
eventRepository,
39+
preRegisterRepository,
40+
queueShuffleService
41+
);
42+
}
43+
44+
@Override
45+
public String key() {
46+
return "preclosed_101_shuffled_before_open";
47+
}
48+
49+
@Override
50+
public void reset() {
51+
resetAll();
52+
}
53+
54+
@Override
55+
public SeedResult seed() {
56+
LocalDateTime now = LocalDateTime.now();
57+
58+
LocalDateTime ticketOpenAt = now.plusMinutes(30);
59+
60+
Event event = createEvent(
61+
now,
62+
"PRE-CLOSED + 101 PR + SHUFFLED + BEFORE OPEN",
63+
ticketOpenAt,
64+
EventStatus.PRE_OPEN
65+
);
66+
67+
List<User> users = createUsersN(101);
68+
createPreRegisters(event, users);
69+
shuffleQueue(event, users);
70+
71+
return new SeedResult(
72+
key(),
73+
event.getId(),
74+
users.size(),
75+
users.size(),
76+
true,
77+
"ticketOpenAt=" + ticketOpenAt + ", eventStatus=" + event.getStatus()
78+
);
79+
}
80+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package com.back.domain.seed.scenario;
2+
3+
import java.time.LocalDateTime;
4+
import java.util.List;
5+
6+
import org.springframework.context.annotation.Profile;
7+
import org.springframework.security.crypto.password.PasswordEncoder;
8+
import org.springframework.stereotype.Component;
9+
10+
import com.back.api.queue.service.QueueShuffleService;
11+
import com.back.api.seed.dto.ScenarioSeeder;
12+
import com.back.api.seed.dto.response.SeedResult;
13+
import com.back.domain.event.entity.Event;
14+
import com.back.domain.event.entity.EventStatus;
15+
import com.back.domain.event.repository.EventRepository;
16+
import com.back.domain.preregister.repository.PreRegisterRepository;
17+
import com.back.domain.seed.scenario.base.BaseSeedScenario;
18+
import com.back.domain.seed.util.SeedResetSupport;
19+
import com.back.domain.user.entity.User;
20+
import com.back.domain.user.repository.UserRepository;
21+
22+
@Component
23+
@Profile({"perf", "dev"})
24+
public class SeedScenario2 extends BaseSeedScenario implements ScenarioSeeder {
25+
26+
public SeedScenario2(
27+
SeedResetSupport resetSupport,
28+
UserRepository userRepository,
29+
PasswordEncoder passwordEncoder,
30+
EventRepository eventRepository,
31+
PreRegisterRepository preRegisterRepository,
32+
QueueShuffleService queueShuffleService
33+
) {
34+
super(
35+
resetSupport,
36+
userRepository,
37+
passwordEncoder,
38+
eventRepository,
39+
preRegisterRepository,
40+
queueShuffleService
41+
);
42+
}
43+
44+
@Override
45+
public String key() {
46+
return "preclosed_101_shuffled_open";
47+
}
48+
49+
@Override
50+
public void reset() {
51+
resetAll();
52+
}
53+
54+
@Override
55+
public SeedResult seed() {
56+
LocalDateTime now = LocalDateTime.now();
57+
58+
// 티켓 오픈 "시점" (이미 지난 시간으로 두면 EventOpenScheduler가 즉시 OPEN으로 바꿀 대상)
59+
LocalDateTime ticketOpenAt = now.minusSeconds(30);
60+
61+
Event event = createEvent(
62+
now,
63+
"PRE-CLOSED + 101 PR + SHUFFLED + OPEN DUE",
64+
ticketOpenAt,
65+
EventStatus.PRE_OPEN
66+
);
67+
68+
List<User> users = createUsersN(100);
69+
createPreRegisters(event, users);
70+
shuffleQueue(event, users); // Redis waiting + status QUEUE_READY
71+
72+
// 여기서는 OPEN으로 직접 바꾸지 않음.
73+
// -> EventOpenScheduler가 돌면 QUEUE_READY + ticketOpenAt <= now 조건으로 OPEN 전환됨.
74+
75+
return new SeedResult(
76+
key(),
77+
event.getId(),
78+
users.size(),
79+
users.size(),
80+
true,
81+
"ticketOpenAt=" + ticketOpenAt + ", eventStatus=" + event.getStatus() + " (OPEN scheduler target)"
82+
);
83+
}
84+
}
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
package com.back.domain.seed.scenario.base;
2+
3+
import java.time.LocalDate;
4+
import java.time.LocalDateTime;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
8+
import org.springframework.security.crypto.password.PasswordEncoder;
9+
10+
import com.back.api.queue.service.QueueShuffleService;
11+
import com.back.domain.event.entity.Event;
12+
import com.back.domain.event.entity.EventCategory;
13+
import com.back.domain.event.entity.EventStatus;
14+
import com.back.domain.event.repository.EventRepository;
15+
import com.back.domain.preregister.entity.PreRegister;
16+
import com.back.domain.preregister.repository.PreRegisterRepository;
17+
import com.back.domain.seed.util.SeedResetSupport;
18+
import com.back.domain.user.entity.User;
19+
import com.back.domain.user.entity.UserActiveStatus;
20+
import com.back.domain.user.entity.UserRole;
21+
import com.back.domain.user.repository.UserRepository;
22+
23+
import lombok.RequiredArgsConstructor;
24+
25+
@RequiredArgsConstructor
26+
public class BaseSeedScenario {
27+
28+
protected static final String PREFIX = "[SEED] ";
29+
30+
protected final SeedResetSupport resetSupport;
31+
protected final UserRepository userRepository;
32+
protected final PasswordEncoder passwordEncoder;
33+
protected final EventRepository eventRepository;
34+
protected final PreRegisterRepository preRegisterRepository;
35+
protected final QueueShuffleService queueShuffleService;
36+
37+
protected void resetAll() {
38+
resetSupport.resetSeedDataByPrefix(PREFIX);
39+
}
40+
41+
protected List<User> createUsersN(int userCount) {
42+
List<User> users = new ArrayList<>(userCount);
43+
String encoded = passwordEncoder.encode("abc12345");
44+
45+
for (int i = 1; i <= userCount; i++) {
46+
User user = User.builder()
47+
.email("seed" + i + "@test.com")
48+
.password(encoded)
49+
.fullName("Seed User " + i)
50+
.nickname("seedNick" + i)
51+
.role(UserRole.NORMAL)
52+
.activeStatus(UserActiveStatus.ACTIVE)
53+
.birthDate(LocalDate.of(2000, 1, 1))
54+
.build();
55+
56+
users.add(user);
57+
}
58+
59+
return userRepository.saveAll(users);
60+
}
61+
62+
protected Event createEvent(LocalDateTime now, String titleSuffix,
63+
LocalDateTime ticketOpenAt, EventStatus status) {
64+
65+
// 사전등록은 "마감" 상태로 만들 거라서 preCloseAt은 now보다 과거
66+
LocalDateTime preOpenAt = now.minusDays(7);
67+
LocalDateTime preCloseAt = now.minusMinutes(1);
68+
69+
LocalDateTime ticketCloseAt = ticketOpenAt.plusHours(2);
70+
LocalDateTime eventDate = now.plusDays(10);
71+
72+
Event event = Event.builder()
73+
.title(PREFIX + titleSuffix)
74+
.category(EventCategory.CONCERT)
75+
.description("seed data")
76+
.place("seed place")
77+
.imageUrl(null)
78+
.minPrice(10000)
79+
.maxPrice(50000)
80+
.preOpenAt(preOpenAt)
81+
.preCloseAt(preCloseAt)
82+
.ticketOpenAt(ticketOpenAt)
83+
.ticketCloseAt(ticketCloseAt)
84+
.eventDate(eventDate)
85+
.maxTicketAmount(1000)
86+
.status(status)
87+
.build();
88+
89+
return eventRepository.save(event);
90+
}
91+
92+
protected void createPreRegisters(Event event, List<User> users) {
93+
List<PreRegister> preRegisters = users.stream()
94+
.map(user -> PreRegister.builder()
95+
.event(event)
96+
.user(user)
97+
.preRegisterAgreeTerms(true)
98+
.preRegisterAgreePrivacy(true)
99+
.build()
100+
)
101+
.toList();
102+
103+
preRegisterRepository.saveAll(preRegisters);
104+
}
105+
106+
protected void shuffleQueue(Event event, List<User> users) {
107+
List<Long> userIds = users.stream().map(User::getId).toList();
108+
queueShuffleService.shuffleQueue(event.getId(), userIds);
109+
110+
event.changeStatus(EventStatus.QUEUE_READY);
111+
eventRepository.save(event);
112+
}
113+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package com.back.domain.seed.util;
2+
3+
import java.util.List;
4+
5+
import org.springframework.jdbc.core.JdbcTemplate;
6+
import org.springframework.stereotype.Component;
7+
8+
import com.back.domain.queue.repository.QueueEntryRedisRepository;
9+
10+
import lombok.RequiredArgsConstructor;
11+
12+
@Component
13+
@RequiredArgsConstructor
14+
public class SeedResetSupport {
15+
16+
private final JdbcTemplate jdbcTemplate;
17+
private final QueueEntryRedisRepository queueEntryRedisRepository;
18+
19+
public void resetSeedDataByPrefix(String prefix) {
20+
// seed 이벤트 id 목록 수집
21+
List<Long> eventIds = jdbcTemplate.queryForList(
22+
"SELECT id from events WHERE title LIKE ?",
23+
Long.class,
24+
prefix + "%"
25+
);
26+
27+
// Redis 키 정리
28+
for (Long eventId : eventIds) {
29+
queueEntryRedisRepository.clearAll(eventId);
30+
}
31+
32+
// seed 데이터 삭제
33+
jdbcTemplate.update(
34+
"DELETE FROM pre_registers WHERE event_id in (SELECT id FROM events WHERE title LIKE ?)",
35+
prefix + "%"
36+
);
37+
38+
jdbcTemplate.update(
39+
"DELETE FROM queue_entries WHERE event_id in (SELECT id FROM events WHERE title LIKE ?)",
40+
prefix + "%"
41+
);
42+
43+
jdbcTemplate.update(
44+
"DELETE FROM events WHERE title LIKE ?",
45+
prefix + "%"
46+
);
47+
48+
jdbcTemplate.update(
49+
"DELETE FROM users WHERE email LIKE ?",
50+
"seed%@test.com"
51+
);
52+
}
53+
}

0 commit comments

Comments
 (0)