-
Notifications
You must be signed in to change notification settings - Fork 0
Feat/#186 calendar event #190
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e591168
Merge pull request #170 from TackitOnboarding/dev
yeongsinkeem 67c3595
Merge pull request #171 from TackitOnboarding/dev
yeongsinkeem a90100d
Merge pull request #172 from TackitOnboarding/dev
yeongsinkeem b774d65
Merge pull request #173 from TackitOnboarding/dev
yeongsinkeem e62e0dd
Merge pull request #174 from TackitOnboarding/dev
yeongsinkeem 37a7d6e
Merge pull request #175 from TackitOnboarding/dev
yeongsinkeem dbbf06f
Merge pull request #177 from TackitOnboarding/dev
yeongsinkeem 004f304
Merge pull request #183 from TackitOnboarding/dev
yeongsinkeem 3e6be51
Merge remote-tracking branch 'origin/dev_temp' into feat/#186-calenda…
tishakong 92f1b5e
Fix: 초기 admin 중복 생성 오류 해결
tishakong e65880c
feat: #186- Event, EventParticipant, EventScope 모델링
tishakong 7955020
feat: #186-일정 관련 권한 확인 메서드, 일정 참가자 생성 메서드 구현
tishakong 37879c7
feat: #186-일정 생성 API 구현
tishakong fa24154
feat: #186-일정 수정 API 구현
tishakong 4d92ff7
feat: #186-특정 조직의 월간 일정 조회
tishakong 51a6e9a
feat: #186-특정 조직의 다가오는 일정 조회
tishakong 2d2bc1a
Fix: 일정 생성/수정 시 필요한 EventParticipantDto 추가
tishakong 21eca20
Fix: 일정 관련 권한 확인 메서드에 필요한 JPA 추가
tishakong 99592ab
feat: #186-특정 조직의 일정 삭제 API 구현
tishakong 66bfb0e
feat: #186-특정 조직의 일정 상세 조회 API 구현
tishakong File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| package org.example.tackit.domain.entity; | ||
|
|
||
| import jakarta.persistence.*; | ||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
| import org.example.tackit.domain.entity.Org.Organization; | ||
| import org.springframework.data.annotation.CreatedDate; | ||
| import org.springframework.data.annotation.LastModifiedDate; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @Table(name = "event") | ||
| public class Event{ | ||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "event_id") | ||
| private Long id; | ||
|
|
||
| @Column(nullable = false) | ||
| private String title; | ||
|
|
||
| @Column(nullable = false) | ||
| private LocalDateTime startsAt; | ||
|
|
||
| @Column(nullable = false) | ||
| private LocalDateTime endsAt; | ||
|
|
||
| @CreatedDate | ||
| private LocalDateTime createdAt; | ||
|
|
||
| @LastModifiedDate | ||
| private LocalDateTime updatedAt; | ||
|
|
||
| @Column(columnDefinition = "TEXT") | ||
| private String description; | ||
|
|
||
| @Column(name = "color_chip", nullable = false) | ||
| private String colorChip; | ||
|
|
||
| @Enumerated(EnumType.STRING) | ||
| @Column(name = "event_scope", nullable = false) | ||
| private EventScope eventScope; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "org_id", nullable = false) | ||
| private Organization organization; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "member_id", nullable = false) | ||
| private Member creator; | ||
|
|
||
| @OneToMany(mappedBy = "event", cascade = CascadeType.ALL, orphanRemoval = true) | ||
| private List<EventParticipant> participants = new ArrayList<>(); | ||
|
|
||
| @Builder | ||
| public Event(String title, LocalDateTime startsAt, LocalDateTime endsAt, LocalDateTime createdAt, LocalDateTime updatedAt, | ||
| String description, String colorChip, EventScope eventScope, | ||
| Organization organization, Member creator) { | ||
| this.title = title; | ||
| this.startsAt = startsAt; | ||
| this.endsAt = endsAt; | ||
| this.createdAt = createdAt; | ||
| this.updatedAt = updatedAt; | ||
| this.description = description; | ||
| this.colorChip = colorChip; | ||
| this.eventScope = eventScope; | ||
| this.organization = organization; | ||
| this.creator = creator; | ||
| } | ||
|
|
||
| public void update(String title, LocalDateTime startsAt, LocalDateTime endsAt, | ||
| String description, String colorChip, EventScope eventScope) { | ||
| if (title != null) this.title = title; | ||
| if (startsAt != null) this.startsAt = startsAt; | ||
| if (endsAt != null) this.endsAt = endsAt; | ||
| if (description != null) this.description = description; | ||
| if (colorChip != null) this.colorChip = colorChip; | ||
| if (eventScope != null) this.eventScope = eventScope; | ||
| } | ||
|
|
||
| // 참여자 목록 초기화 (참여자 수정 시 사용) | ||
| public void clearParticipants() { | ||
| this.participants.clear(); | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
src/main/java/org/example/tackit/domain/entity/EventParticipant.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package org.example.tackit.domain.entity; | ||
|
|
||
| import jakarta.persistence.*; | ||
| import lombok.AccessLevel; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
| import org.example.tackit.domain.entity.Org.MemberOrg; | ||
| import org.springframework.data.annotation.CreatedDate; | ||
| import org.springframework.data.jpa.domain.support.AuditingEntityListener; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @Entity | ||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @EntityListeners(AuditingEntityListener.class) | ||
| @Table(name = "event_participant") | ||
| public class EventParticipant { | ||
|
|
||
| @Id | ||
| @GeneratedValue(strategy = GenerationType.IDENTITY) | ||
| @Column(name = "event_participant_id") | ||
| private Long id; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "event_id", nullable = false) | ||
| private Event event; | ||
|
|
||
| @ManyToOne(fetch = FetchType.LAZY) | ||
| @JoinColumn(name = "member_org_id", nullable = false) | ||
| private MemberOrg memberOrg; | ||
|
|
||
| @CreatedDate | ||
| private LocalDateTime createdAt; | ||
|
|
||
| @Builder | ||
| public EventParticipant(Event event, MemberOrg memberOrg) { | ||
| this.event = event; | ||
| this.memberOrg = memberOrg; | ||
| } | ||
|
|
||
| // 연관관계 편의 메서드 | ||
| public void assignEvent(Event event) { | ||
| this.event = event; | ||
| event.getParticipants().add(this); | ||
| } | ||
| } |
6 changes: 6 additions & 0 deletions
6
src/main/java/org/example/tackit/domain/entity/EventScope.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| package org.example.tackit.domain.entity; | ||
|
|
||
| public enum EventScope { | ||
| PARTIAL, | ||
| ALL | ||
| } |
125 changes: 125 additions & 0 deletions
125
src/main/java/org/example/tackit/domain/event/controller/EventController.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| package org.example.tackit.domain.event.controller; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
| import org.example.tackit.domain.auth.login.security.CustomUserDetails; | ||
| import org.example.tackit.domain.event.dto.EventCreateReqDto; | ||
| import org.example.tackit.domain.event.dto.EventDetailResDto; | ||
| import org.example.tackit.domain.event.dto.EventSimpleResDto; | ||
| import org.example.tackit.domain.event.dto.EventUpdateReqDto; | ||
| import org.example.tackit.domain.event.service.EventService; | ||
| import org.springframework.http.HttpStatus; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| @RestController | ||
| @RequiredArgsConstructor | ||
| @RequestMapping("/api/events") | ||
| public class EventController { | ||
|
|
||
| private final EventService eventService; | ||
|
|
||
| /** | ||
| * 일정 생성 | ||
| */ | ||
| @PostMapping | ||
| public ResponseEntity<?> createEvent( | ||
| @RequestBody EventCreateReqDto reqDto, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| // TODO 인증 정보가 없습니다 코드 공통 로직 처리 | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| Long eventId = eventService.createEvent(reqDto, userDetails.getId()); | ||
|
|
||
| // TODO ResponseEntity 커스텀 공통 양식 추가 | ||
| return ResponseEntity.status(HttpStatus.CREATED).body(eventId); | ||
| } | ||
|
|
||
| /** | ||
| * 일정 수정 | ||
| */ | ||
| @PatchMapping("/{eventId}") | ||
| public ResponseEntity<?> updateEvent( | ||
| @PathVariable Long eventId, | ||
| @RequestBody EventUpdateReqDto reqDto, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| eventService.updateEvent(eventId, reqDto, userDetails.getId()); | ||
| return ResponseEntity.status(HttpStatus.OK).body(eventId); | ||
| } | ||
|
|
||
| /** | ||
| * 월간 일정 조회 | ||
| */ | ||
| @GetMapping("/monthly") | ||
| public ResponseEntity<?> getMonthlyEvents( | ||
| @RequestParam Long orgId, | ||
| @RequestParam int year, | ||
| @RequestParam int month, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| List<EventSimpleResDto> events = eventService.getMonthlyEvents(orgId, year, month, userDetails.getId()); | ||
| return ResponseEntity.status(HttpStatus.OK).body(events); | ||
| } | ||
|
|
||
| /** | ||
| * 다가오는 일정(사이드바) 조회 | ||
| */ | ||
| @GetMapping("/upcoming") | ||
| public ResponseEntity<?> getUpcomingEvents( | ||
| @RequestParam Long orgId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| List<EventSimpleResDto> events = eventService.getUpcomingEvents(orgId, userDetails.getId()); | ||
| return ResponseEntity.status(HttpStatus.OK).body(events); | ||
| } | ||
|
|
||
| /** | ||
| * 일정 상세 조회 | ||
| */ | ||
| @GetMapping("/{eventId:[0-9]+}") | ||
| public ResponseEntity<?> getEventDetail( | ||
| @PathVariable Long eventId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| EventDetailResDto detail = eventService.getEventDetail(eventId, userDetails.getId()); | ||
| return ResponseEntity.status(HttpStatus.OK).body(detail); | ||
| } | ||
|
|
||
| /** | ||
| * 일정 삭제 | ||
| */ | ||
| @DeleteMapping("/{eventId}") | ||
| public ResponseEntity<?> deleteEvent( | ||
| @PathVariable Long eventId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails | ||
| ) { | ||
| if (userDetails == null) { | ||
| return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body("인증 정보가 없습니다."); | ||
| } | ||
|
|
||
| eventService.deleteEvent(eventId, userDetails.getId()); | ||
| return ResponseEntity.status(HttpStatus.NO_CONTENT).body(null); | ||
| } | ||
| } | ||
39 changes: 39 additions & 0 deletions
39
src/main/java/org/example/tackit/domain/event/dto/EventCreateReqDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package org.example.tackit.domain.event.dto; | ||
|
|
||
| import jakarta.validation.constraints.*; | ||
| import lombok.*; | ||
| import org.example.tackit.domain.entity.EventScope; | ||
| import org.example.tackit.domain.entity.Org.OrgType; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.List; | ||
|
|
||
| @Getter | ||
| @NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
| @AllArgsConstructor | ||
| @Builder | ||
| public class EventCreateReqDto { | ||
|
|
||
| @NotNull(message = "조직 ID는 필수입니다.") | ||
| private Long orgId; | ||
|
|
||
| @NotBlank(message = "일정 제목은 필수입니다.") | ||
| private String title; | ||
|
|
||
| @NotNull(message = "시작 시간은 필수입니다.") | ||
| private LocalDateTime startsAt; | ||
|
|
||
| @NotNull(message = "종료 시간은 필수입니다.") | ||
| private LocalDateTime endsAt; | ||
|
|
||
| private String description; | ||
|
|
||
| @NotNull(message = "참여자 범위(eventScope)는 필수입니다.") | ||
| private EventScope eventScope; | ||
|
|
||
| @NotNull(message = "참여자 목록 필드는 필수입니다.") | ||
| private List<Long> participants; | ||
|
|
||
| @NotBlank(message = "색상 코드는 필수입니다.") | ||
| private String colorChip; | ||
| } |
22 changes: 22 additions & 0 deletions
22
src/main/java/org/example/tackit/domain/event/dto/EventDetailResDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| package org.example.tackit.domain.event.dto; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import lombok.*; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.List; | ||
|
|
||
| @Getter | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| @Builder | ||
| public class EventDetailResDto { | ||
|
|
||
| private Long eventId; | ||
| private String title; | ||
| private LocalDateTime startsAt; | ||
| private LocalDateTime endsAt; | ||
| private String description; | ||
| private String colorChip; | ||
| private List<EventParticipantDto> participants; | ||
| } |
14 changes: 14 additions & 0 deletions
14
src/main/java/org/example/tackit/domain/event/dto/EventParticipantDto.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package org.example.tackit.domain.event.dto; | ||
|
|
||
| import com.fasterxml.jackson.annotation.JsonProperty; | ||
| import lombok.*; | ||
|
|
||
| @Getter | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| @Builder | ||
| public class EventParticipantDto { | ||
| private Long orgMemberId; | ||
| private String profileImageUrl; | ||
| private String nickname; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
모든 컨트롤러 메소드에서
userDetails == null체크가 반복되고 있습니다. Spring Security의@PreAuthorize("isAuthenticated()")어노테이션을 사용하거나, 인터셉터 또는 필터를 활용하여 인증 여부를 공통으로 처리하면 코드 중복을 줄이고 컨트롤러 로직을 더 깔끔하게 유지할 수 있습니다. 예를 들어, 클래스 레벨에@PreAuthorize("isAuthenticated()")를 추가하고 SecurityConfig에서 활성화할 수 있습니다.