diff --git a/README.md b/README.md index bde637b5..81636d0e 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,159 @@ -# permit-server -https://www.instagram.com/permit_invites?utm_source=ig_web_button_share_sheet&igsh=ZDNlZDc0MzIxNw== +# 🎫 Permit Seoul - ν‹°μΌ“ 예맀 μ„œλ²„ + +**Permit Seoul**은 곡연/νŽ˜μŠ€ν‹°λ²Œ/행사 등을 μœ„ν•œ ν‹°μΌ“ 예맀 μ„œλΉ„μŠ€μ˜ λ°±μ—”λ“œ μ„œλ²„μž…λ‹ˆλ‹€. + +[![Instagram](https://img.shields.io/badge/Instagram-E4405F?style=flat&logo=instagram&logoColor=white)](https://www.instagram.com/permit_invites) + +--- + +## πŸ“‹ λͺ©μ°¨ + +- [기술 μŠ€νƒ](#-기술-μŠ€νƒ) +- [μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜](#-μ‹œμŠ€ν…œ-μ•„ν‚€ν…μ²˜) +- [μ£Όμš” κΈ°λŠ₯](#-μ£Όμš”-κΈ°λŠ₯) +- [ν”„λ‘œμ νŠΈ ꡬ쑰](#-ν”„λ‘œμ νŠΈ-ꡬ쑰) +- [CI/CD](#cicd) + +--- + +## πŸ›  기술 μŠ€νƒ + +### Backend +| ꡬ뢄 | 기술 | +|------|------| +| **Language** | Java 17 | +| **Framework** | Spring Boot 3.2.1 | +| **ORM** | Spring Data JPA | +| **Security** | Spring Security, JWT, OAuth2 (Google, Kakao) | +| **Database** | MySQL 8.x | +| **Cache** | Redis | +| **API Client** | Spring Cloud OpenFeign | + +### Infrastructure +| ꡬ뢄 | 기술 | +|------|------| +| **Cloud** | AWS EC2, S3 | +| **Container** | Docker, Amazon Corretto 17 | +| **CI/CD** | GitHub Actions | +| **Logging** | Loki, Logstash, Discord Webhook | +| **Resilience** | Resilience4j (Circuit Breaker) | + +### External Services +| ꡬ뢄 | 기술 | +|------|------| +| **Payment** | Toss Payments API | +| **OAuth** | Google, Kakao μ†Œμ…œ 둜그인 | +| **Notification** | Email (SMTP), Notion API | +| **QR Code** | ZXing | + +--- + +## πŸ— μ‹œμŠ€ν…œ μ•„ν‚€ν…μ²˜ + +``` +β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” +β”‚ Client │────▢│ Nginx │────▢│ Spring Boot β”‚ +β”‚ (Frontend) β”‚ β”‚ (Reverse β”‚ β”‚ Server β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ Proxy) β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚ + β”‚ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”‚ + β”‚ β”‚ β”‚ β”‚ β”‚ β”‚ +β”Œβ”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β” +β”‚ MySQL β”‚ β”‚ Redis β”‚ β”‚ AWS S3 β”‚ β”‚ Toss Payments β”‚ β”‚ Google β”‚ β”‚ Kakao β”‚ +β”‚ DB β”‚ β”‚ Cache β”‚ β”‚ (Images) β”‚ β”‚ API β”‚ β”‚ OAuth β”‚ β”‚ OAuth β”‚ +β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ +``` + +--- + +## ✨ μ£Όμš” κΈ°λŠ₯ + +### 🎟 ν‹°μΌ“ 예맀 μ‹œμŠ€ν…œ +- **μ‹€μ‹œκ°„ 재고 관리**: Redis 기반 λ™μ‹œμ„± μ œμ–΄ +- **μ˜ˆμ•½ μ„Έμ…˜ 관리**: 7λΆ„ TTL의 μ˜ˆμ•½ μ„Έμ…˜μœΌλ‘œ μ’Œμ„ μž„μ‹œ 확보 +- **μ’Œμ„/ν‹°μΌ“ μœ ν˜• 선택**: λ‹€μ–‘ν•œ ν‹°μΌ“ νƒ€μž… 및 회차 지원 + +### πŸ’³ 결제 μ‹œμŠ€ν…œ +- **Toss Payments 연동**: 결제 승인 및 μ·¨μ†Œ API +- **νŠΈλžœμž­μ…˜ 뢄리 μ•„ν‚€ν…μ²˜**: μ™ΈλΆ€ API ν˜ΈμΆœμ„ DB νŠΈλžœμž­μ…˜μ—μ„œ λΆ„λ¦¬ν•˜μ—¬ 컀λ„₯μ…˜ νš¨μœ¨μ„± ν–₯상 +- **Resilience4j Circuit Breaker**: μ™ΈλΆ€ API μž₯μ•  μ‹œ μ„œν‚· 브레이컀 적용 +- **ν™˜λΆˆ μ •μ±…**: 행사 μ‹œμž‘ 3일 μ „κΉŒμ§€ μ·¨μ†Œ κ°€λŠ₯ + +### πŸ‘€ μ‚¬μš©μž 관리 +- **OAuth2 μ†Œμ…œ 둜그인**: Google, Kakao +- **JWT 인증**: Access Token 기반 인증 + +### πŸŽͺ 이벀트/행사 관리 +- **행사 CRUD**: κ΄€λ¦¬μž μ „μš© 행사 생성/μˆ˜μ •/μ‚­μ œ +- **νƒ€μž„ν…Œμ΄λΈ” 관리**: Notion API 연동 νƒ€μž„ν…Œμ΄λΈ” +- **μ’Œμ„ λ°°μΉ˜λ„**: μ΄λ²€νŠΈλ³„ μ’Œμ„ 이미지 관리 + +### πŸ‘₯ 게슀트 관리 (Admin) +- **QR 체크인**: ZXing 기반 QR μ½”λ“œ 생성 및 μŠ€μΊ” +- **게슀트 μ΄ˆλŒ€**: 이메일 μ΄ˆλŒ€μž₯ λ°œμ†‘ +- **μž…μž₯ 관리**: μ‹€μ‹œκ°„ 체크인/체크아웃 + +### 🎁 쿠폰 μ‹œμŠ€ν…œ +- **할인 쿠폰**: μ΄λ²€νŠΈλ³„ 쿠폰 λ°œκΈ‰ 및 적용 +- **쿠폰 μ½”λ“œ 생성**: 고유 쿠폰 μ½”λ“œ μžλ™ 생성 + +--- + +## πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰 + +``` +src/main/java/com/permitseoul/permitserver/ +β”œβ”€β”€ PermitServerApplication.java +β”œβ”€β”€ domain/ # 도메인별 λͺ¨λ“ˆ +β”‚ β”œβ”€β”€ admin/ # κ΄€λ¦¬μž κΈ°λŠ₯ +β”‚ β”‚ β”œβ”€β”€ coupon/ # 쿠폰 관리 +β”‚ β”‚ β”œβ”€β”€ event/ # 행사 관리 +β”‚ β”‚ β”œβ”€β”€ guest/ # 게슀트 관리 +β”‚ β”‚ β”œβ”€β”€ ticket/ # ν‹°μΌ“ 관리 +β”‚ β”‚ └── timetable/ # νƒ€μž„ν…Œμ΄λΈ” 관리 +β”‚ β”œβ”€β”€ auth/ # 인증/인가 +β”‚ β”œβ”€β”€ coupon/ # 쿠폰 +β”‚ β”œβ”€β”€ event/ # 행사 +β”‚ β”œβ”€β”€ payment/ # 결제 +β”‚ β”œβ”€β”€ reservation/ # μ˜ˆμ•½ +β”‚ β”œβ”€β”€ reservationsession/ # μ˜ˆμ•½ μ„Έμ…˜ +β”‚ β”œβ”€β”€ ticket/ # ν‹°μΌ“ +β”‚ └── user/ # μ‚¬μš©μž +└── global/ # 곡톡 λͺ¨λ“ˆ + β”œβ”€β”€ aop/ # AOP μ„€μ • + β”œβ”€β”€ config/ # μ„€μ • 클래슀 + β”œβ”€β”€ exception/ # μ˜ˆμ™Έ 처리 + β”œβ”€β”€ external/ # μ™ΈλΆ€ API 연동 + β”œβ”€β”€ filter/ # ν•„ν„° + β”œβ”€β”€ redis/ # Redis μ„€μ • + β”œβ”€β”€ response/ # 응닡 객체 + └── util/ # μœ ν‹Έλ¦¬ν‹° +``` + +--- + +## CI/CD + +GitHub Actionsλ₯Ό ν†΅ν•œ μžλ™ 배포 νŒŒμ΄ν”„λΌμΈ: + +``` +main 브랜치 Push + ↓ + GitHub Actions + ↓ + β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” + β”‚ 1. Checkout β”‚ + β”‚ 2. Set up JDK 17 β”‚ + β”‚ 3. Build (Gradle) β”‚ + β”‚ 4. Docker Build & Push β”‚ + β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ + ↓ + Docker Hub + ↓ + EC2 SSH Deploy +``` + +### μ›Œν¬ν”Œλ‘œμš° 파일 +- `CI-PROD.yml` / `CI_DEV.yml`: CI νŒŒμ΄ν”„λΌμΈ +- `DOCKER-PROD-CD.yml` / `DOCKER-DEV-CD.yml`: CD νŒŒμ΄ν”„λΌμΈ diff --git a/src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.java b/src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.java index 4efc7ce9..fdb50187 100644 --- a/src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.java +++ b/src/main/java/com/permitseoul/permitserver/domain/admin/guestticket/api/service/AdminGuestTicketService.java @@ -18,12 +18,14 @@ import com.permitseoul.permitserver.domain.event.core.exception.EventNotfoundException; import com.permitseoul.permitserver.global.response.code.ErrorCode; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; import java.util.List; @Service @RequiredArgsConstructor +@Slf4j public class AdminGuestTicketService { private final AdminGuestRetriever adminGuestRetriever; private final GuestTicketEmailSender guestTicketEmailSender; @@ -53,6 +55,9 @@ public void issueGuestTickets(final long eventId, final List