Skip to content

Commit e9dbff3

Browse files
authored
Merge pull request #61 from CodIN-INU/develop
fix: #60 티켓팅 수정 사항 적용
2 parents f61b9f6 + 571f069 commit e9dbff3

21 files changed

Lines changed: 213 additions & 113 deletions

src/main/java/inu/codin/codinticketingapi/domain/admin/controller/EventAdminControllerImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,9 +132,9 @@ public ResponseEntity<SingleResponse<EventStockResponse>> getStock(@PathVariable
132132
*/
133133
@PreAuthorize("hasAnyRole('MANAGER', 'ADMIN')")
134134
@DeleteMapping("{eventId}/management/cancel/{userId}")
135-
public ResponseEntity<SingleResponse<Boolean>> cancelTicket(@PathVariable Long eventId, @PathVariable String userId) {
136-
137-
return ResponseEntity.ok(new SingleResponse<>(200, "사용자 티켓 취소 성공", eventAdminService.cancelTicket(eventId, userId)));
135+
public ResponseEntity<SingleResponse<?>> cancelTicket(@PathVariable Long eventId, @PathVariable String userId) {
136+
eventAdminService.cancelTicket(eventId, userId);
137+
return ResponseEntity.ok(new SingleResponse<>(200, "사용자 티켓 취소 성공", null));
138138
}
139139

140140
/**

src/main/java/inu/codin/codinticketingapi/domain/admin/controller/swagger/EventAdminController.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ ResponseEntity<SingleResponse<EventStockResponse>> getStock(
108108
@ApiResponses(value = {
109109
@ApiResponse(responseCode = "200", description = "사용자 티켓 취소 성공")
110110
})
111-
ResponseEntity<SingleResponse<Boolean>> cancelTicket(
111+
ResponseEntity<SingleResponse<?>> cancelTicket(
112112
@Parameter(description = "이벤트 ID", example = "1", required = true) @PathVariable Long eventId,
113113
@Parameter(description = "티켓팅을 취소할 사용자 ID", example = "1", required = true) @PathVariable String userId);
114114

src/main/java/inu/codin/codinticketingapi/domain/admin/dto/request/EventCreateRequest.java

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,18 +46,28 @@ public class EventCreateRequest {
4646
@Schema(description = "이벤트 재고 수량", example = "100")
4747
private int quantity;
4848

49-
@NotNull(message = "이벤트 시작 시간은 필수입니다")
50-
@Future(message = "이벤트 시작 시간은 현재 시간보다 나중이어야 합니다")
51-
@Schema(description = "이벤트 시작 시간", example = "2025-07-25T10:00:00")
49+
@NotNull(message = "티켓팅 이벤트 시작 시간은 필수입니다")
50+
@Future(message = "티켓팅 이벤트 시작 시간은 현재 시간보다 나중이어야 합니다")
5251
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
52+
@Schema(description = "티켓팅 이벤트 시작 시간", example = "2025-07-25T10:00:00")
5353
private LocalDateTime eventTime;
5454

55-
@NotNull(message = "이벤트 마감 시간은 필수입니다")
56-
@Future(message = "이벤트 마감 시간은 현재 시간보다 나중이어야 합니다")
57-
@Schema(description = "이벤트 종료 시간", example = "2025-07-25T12:00:00")
55+
@NotNull(message = "티켓팅 이벤트 종료 시간은 필수입니다")
56+
@Future(message = "티켓팅 이벤트 종료 시간은 현재 시간보다 나중이어야 합니다")
5857
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
58+
@Schema(description = "티켓팅 이벤트 종료 시간", example = "2025-07-25T12:00:00")
5959
private LocalDateTime eventEndTime;
6060

61+
@NotNull(message = "티켓팅 상품 수령 시작 시간은 필수입니다")
62+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
63+
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025-07-02T16:00")
64+
private LocalDateTime eventReceivedStartTime;
65+
66+
@NotNull(message = "티켓팅 상품 수령 종료 시간은 필수입니다")
67+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
68+
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025-07-02T16:00")
69+
private LocalDateTime eventReceivedEndTime;
70+
6171
@Pattern(regexp = "\\d{2,3}-\\d{3,4}-\\d{4}", message = "올바른 전화번호 형식이 아닙니다")
6272
@Schema(description = "문의 전화번호", example = "02-1234-5678")
6373
private String inquiryNumber;
@@ -72,6 +82,8 @@ public Event toEntity(String userId, String eventImageUrl) {
7282
.campus(this.campus)
7383
.eventTime(this.eventTime)
7484
.eventEndTime(this.eventEndTime)
85+
.eventReceivedStartTime(this.eventReceivedStartTime)
86+
.eventReceivedEndTime(this.eventReceivedEndTime)
7587
.eventImageUrl(eventImageUrl)
7688
.title(this.title)
7789
.locationInfo(this.locationInfo)
@@ -83,11 +95,15 @@ public Event toEntity(String userId, String eventImageUrl) {
8395
}
8496

8597
/** 이벤트 시간 검증
86-
* - 이벤트 종료 시간은 시작 시간 이후
98+
* - 티켓팅 종료 시간은 시작 시간 이후
99+
* - 티켓팅 상품 수령 마감 시간은 시작 시간 이후
87100
* */
88101
public void validateEventTimes() {
89102
if (eventEndTime != null && eventTime != null && !eventEndTime.isAfter(eventTime)) {
90103
throw new TicketingException(TicketingErrorCode.ILLEGAL_ARGUMENT);
91104
}
105+
if (eventReceivedStartTime != null && eventReceivedEndTime != null && !eventReceivedEndTime.isAfter(eventReceivedStartTime)) {
106+
throw new TicketingException(TicketingErrorCode.ILLEGAL_ARGUMENT);
107+
}
92108
}
93109
}

src/main/java/inu/codin/codinticketingapi/domain/admin/dto/request/EventUpdateRequest.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,16 @@ public class EventUpdateRequest {
5757
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
5858
private LocalDateTime eventEndTime;
5959

60+
@NotNull(message = "티켓팅 상품 수령 시작 시간은 필수입니다")
61+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
62+
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025-07-02 16:00")
63+
private LocalDateTime eventReceivedStartTime;
64+
65+
@NotNull(message = "티켓팅 상품 수령 종료 시간은 필수입니다")
66+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul")
67+
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025-07-02 16:00")
68+
private LocalDateTime eventReceivedEndTime;
69+
6070
@Pattern(regexp = "\\d{2,3}-\\d{3,4}-\\d{4}", message = "올바른 전화번호 형식이 아닙니다")
6171
@Schema(description = "문의 전화번호", example = "02-9876-5432")
6272
private String inquiryNumber;

src/main/java/inu/codin/codinticketingapi/domain/admin/dto/response/EventParticipationProfilePageResponse.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package inu.codin.codinticketingapi.domain.admin.dto.response;
22

3+
import com.fasterxml.jackson.annotation.JsonFormat;
4+
import inu.codin.codinticketingapi.domain.admin.entity.Event;
5+
import inu.codin.codinticketingapi.domain.ticketing.entity.Stock;
36
import io.swagger.v3.oas.annotations.media.Schema;
47
import lombok.AllArgsConstructor;
8+
import lombok.Builder;
59
import lombok.Getter;
610

711
import java.time.LocalDateTime;
812
import java.util.List;
913

1014
@Getter
1115
@AllArgsConstructor
16+
@Builder
1217
@Schema(description = "이벤트 참여자 목록 및 페이지 정보를 담는 응답 DTO")
1318
public class EventParticipationProfilePageResponse {
1419
@Schema(description = "이벤트 참여자 프로필 목록")
@@ -23,10 +28,32 @@ public class EventParticipationProfilePageResponse {
2328
private int stock;
2429
@Schema(description = "수령 대기", example = "100")
2530
private long waitNum;
26-
@Schema(description = "이벤트 종료 시간", example = "2025-07-25T12:00:00")
31+
32+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
33+
@Schema(description = "이벤트 티켓팅 시작 시간", example = "2025.07.02 (수) 16:00")
34+
private LocalDateTime eventTime;
35+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
36+
@Schema(description = "이벤트 티켓팅 종료 시간", example = "2025.07.02 (수) 16:00")
2737
private LocalDateTime eventEndTime;
38+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
39+
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025.07.02 (수) 16:00")
40+
private LocalDateTime eventReceivedStartTime;
41+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
42+
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025.07.02 (수) 16:00")
43+
private LocalDateTime eventReceivedEndTime;
2844

29-
public static EventParticipationProfilePageResponse of(List<EventParticipationProfileResponse> list, int lastPage, int nextPage, String title, int stock, long waitNum, LocalDateTime eventEndTime) {
30-
return new EventParticipationProfilePageResponse(list, lastPage, nextPage, title, stock, waitNum, eventEndTime);
45+
public static EventParticipationProfilePageResponse from(Event event, Stock stock, List<EventParticipationProfileResponse> list, int lastPage, int nextPage, long waitNum) {
46+
return EventParticipationProfilePageResponse.builder()
47+
.eventParticipationProfileResponseList(list)
48+
.lastPage(lastPage)
49+
.nextPage(nextPage)
50+
.title(event.getTitle())
51+
.stock(stock.getRemainingStock())
52+
.waitNum(waitNum)
53+
.eventTime(event.getEventTime())
54+
.eventEndTime(event.getEventEndTime())
55+
.eventReceivedStartTime(event.getEventReceivedStartTime())
56+
.eventReceivedEndTime(event.getEventReceivedEndTime())
57+
.build();
3158
}
3259
}

src/main/java/inu/codin/codinticketingapi/domain/admin/dto/response/EventResponse.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package inu.codin.codinticketingapi.domain.admin.dto.response;
22

3+
import com.fasterxml.jackson.annotation.JsonFormat;
34
import inu.codin.codinticketingapi.domain.admin.entity.Event;
45
import inu.codin.codinticketingapi.domain.admin.entity.EventStatus;
56
import inu.codin.codinticketingapi.domain.ticketing.entity.Campus;
@@ -19,10 +20,6 @@ public class EventResponse {
1920
private Long id;
2021
@Schema(description = "이벤트가 진행되는 캠퍼스", example = "송도 캠퍼스")
2122
private Campus campus;
22-
@Schema(description = "이벤트 시작 시간", example = "2025-07-25T10:00:00")
23-
private LocalDateTime eventTime;
24-
@Schema(description = "이벤트 종료 시간", example = "2025-07-25T12:00:00")
25-
private LocalDateTime eventEndTime;
2623
@Schema(description = "이벤트 이미지 URL", example = "https://example.com/image.jpg")
2724
private String eventImageUrl;
2825
@Schema(description = "이벤트 제목", example = "새내기 환영회")
@@ -42,12 +39,23 @@ public class EventResponse {
4239
@Schema(description = "이벤트 상태", example = "UPCOMING")
4340
private EventStatus status;
4441

45-
public static EventResponse of(Event event) {
42+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
43+
@Schema(description = "이벤트 티켓팅 시작 시간", example = "2025.07.02 (수) 16:00")
44+
private LocalDateTime eventTime;
45+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
46+
@Schema(description = "이벤트 티켓팅 종료 시간", example = "2025.07.02 (수) 16:00")
47+
private LocalDateTime eventEndTime;
48+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
49+
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025.07.02 (수) 16:00")
50+
private LocalDateTime eventReceivedStartTime;
51+
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy.MM.dd (E) HH:mm", timezone = "Asia/Seoul")
52+
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025.07.02 (수) 16:00")
53+
private LocalDateTime eventReceivedEndTime;
54+
55+
public static EventResponse from(Event event) {
4656
return EventResponse.builder()
4757
.id(event.getId())
4858
.campus(event.getCampus())
49-
.eventTime(event.getEventTime())
50-
.eventEndTime(event.getEventEndTime())
5159
.eventImageUrl(event.getEventImageUrl())
5260
.title(event.getTitle())
5361
.locationInfo(event.getLocationInfo())
@@ -57,6 +65,10 @@ public static EventResponse of(Event event) {
5765
.inquiryNumber(event.getInquiryNumber())
5866
.promotionLink(event.getPromotionLink())
5967
.status(event.getEventStatus())
68+
.eventTime(event.getEventTime())
69+
.eventEndTime(event.getEventEndTime())
70+
.eventReceivedStartTime(event.getEventReceivedStartTime())
71+
.eventReceivedEndTime(event.getEventReceivedEndTime())
6072
.build();
6173
}
6274
}

src/main/java/inu/codin/codinticketingapi/domain/admin/dto/response/EventStockResponse.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ public class EventStockResponse {
1313
@Schema(description = "이벤트 잔여 수량", example = "50")
1414
private int eventStock;
1515

16-
public static EventStockResponse of(int eventStock) {
16+
public static EventStockResponse from(int eventStock) {
1717
return EventStockResponse.builder()
1818
.eventStock(eventStock)
1919
.build();

src/main/java/inu/codin/codinticketingapi/domain/admin/entity/Event.java

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,22 @@ public class Event extends BaseEntity {
3939
@Column(name = "campus", nullable = false)
4040
private Campus campus;
4141

42-
/** 이벤트 시작 시간 */
42+
/** 티켓팅 시작 시간 */
4343
@Column(name = "event_time", nullable = false)
4444
private LocalDateTime eventTime;
4545

46-
/** 이벤트 종료 시간 */
46+
/** 티켓팅 종료 시간 */
4747
@Column(name = "event_end_time", nullable = false)
4848
private LocalDateTime eventEndTime;
4949

50+
/** 티켓팅 상품 수령 시작 시간 */
51+
@Column(name = "event_received_start_time")
52+
private LocalDateTime eventReceivedStartTime;
53+
54+
/** 티켓팅 상품 수령 종료 시간 */
55+
@Column(name = "event_received_end_time")
56+
private LocalDateTime eventReceivedEndTime;
57+
5058
/** 이제 단일 이미지 URL */
5159
@Column(name = "event_image_url")
5260
private String eventImageUrl;
@@ -89,12 +97,14 @@ public class Event extends BaseEntity {
8997
private EventStatus eventStatus;
9098

9199
@Builder
92-
public Event(Long id, String userId, Campus campus, LocalDateTime eventTime, LocalDateTime eventEndTime, String eventImageUrl, String title, String locationInfo, String target, String description, String inquiryNumber, String promotionLink, Stock stock) {
100+
public Event(Long id, String userId, Campus campus, LocalDateTime eventTime, LocalDateTime eventEndTime, LocalDateTime eventReceivedStartTime, LocalDateTime eventReceivedEndTime, String eventImageUrl, String title, String locationInfo, String target, String description, String inquiryNumber, String promotionLink, Stock stock) {
93101
this.id = id;
94102
this.userId = userId;
95103
this.campus = campus;
96104
this.eventTime = eventTime;
97105
this.eventEndTime = eventEndTime;
106+
this.eventReceivedStartTime = eventReceivedStartTime;
107+
this.eventReceivedEndTime = eventReceivedEndTime;
98108
this.eventImageUrl = eventImageUrl;
99109
this.title = title;
100110
this.locationInfo = locationInfo;
@@ -107,21 +117,21 @@ public Event(Long id, String userId, Campus campus, LocalDateTime eventTime, Loc
107117
this.stock = stock;
108118
}
109119

110-
public void setStock(Stock stock) {
111-
this.stock = stock;
112-
}
113-
120+
// todo: eventTime을 바꾸면 EventStatusScheduler 부분이 꼬일 수 있음 수정 필요
114121
public void updateFrom(EventUpdateRequest dto) {
115122
this.campus = dto.getCampus();
116123
this.eventTime = dto.getEventTime();
117124
this.eventEndTime = dto.getEventEndTime();
125+
this.eventReceivedStartTime = dto.getEventReceivedStartTime();
126+
this.eventReceivedEndTime = dto.getEventReceivedEndTime();
118127
this.title = dto.getTitle();
119128
this.locationInfo = dto.getLocationInfo();
120129
this.target = dto.getTarget();
121130
this.description = dto.getDescription();
122131
this.inquiryNumber = dto.getInquiryNumber();
123132
this.promotionLink = dto.getPromotionLink();
124133

134+
// todo: stock을 업데이트 하고 Redis ZSET 수량 조절도 필요함
125135
if (this.stock != null && this.eventStatus == EventStatus.UPCOMING) {
126136
this.stock.updateStock(dto.getStock());
127137
}
@@ -144,6 +154,13 @@ public void closeEvent() {
144154
this.eventEndTime = LocalDateTime.now();
145155
}
146156

157+
public void setStock(Stock stock) {
158+
this.stock = stock;
159+
if (stock != null && stock.getEvent() != this) {
160+
stock.setEvent(this); // 소유자 쪽 동기화
161+
}
162+
}
163+
147164
/** 이벤트 비밀번호 생성 (무작위 4자리 숫자) */
148165
private String generateEventPassword() {
149166
return String.format("%04d", new Random().nextInt(10000));

src/main/java/inu/codin/codinticketingapi/domain/admin/scheduler/EventStatusScheduler.java

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ public void scheduleCreateOrUpdatedEvent(Event event) {
4444
scheduler.scheduleJob(startJobDetail, startTrigger);
4545
log.info("새/업데이트 이벤트 시작 Job 스케줄링: ID = {}", event.getId());
4646

47-
JobDetail endJobDetail = createEndJob(event);
48-
Trigger endTrigger = createEndTrigger(event);
49-
scheduler.scheduleJob(endJobDetail, endTrigger);
50-
log.info("새/업데이트 이벤트 종료 Job 스케줄링: ID = {}", event.getId());
47+
// JobDetail endJobDetail = createEndJob(event);
48+
// Trigger endTrigger = createEndTrigger(event);
49+
// scheduler.scheduleJob(endJobDetail, endTrigger);
50+
// log.info("새/업데이트 이벤트 종료 Job 스케줄링: ID = {}", event.getId());
5151

5252
JobDetail stockJob = createStockCheckJob(event);
5353
Trigger stockTrigger = createStockCheckTrigger(event);
@@ -78,11 +78,11 @@ private void scheduleUpcomingEvents() {
7878
scheduler.scheduleJob(startJobDetail, startTrigger);
7979
log.info("이벤트 시작 Job 스케줄링 완료: Event ID = {}, 시작 시간 = {}", event.getId(), event.getEventTime());
8080

81-
JobDetail endJobDetail = createEndJob(event);
82-
Trigger endTrigger = createEndTrigger(event);
83-
84-
scheduler.scheduleJob(endJobDetail, endTrigger);
85-
log.info("이벤트 종료 Job 스케줄링 완료: Event ID = {}, 종료 시간 = {}", event.getId(), event.getEventEndTime());
81+
// JobDetail endJobDetail = createEndJob(event);
82+
// Trigger endTrigger = createEndTrigger(event);
83+
//
84+
// scheduler.scheduleJob(endJobDetail, endTrigger);
85+
// log.info("이벤트 종료 Job 스케줄링 완료: Event ID = {}, 종료 시간 = {}", event.getId(), event.getEventEndTime());
8686

8787
} catch (SchedulerException e) {
8888
log.error("이벤트 스케줄링 중 오류 발생: Event ID = {}", event.getId(), e);
@@ -105,11 +105,11 @@ private void scheduleActiveEvents() {
105105

106106
for (Event event : activeEvents) {
107107
try {
108-
// 종료 Job만 스케줄링 (시작은 이미 지났으므로)
109-
JobDetail endJobDetail = createEndJob(event);
110-
Trigger endTrigger = createEndTrigger(event);
111-
scheduler.scheduleJob(endJobDetail, endTrigger);
112-
log.info("ACTIVE 이벤트 종료 Job 스케줄링: Event ID = {}", event.getId());
108+
// // 종료 Job만 스케줄링 (시작은 이미 지났으므로)
109+
// JobDetail endJobDetail = createEndJob(event);
110+
// Trigger endTrigger = createEndTrigger(event);
111+
// scheduler.scheduleJob(endJobDetail, endTrigger);
112+
// log.info("ACTIVE 이벤트 종료 Job 스케줄링: Event ID = {}", event.getId());
113113

114114
// StockCheckJob은 즉시 시작
115115
JobDetail stockJob = createStockCheckJob(event);
@@ -174,13 +174,15 @@ private Trigger createStartTrigger(Event event) {
174174
.build();
175175
}
176176

177+
// 이벤트 종료를 수동으로 조작
177178
private JobDetail createEndJob(Event event) {
178179
return newJob(EventEndJob.class)
179180
.withIdentity("endJob-" + event.getId(), "event-status")
180181
.usingJobData("eventId", event.getId())
181182
.build();
182183
}
183184

185+
// 이벤트 종료를 수동으로 조작
184186
private Trigger createEndTrigger(Event event) {
185187
return newTrigger()
186188
.withIdentity("endTrigger-" + event.getId(), "event-status")

0 commit comments

Comments
 (0)