Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -33,47 +33,67 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'org.springframework.boot:spring-boot-starter-test'

// DB
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.mysql:mysql-connector-j'

// Query DSL
implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta'
annotationProcessor "com.querydsl:querydsl-apt:${dependencyManagement.importedProperties['querydsl.version']}:jakarta"
annotationProcessor "jakarta.annotation:jakarta.annotation-api"
annotationProcessor "jakarta.persistence:jakarta.persistence-api"

// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'

// Redisson
implementation 'org.redisson:redisson-spring-boot-starter:3.46.0'

// Security
implementation 'org.springframework.boot:spring-boot-starter-security'
testImplementation 'org.springframework.security:spring-security-test'

// Lombok
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

// OAuth 2.0
implementation 'org.springframework.boot:spring-boot-starter-oauth2-client'

// JWT
implementation 'io.jsonwebtoken:jjwt-api:0.12.2'
implementation 'io.jsonwebtoken:jjwt-impl:0.12.2'
implementation 'io.jsonwebtoken:jjwt-jackson:0.12.2'

// REST Docs & Swagger
testImplementation 'com.epages:restdocs-api-spec-mockmvc:0.19.2'
testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc:3.0.0'
testImplementation 'com.squareup.okhttp3:mockwebserver'
implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.1.0'

// AWS S3
implementation 'io.awspring.cloud:spring-cloud-aws-starter:3.1.1'
implementation 'software.amazon.awssdk:s3:2.25.30'

// org.json
implementation 'org.json:json:20231013'

// Zxing
implementation 'com.google.zxing:core:3.5.1'
implementation 'com.google.zxing:javase:3.5.1'

// Health-Check
//implementation 'org.springframework.boot:spring-boot-starter-actuator'

// Logback
implementation 'net.logstash.logback:logstash-logback-encoder:8.1'

// Feign
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.4'

// Jsoup
implementation 'org.jsoup:jsoup:1.21.2'
}

// QueryDSL 디렉토리
Expand Down
39 changes: 31 additions & 8 deletions src/main/java/side/onetime/controller/EventController.java
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
package side.onetime.controller;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import side.onetime.dto.event.request.CreateEventRequest;
import side.onetime.dto.event.request.ModifyUserCreatedEventRequest;
import side.onetime.dto.event.request.ModifyEventRequest;
import side.onetime.dto.event.response.*;
import side.onetime.dto.schedule.request.GetFilteredSchedulesRequest;
import side.onetime.global.common.ApiResponse;
import side.onetime.global.common.status.SuccessStatus;
import side.onetime.service.EventService;

import java.time.LocalDateTime;
import java.util.List;

@RestController
Expand Down Expand Up @@ -130,6 +132,27 @@ public ResponseEntity<ApiResponse<List<GetUserParticipatedEventsResponse>>> getU
return ApiResponse.onSuccess(SuccessStatus._GET_USER_PARTICIPATED_EVENTS, getUserParticipatedEventsResponses);
}

/**
* 유저 참여 이벤트 목록 조회 API.
*
* 이 API는 인증된 유저가 참여한 이벤트 목록을 페이지 단위로 조회합니다. 유저의 참여 상태, 이벤트 정보 등이 포함됩니다.
*
* 커서 기반의 페이징을 지원하며, createdDate 커서를 기준으로 이전에 생성된 이벤트를 조회합니다.
* createdDate를 전달하지 않으면 가장 최신 이벤트부터 조회합니다.
*
* @param size 한 번에 가져올 이벤트 개수
* @param createdDate 마지막으로 조회한 이벤트 생성일
* @return 유저가 참여한 이벤트 목록 및 페이지(커서) 정보가 포함된 응답 DTO
*/
@GetMapping("/user/all/v2")
public ResponseEntity<ApiResponse<GetParticipatedEventsResponse>> getParticipatedEventsByCursor(
@RequestParam(value = "size", defaultValue = "2") @Min(1) int size,
@RequestParam(value = "cursor", required = false) LocalDateTime createdDate
) {
GetParticipatedEventsResponse response = eventService.getParticipatedEventsByCursor(size, createdDate);
return ApiResponse.onSuccess(SuccessStatus._GET_PARTICIPATED_EVENTS, response);
}

