Skip to content

Commit 09a18a2

Browse files
authored
refactor: [관리자] 게시글 변경 및 삭제 시, 검증 로직 추가
refactor: [관리자] 게시글 변경 및 삭제 시, 검증 로직 추가
2 parents e9dbff3 + c047043 commit 09a18a2

4 files changed

Lines changed: 102 additions & 82 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ out/
3636
### VS Code ###
3737
.vscode/
3838

39+
.env
3940
.env.*
4041
!.env.example
4142
logs

README.md

Lines changed: 77 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,77 +1,77 @@
1-
# CodIN Ticketing API
2-
3-
<img width="1324" height="679" alt="image" src="https://github.com/user-attachments/assets/d3719539-a47e-4043-a57a-408570411762" />
4-
5-
인천대학교 정보기술대학 티켓팅 시스템을 위한 Spring Boot REST API
6-
7-
## 프로젝트 개요
8-
9-
**CodIN Ticketing API** 는 인천대학교 정보기술대학의 다양한 **이벤트(간식 나눔, 행사 등)** 에 대한 **티켓팅 시스템** 을 제공 서버입니다.
10-
11-
### 주요 기능
12-
13-
<img width="10046" height="4961" alt="image" src="https://github.com/user-attachments/assets/53a52f4c-e766-4a9d-80cc-f1d507c19413" />
14-
15-
[API Endpoint 문서](/API.md)
16-
17-
- **이벤트 관리**: 티켓팅 이벤트 생성, 조회, 수정, 삭제
18-
- **사용자 프로필**: 수령자 정보 관리 (학과, 학번)
19-
- **티켓팅 참여**: 실시간 티켓팅 참여 및 교환권 발급
20-
- **전자 서명**: 수령 확인을 위한 전자 서명 기능
21-
- **관리자 기능**: 이벤트 관리, 수령 확인, 통계
22-
- **엑셀 다운로드**: 참여자 정보 엑셀 내보내기
23-
24-
## 기술 스택
25-
26-
- **Backend**: Spring Boot 3.5.3, Spring Security, Spring Data JPA, SSE
27-
- **Database**: MySQL 8.0
28-
- **MQ**: Redis Stream
29-
- **Authentication**: JWT
30-
31-
## 데이터 모델
32-
33-
<img width="2094" height="1186" alt="스크린샷 2025-07-24 18 12 10" src="https://github.com/user-attachments/assets/f124eb08-ffc9-4411-afbf-b56238273c01" />
34-
35-
### 주요 엔티티
36-
37-
- **Event**: 티켓팅 이벤트 정보
38-
- **Participation**: 이벤트별 참여자 정보 및 수령 상태
39-
- **Stock**: 티켓팅 이벤트 재고 정보
40-
41-
**Enum Class**
42-
- **Campus**: 캠퍼스 구분 (송도캠퍼스, 미추홀캠퍼스)
43-
- **Department**: 학과 정보 (컴퓨터공학부, 정보통신공학과 등)
44-
45-
## 외부 API 엔드포인트
46-
47-
- CodIN Main API : https://github.com/CodIN-INU/BACKEND
48-
- `GET /api/users` : 유저 조회 엔드 포인트
49-
50-
### 티켓팅 참여자 정보 엔드포인트
51-
52-
- `GET /api/users/ticketing-participation` : 유저 티켓팅 참여자 정보 조회
53-
- `PUT /api/users/ticketing-participation` : 유저 티켓팅 참여자 정보 수정
54-
55-
## 인증 및 권한
56-
57-
- **JWT 토큰 기반 인증**
58-
- JWT 토큰에서 User Email 추출 -> `SecurityContextHolder`
59-
- SecurityUtils 클래스로 SecurityContextHolder 사용
60-
- 유저 검증 과정에서 `UserClientService`를 호출해야함.
61-
- User Role:
62-
- `USER`: 일반 사용자 - 이벤트 조회, 티켓팅 참여
63-
- `MANAGER`: 관리자 - 이벤트 관리, 수령 확인
64-
- `ADMIN`: 최고 관리자 - 모든 권한
65-
66-
## 개발 환경 설정
67-
68-
- **도커 이미지 실행**
69-
- `./docker/codin-db/docker-compose.yml` : MySQL, Redis 실행 도커 컴포즈 스크립트
70-
71-
- **환경 변수 설정**
72-
```bash
73-
# .env.example 파일을 복사해 필요한 값들을 수정
74-
cp .env.example .env
75-
```
76-
77-
1+
# CodIN Ticketing API
2+
3+
<img width="1324" height="679" alt="image" src="https://github.com/user-attachments/assets/d3719539-a47e-4043-a57a-408570411762" />
4+
5+
인천대학교 정보기술대학 티켓팅 시스템을 위한 Spring Boot REST API
6+
7+
## 프로젝트 개요
8+
9+
**CodIN Ticketing API** 는 인천대학교 정보기술대학의 다양한 **이벤트(간식 나눔, 행사 등)** 에 대한 **티켓팅 시스템** 을 제공 서버입니다.
10+
11+
### 주요 기능
12+
13+
<img width="10046" height="4961" alt="image" src="https://github.com/user-attachments/assets/53a52f4c-e766-4a9d-80cc-f1d507c19413" />
14+
15+
[API Endpoint 문서](/API.md)
16+
17+
- **이벤트 관리**: 티켓팅 이벤트 생성, 조회, 수정, 삭제
18+
- **사용자 프로필**: 수령자 정보 관리 (학과, 학번)
19+
- **티켓팅 참여**: 실시간 티켓팅 참여 및 교환권 발급
20+
- **전자 서명**: 수령 확인을 위한 전자 서명 기능
21+
- **관리자 기능**: 이벤트 관리, 수령 확인, 통계
22+
- **엑셀 다운로드**: 참여자 정보 엑셀 내보내기
23+
24+
## 기술 스택
25+
26+
- **Backend**: Spring Boot 3.5.3, Spring Security, Spring Data JPA, SSE
27+
- **Database**: MySQL 8.0
28+
- **MQ**: Redis Stream
29+
- **Authentication**: JWT
30+
31+
## 데이터 모델
32+
33+
<img width="2094" height="1186" alt="스크린샷 2025-07-24 18 12 10" src="https://github.com/user-attachments/assets/f124eb08-ffc9-4411-afbf-b56238273c01" />
34+
35+
### 주요 엔티티
36+
37+
- **Event**: 티켓팅 이벤트 정보
38+
- **Participation**: 이벤트별 참여자 정보 및 수령 상태
39+
- **Stock**: 티켓팅 이벤트 재고 정보
40+
41+
**Enum Class**
42+
- **Campus**: 캠퍼스 구분 (송도캠퍼스, 미추홀캠퍼스)
43+
- **Department**: 학과 정보 (컴퓨터공학부, 정보통신공학과 등)
44+
45+
## 외부 API 엔드포인트
46+
47+
- CodIN Main API : https://github.com/CodIN-INU/BACKEND
48+
- `GET /api/users` : 유저 조회 엔드 포인트
49+
50+
### 티켓팅 참여자 정보 엔드포인트
51+
52+
- `GET /api/users/ticketing-participation` : 유저 티켓팅 참여자 정보 조회
53+
- `PUT /api/users/ticketing-participation` : 유저 티켓팅 참여자 정보 수정
54+
55+
## 인증 및 권한
56+
57+
- **JWT 토큰 기반 인증**
58+
- JWT 토큰에서 User Email 추출 -> `SecurityContextHolder`
59+
- SecurityUtils 클래스로 SecurityContextHolder 사용
60+
- 유저 검증 과정에서 `UserClientService`를 호출해야함.
61+
- User Role:
62+
- `USER`: 일반 사용자 - 이벤트 조회, 티켓팅 참여
63+
- `MANAGER`: 관리자 - 이벤트 관리, 수령 확인
64+
- `ADMIN`: 최고 관리자 - 모든 권한
65+
66+
## 개발 환경 설정
67+
68+
- **도커 이미지 실행**
69+
- `./docker/codin-db/docker-compose.yml` : MySQL, Redis 실행 도커 컴포즈 스크립트
70+
71+
- **환경 변수 설정**
72+
```bash
73+
# .env.example 파일을 복사해 필요한 값들을 수정
74+
cp .env.example .env
75+
```
76+
77+

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -49,23 +49,23 @@ public class EventCreateRequest {
4949
@NotNull(message = "티켓팅 이벤트 시작 시간은 필수입니다")
5050
@Future(message = "티켓팅 이벤트 시작 시간은 현재 시간보다 나중이어야 합니다")
5151
@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")
52+
@Schema(description = "티켓팅 이벤트 시작 시간", example = "2025-12-25T10:00:00")
5353
private LocalDateTime eventTime;
5454

5555
@NotNull(message = "티켓팅 이벤트 종료 시간은 필수입니다")
5656
@Future(message = "티켓팅 이벤트 종료 시간은 현재 시간보다 나중이어야 합니다")
5757
@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")
58+
@Schema(description = "티켓팅 이벤트 종료 시간", example = "2025-12-25T12:00:00")
5959
private LocalDateTime eventEndTime;
6060

6161
@NotNull(message = "티켓팅 상품 수령 시작 시간은 필수입니다")
6262
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
63-
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025-07-02T16:00")
63+
@Schema(description = "티켓팅 상품 수령 시작 시간", example = "2025-12-25T16:00:00")
6464
private LocalDateTime eventReceivedStartTime;
6565

6666
@NotNull(message = "티켓팅 상품 수령 종료 시간은 필수입니다")
6767
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss", timezone = "Asia/Seoul")
68-
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025-07-02T16:00")
68+
@Schema(description = "티켓팅 상품 수령 종료 시간", example = "2025-12-25T17:00:00")
6969
private LocalDateTime eventReceivedEndTime;
7070

7171
@Pattern(regexp = "\\d{2,3}-\\d{3,4}-\\d{4}", message = "올바른 전화번호 형식이 아닙니다")

src/main/java/inu/codin/codinticketingapi/domain/admin/service/EventAdminService.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,10 @@ public EventResponse updateEvent(Long eventId, EventUpdateRequest request, Multi
110110
@Transactional
111111
public void deleteEvent(Long eventId) {
112112
Event event = findEventById(eventId);
113+
114+
String currentUserId = findAdminUser();
115+
validationEvent(event, currentUserId);
116+
113117
event.delete();
114118
eventStatusScheduler.scheduleAllDelete(event);
115119
redisEventService.deleteTickets(eventId);
@@ -149,6 +153,11 @@ public EventParticipationProfilePageResponse getParticipationList(Long eventId,
149153

150154
@Transactional
151155
public boolean changeReceiveStatus(Long eventId, String userId, MultipartFile image) {
156+
// 권한 검증
157+
Event event = findEventById(eventId);
158+
String currentUserId = findAdminUser();
159+
validationEvent(event, currentUserId);
160+
152161
Participation findParticipation = getParticipationByEventIdAndUserId(eventId, userId);
153162
String imageURL = imageService.handleImageUpload(image);
154163

@@ -167,6 +176,11 @@ public EventStockResponse getStock(Long eventId) {
167176

168177
@Transactional
169178
public void cancelTicket(Long eventId, String userId) {
179+
// 권한 검증
180+
Event event = findEventById(eventId);
181+
String currentUserId = findAdminUser();
182+
validationEvent(event, currentUserId);
183+
170184
ticketingService.cancelParticipation(eventId, userId);
171185
}
172186

@@ -214,7 +228,12 @@ private String findAdminUser() {
214228
}
215229

216230
private void validationEvent(Event event, String userId) {
217-
if (!event.getUserId().equals(userId) && !SecurityUtil.hasRole("ADMIN")) {
231+
boolean isAdmin = SecurityUtil.hasRole("ADMIN");
232+
boolean isManager = SecurityUtil.hasRole("MANAGER");
233+
boolean isOwner = event.getUserId().equals(userId);
234+
235+
// 관리자이거나 매니저이면서 이벤트 생성자일 경우에만 수정 가능
236+
if (!(isAdmin || (isManager && isOwner))) {
218237
throw new TicketingException(TicketingErrorCode.UNAUTHORIZED_EVENT_UPDATE);
219238
}
220239

0 commit comments

Comments
 (0)