/**
* 유저가 생성한 이벤트 삭제 API.
*
Expand All @@ -147,9 +170,9 @@ public ResponseEntity<ApiResponse<SuccessStatus>> removeUserCreatedEvent(
}

/**
* 유저가 생성한 이벤트 수정 API.
* 이벤트 수정 API.
*
* 이 API는 인증된 유저가 생성한 특정 이벤트의 제목, 시간, 설문 범위를 수정합니다.
* 이 API는 특정 이벤트의 제목, 시간, 설문 범위를 수정합니다.
* 수정 가능한 항목은 다음과 같습니다:
* - 이벤트 제목
* - 시작 시간 및 종료 시간
Expand All @@ -158,16 +181,16 @@ public ResponseEntity<ApiResponse<SuccessStatus>> removeUserCreatedEvent(
* 요청 데이터에 따라 변경 사항을 반영하며, 필요에 따라 기존 스케줄 데이터를 삭제하거나 새로운 스케줄을 생성합니다.
*
* @param eventId 수정할 이벤트의 ID
* @param modifyUserCreatedEventRequest 새로운 이벤트 정보가 담긴 요청 데이터 (제목, 시간, 범위 등)
* @param modifyEventRequest 새로운 이벤트 정보가 담긴 요청 데이터 (제목, 시간, 범위 등)
* @return 수정 성공 여부
*/
@PatchMapping("/{event_id}")
public ResponseEntity<ApiResponse<SuccessStatus>> modifyUserCreatedEvent(
public ResponseEntity<ApiResponse<SuccessStatus>> modifyEvent(
@PathVariable("event_id") String eventId,
@Valid @RequestBody ModifyUserCreatedEventRequest modifyUserCreatedEventRequest) {
@Valid @RequestBody ModifyEventRequest modifyEventRequest) {

eventService.modifyUserCreatedEvent(eventId, modifyUserCreatedEventRequest);
return ApiResponse.onSuccess(SuccessStatus._MODIFY_USER_CREATED_EVENT);
eventService.modifyEvent(eventId, modifyEventRequest);
return ApiResponse.onSuccess(SuccessStatus._MODIFY_EVENT);
}

/**
Expand Down
31 changes: 29 additions & 2 deletions src/main/java/side/onetime/controller/FixedController.java
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
package side.onetime.controller;

import org.springframework.http.ResponseEntity;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import jakarta.validation.Valid;
import jakarta.validation.constraints.Pattern;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import side.onetime.dto.fixed.request.UpdateFixedScheduleRequest;
import side.onetime.dto.fixed.response.GetFixedScheduleResponse;
import side.onetime.global.common.ApiResponse;
Expand All @@ -13,6 +21,7 @@
@RestController
@RequestMapping("/api/v1/fixed-schedules")
@RequiredArgsConstructor
@Validated
public class FixedController {

private final FixedScheduleService fixedScheduleService;
Expand Down Expand Up @@ -46,4 +55,22 @@ public ResponseEntity<ApiResponse<SuccessStatus>> updateUserFixedSchedules(
fixedScheduleService.updateUserFixedSchedules(request);
return ApiResponse.onSuccess(SuccessStatus._UPDATE_USER_FIXED_SCHEDULE);
}

/**
* 에브리타임 시간표 조회 API.
*
* 유저의 에브리타임 시간표를 조회한 후 파싱하여, 고정 스케줄 형태로 반환합니다.
*
* @param identifier 파싱할 에브리타임 시간표 URL 식별자
* @return 성공 상태 응답 객체
*/
@GetMapping("/everytime/{identifier}")
public ResponseEntity<ApiResponse<GetFixedScheduleResponse>> getUserEverytimeTimetable(
@PathVariable
@Pattern(regexp = "^[a-zA-Z0-9]{20}$", message = "식별자는 20자리의 영문 대소문자 및 숫자로만 구성되어야 합니다.")
String identifier
) {
GetFixedScheduleResponse response = fixedScheduleService.getUserEverytimeTimetable(identifier);
return ApiResponse.onSuccess(SuccessStatus._GET_USER_EVERYTIME_TIMETABLE, response);
}
}
14 changes: 14 additions & 0 deletions src/main/java/side/onetime/domain/Event.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
import side.onetime.domain.enums.Category;
import side.onetime.domain.enums.Status;
import side.onetime.global.common.dao.BaseEntity;

import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "events")
@SQLDelete(sql = "UPDATE events SET status = 'DELETED', deleted_at = CURRENT_TIMESTAMP WHERE events_id = ?")
@SQLRestriction("status = 'ACTIVE'")
public class Event extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand Down Expand Up @@ -49,13 +55,21 @@ public class Event extends BaseEntity {
@OneToMany(mappedBy = "event",cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<EventParticipation> eventParticipations;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private Status status;

@Column(name = "deleted_at", nullable = true)
private LocalDateTime deletedAt;

@Builder
public Event(UUID eventId, String title, String startTime, String endTime, Category category) {
this.eventId = eventId;
this.title = title;
this.startTime = startTime;
this.endTime = endTime;
this.category = category;
this.status = Status.ACTIVE;
}

public void updateTitle(String title) {
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/side/onetime/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,21 @@
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLRestriction;
import side.onetime.domain.enums.Language;
import side.onetime.domain.enums.Status;
import side.onetime.global.common.dao.BaseEntity;

import java.time.LocalDateTime;
import java.util.List;

@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Getter
@Table(name = "users")
@SQLDelete(sql = "UPDATE users SET status = 'DELETED', deleted_at = CURRENT_TIMESTAMP WHERE users_id = ?")
@SQLRestriction("status = 'ACTIVE'")
public class User extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
Expand All @@ -32,7 +38,7 @@ public class User extends BaseEntity {
@Column(name = "provider", nullable = false, length = 50)
private String provider;

@Column(name = "provider_id", nullable = false, length = 50, unique = true)
@Column(name = "provider_id", length = 50, unique = true)
private String providerId;

@Column(name = "service_policy_agreement")
Expand Down Expand Up @@ -63,6 +69,13 @@ public class User extends BaseEntity {
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<FixedSelection> fixedSelections;

@Enumerated(EnumType.STRING)
@Column(name = "status", nullable = false)
private Status status;

@Column(name = "deleted_at", nullable = true)
private LocalDateTime deletedAt;

@Builder
public User(String name, String email, String nickname, String provider, String providerId, Boolean servicePolicyAgreement, Boolean privacyPolicyAgreement, Boolean marketingPolicyAgreement, String sleepStartTime, String sleepEndTime, Language language) {
this.name = name;
Expand All @@ -76,6 +89,7 @@ public User(String name, String email, String nickname, String provider, String
this.sleepStartTime = sleepStartTime;
this.sleepEndTime = sleepEndTime;
this.language = language;
this.status = Status.ACTIVE;
}

public void updateNickName(String nickname) {
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/side/onetime/domain/enums/Status.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package side.onetime.domain.enums;

public enum Status {
ACTIVE, // 활성 상태의 엔티티
DELETED, // 삭제된 엔티티
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record ModifyUserCreatedEventRequest(
public record ModifyEventRequest(
@NotBlank(message = "제목은 필수 값입니다.") String title,
@NotBlank(message = "시작 시간은 필수 값입니다.") String startTime,
@NotBlank(message = "종료 시간은 필수 값입니다.") String endTime,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package side.onetime.dto.event.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import side.onetime.domain.Event;
import side.onetime.domain.EventParticipation;
import side.onetime.domain.enums.Category;
import side.onetime.domain.enums.EventStatus;

import java.util.List;
import java.util.UUID;

@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record GetParticipatedEventResponse(
UUID eventId,
Category category,
String title,
String createdDate,
int participantCount,
EventStatus eventStatus,
List<GetMostPossibleTime> mostPossibleTimes
) {
public static GetParticipatedEventResponse of(Event event, EventParticipation eventParticipation, int participantCount, List<GetMostPossibleTime> mostPossibleTimes) {
return new GetParticipatedEventResponse(
event.getEventId(),
event.getCategory(),
event.getTitle(),
String.valueOf(event.getCreatedDate()),
participantCount,
eventParticipation.getEventStatus(),
mostPossibleTimes
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package side.onetime.dto.event.response;

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

import java.util.List;

@JsonNaming(value = PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonInclude(JsonInclude.Include.NON_NULL)
public record GetParticipatedEventsResponse(
List<GetParticipatedEventResponse> events,
PageCursorInfo<String> pageCursorInfo
) {
public static GetParticipatedEventsResponse of(List<GetParticipatedEventResponse> events, PageCursorInfo<String> pageCursorInfo) {
return new GetParticipatedEventsResponse(events, pageCursorInfo);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package side.onetime.dto.event.response;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.annotation.JsonNaming;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
public record PageCursorInfo<T>(
T nextCursor,
boolean hasNext
) {
public static <T> PageCursorInfo<T> of(T nextCursor, boolean hasNext) {
return new PageCursorInfo<>(nextCursor, hasNext);
}
}
Loading
Loading