Skip to content

Commit ac828ae

Browse files
committed
feat(api): add calendar event services and relationship-based event creation logic
1 parent 7ad3b4b commit ac828ae

29 files changed

+987
-2
lines changed

http/calendar/calendar.http

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
### Login first
2+
POST {{memory_api}}/api/v1/member/login
3+
Content-Type: application/json
4+
5+
{
6+
"email": "[email protected]",
7+
"password": "1234"
8+
}
9+
10+
> {%
11+
client.global.set("access_token", response.body.data.accessToken);
12+
%}
13+
14+
### Create a new personal event
15+
POST {{memory_api}}/api/v1/calendar/events
16+
Content-Type: application/json
17+
Authorization: Bearer {{access_token}}
18+
19+
{
20+
"title": "개인 일정",
21+
"description": "중요한 미팅",
22+
"startDateTime": "2023-12-01T10:00:00",
23+
"endDateTime": "2023-12-01T11:00:00",
24+
"location": "회사 회의실",
25+
"eventType": "PERSONAL",
26+
"repeatType": "NONE",
27+
"repeatInterval": null,
28+
"repeatEndDate": null
29+
}
30+
31+
### Create a new personal event with repeat settings
32+
POST {{memory_api}}/api/v1/calendar/events
33+
Content-Type: application/json
34+
Authorization: Bearer {{access_token}}
35+
36+
{
37+
"title": "주간 미팅",
38+
"description": "팀 주간 미팅",
39+
"startDateTime": "2023-12-04T14:00:00",
40+
"endDateTime": "2023-12-04T15:00:00",
41+
"location": "회사 회의실",
42+
"eventType": "PERSONAL",
43+
"repeatType": "WEEKLY",
44+
"repeatInterval": 1,
45+
"repeatEndDate": "2024-03-31"
46+
}
47+
48+
### Create a new relationship event
49+
POST {{memory_api}}/api/v1/calendar/events
50+
Content-Type: application/json
51+
Authorization: Bearer {{access_token}}
52+
53+
{
54+
"title": "데이트",
55+
"description": "영화 관람",
56+
"startDateTime": "2023-12-02T18:00:00",
57+
"endDateTime": "2023-12-02T22:00:00",
58+
"location": "CGV 강남",
59+
"eventType": "RELATIONSHIP_EVENT"
60+
}
61+
62+
### Create a new anniversary event
63+
POST {{memory_api}}/api/v1/calendar/events
64+
Content-Type: application/json
65+
Authorization: Bearer {{access_token}}
66+
67+
{
68+
"title": "100일 기념일",
69+
"description": "만난지 100일",
70+
"startDateTime": "2023-12-25T00:00:00",
71+
"endDateTime": "2023-12-25T23:59:59",
72+
"location": "레스토랑",
73+
"eventType": "ANNIVERSARY_EVENT",
74+
"relationshipId": 1
75+
}

http/relationship/relationship.http

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ POST {{memory_api}}/api/v1/member/login
33
Content-Type: application/json
44

55
{
6-
"email": "tnswh2023@naver.com",
6+
"email": "rkdud449@naver.com",
77
"password": "1234"
88
}
99

@@ -17,7 +17,7 @@ Content-Type: application/json
1717
Authorization: Bearer {{access_token}}
1818

1919
{
20-
"relatedMemberId": 3
20+
"relatedMemberId": 2
2121
}
2222

2323
### accept relationship request (requires JWT token)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package com.memory.controller.calendar;
2+
3+
import com.memory.annotation.Auth;
4+
import com.memory.annotation.MemberId;
5+
import com.memory.annotation.swagger.ApiOperations;
6+
import com.memory.dto.calendar.CalendarEventRequest;
7+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
8+
import com.memory.response.ServerResponse;
9+
import com.memory.service.calendar.CalendarEventService;
10+
import io.swagger.v3.oas.annotations.Parameter;
11+
import io.swagger.v3.oas.annotations.tags.Tag;
12+
import jakarta.validation.Valid;
13+
import lombok.RequiredArgsConstructor;
14+
import org.springframework.web.bind.annotation.PostMapping;
15+
import org.springframework.web.bind.annotation.RequestBody;
16+
import org.springframework.web.bind.annotation.RestController;
17+
18+
@RestController
19+
@RequiredArgsConstructor
20+
@Tag(name = "Calendar", description = "Calendar API")
21+
public class CalendarEventController {
22+
23+
private final CalendarEventService calendarEventService;
24+
25+
@ApiOperations.SecuredApi(
26+
summary = "일정 생성",
27+
description = "새로운 일정을 생성합니다. 이벤트 타입에 따라 개인 일정, 관계 일정, 기념일을 생성할 수 있습니다.",
28+
response = BaseCalendarEventResponse.class
29+
)
30+
@Auth
31+
@PostMapping("api/v1/calendar/events")
32+
public ServerResponse<BaseCalendarEventResponse> createCalendarEvent(
33+
@Parameter(hidden = true) @MemberId Long memberId,
34+
@RequestBody @Valid CalendarEventRequest.Create request) {
35+
return ServerResponse.success(calendarEventService.createCalendarEvent(memberId, request));
36+
}
37+
38+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package com.memory.service.calendar;
2+
3+
import com.memory.domain.calendar.AnniversaryEvent;
4+
import com.memory.domain.calendar.repository.AnniversaryEventRepository;
5+
import com.memory.domain.member.Member;
6+
import com.memory.domain.member.repository.MemberRepository;
7+
import com.memory.domain.relationship.Relationship;
8+
import com.memory.domain.relationship.RelationshipStatus;
9+
import com.memory.domain.relationship.repository.RelationshipRepository;
10+
import com.memory.dto.calendar.CalendarEventRequest;
11+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
12+
import com.memory.exception.customException.NotFoundException;
13+
import com.memory.service.calendar.factory.CalendarEventFactoryService;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.stereotype.Service;
16+
import org.springframework.transaction.annotation.Transactional;
17+
18+
import java.util.List;
19+
20+
import static com.memory.service.calendar.CalendarEventServiceUtils.validateRelationshipMember;
21+
22+
@Service
23+
@RequiredArgsConstructor
24+
public class AnniversaryEventService implements CalendarEventFactoryService {
25+
26+
private final MemberRepository memberRepository;
27+
private final RelationshipRepository relationshipRepository;
28+
private final AnniversaryEventRepository anniversaryEventRepository;
29+
30+
@Override
31+
@Transactional
32+
public BaseCalendarEventResponse createCalendarEvent(Long memberId, CalendarEventRequest.Create request) {
33+
Member member = memberRepository.findMemberById(memberId)
34+
.orElseThrow(() -> new NotFoundException("회원을 찾을 수 없습니다."));
35+
36+
List<Relationship> relationshipList = relationshipRepository.findByMemberOrRelatedMemberAndStatus(member, RelationshipStatus.ACCEPTED);
37+
if (relationshipList.isEmpty()) {
38+
throw new NotFoundException("회원의 관계가 존재하지 않습니다.");
39+
}
40+
41+
// 모든 관계에 대해 기념일 이벤트 생성
42+
List<AnniversaryEvent> savedEventList = relationshipList.stream()
43+
.map(relationship -> {
44+
validateRelationshipMember(relationship.getMember(), relationship);
45+
AnniversaryEvent anniversaryEvent = request.toAnniversaryEvent(relationship.getMember(), relationship);
46+
return anniversaryEventRepository.save(anniversaryEvent);
47+
})
48+
.toList();
49+
50+
return BaseCalendarEventResponse.from(savedEventList.get(0));
51+
}
52+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.memory.service.calendar;
2+
3+
import com.memory.dto.calendar.CalendarEventRequest;
4+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
5+
import com.memory.service.calendar.factory.CalendarEventFactory;
6+
import com.memory.service.calendar.factory.CalendarEventFactoryService;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.stereotype.Service;
9+
import org.springframework.transaction.annotation.Transactional;
10+
11+
@Service
12+
@RequiredArgsConstructor
13+
public class CalendarEventService {
14+
15+
private final CalendarEventFactory calendarEventFactory;
16+
17+
/**
18+
* 통합 일정 생성 API
19+
* 이벤트 타입에 따라 적절한 일정을 생성합니다.
20+
*/
21+
@Transactional
22+
public BaseCalendarEventResponse createCalendarEvent(Long memberId, CalendarEventRequest.Create request) {
23+
CalendarEventFactoryService calendarEventService = calendarEventFactory.getCalendarEventService(request.getEventType());
24+
return calendarEventService.createCalendarEvent(memberId, request);
25+
}
26+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package com.memory.service.calendar;
2+
3+
import com.memory.domain.member.Member;
4+
import com.memory.domain.relationship.Relationship;
5+
import com.memory.exception.customException.ValidationException;
6+
7+
public class CalendarEventServiceUtils {
8+
9+
public static void validateRelationshipMember(Member member, Relationship relationship) {
10+
if (!relationship.getMember().getId().equals(member.getId()) &&
11+
!relationship.getRelatedMember().getId().equals(member.getId())) {
12+
throw new ValidationException("해당 관계에 속한 회원이 아닙니다.");
13+
}
14+
}
15+
16+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.memory.service.calendar;
2+
3+
import com.memory.domain.calendar.repository.PersonalEventRepository;
4+
import com.memory.domain.member.repository.MemberRepository;
5+
import com.memory.dto.calendar.CalendarEventRequest;
6+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
7+
import com.memory.exception.customException.NotFoundException;
8+
import com.memory.service.calendar.factory.CalendarEventFactoryService;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Service;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
public class PersonalEventService implements CalendarEventFactoryService {
15+
16+
private final MemberRepository memberRepository;
17+
private final PersonalEventRepository personalEventRepository;
18+
19+
@Override
20+
public BaseCalendarEventResponse createCalendarEvent(Long memberId, CalendarEventRequest.Create request) {
21+
var member = memberRepository.findMemberById(memberId)
22+
.orElseThrow(() -> new NotFoundException("회원을 찾을 수 없습니다."));
23+
24+
var personalEvent = request.toPersonalEvent(member);
25+
var savedEvent = personalEventRepository.save(personalEvent);
26+
27+
return BaseCalendarEventResponse.from(savedEvent);
28+
}
29+
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package com.memory.service.calendar;
2+
3+
import com.memory.domain.calendar.RelationshipEvent;
4+
import com.memory.domain.calendar.repository.RelationshipEventRepository;
5+
import com.memory.domain.member.Member;
6+
import com.memory.domain.member.repository.MemberRepository;
7+
import com.memory.domain.relationship.Relationship;
8+
import com.memory.domain.relationship.RelationshipStatus;
9+
import com.memory.domain.relationship.repository.RelationshipRepository;
10+
import com.memory.dto.calendar.CalendarEventRequest;
11+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
12+
import com.memory.exception.customException.NotFoundException;
13+
import com.memory.service.calendar.factory.CalendarEventFactoryService;
14+
import lombok.RequiredArgsConstructor;
15+
import org.springframework.stereotype.Service;
16+
17+
import java.util.List;
18+
19+
import static com.memory.service.calendar.CalendarEventServiceUtils.validateRelationshipMember;
20+
21+
@Service
22+
@RequiredArgsConstructor
23+
public class RelationshipEventService implements CalendarEventFactoryService {
24+
25+
private final MemberRepository memberRepository;
26+
private final RelationshipRepository relationshipRepository;
27+
private final RelationshipEventRepository relationshipEventRepository;
28+
29+
@Override
30+
public BaseCalendarEventResponse createCalendarEvent(Long memberId, CalendarEventRequest.Create request) {
31+
Member member = memberRepository.findMemberById(memberId)
32+
.orElseThrow(() -> new NotFoundException("회원을 찾을 수 없습니다."));
33+
34+
List<Relationship> relationshipList = relationshipRepository.findByMemberOrRelatedMemberAndStatus(member, RelationshipStatus.ACCEPTED);
35+
if (relationshipList.isEmpty()) {
36+
throw new NotFoundException("회원의 관계가 존재하지 않습니다.");
37+
}
38+
39+
// 모든 관계에 대해 기념일 이벤트 생성
40+
List<RelationshipEvent> savedEventList = relationshipList.stream()
41+
.map(relationship -> {
42+
validateRelationshipMember(relationship.getMember(), relationship);
43+
RelationshipEvent anniversaryEvent = request.toRelationshipEvent(relationship.getMember(), relationship);
44+
return relationshipEventRepository.save(anniversaryEvent);
45+
})
46+
.toList();
47+
48+
return BaseCalendarEventResponse.from(savedEventList.get(0));
49+
}
50+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package com.memory.service.calendar.factory;
2+
3+
import com.memory.domain.calendar.*;
4+
import com.memory.exception.customException.ValidationException;
5+
import com.memory.service.calendar.AnniversaryEventService;
6+
import com.memory.service.calendar.PersonalEventService;
7+
import com.memory.service.calendar.RelationshipEventService;
8+
import jakarta.annotation.PostConstruct;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Component;
11+
12+
import java.util.HashMap;
13+
import java.util.Map;
14+
15+
@Component
16+
@RequiredArgsConstructor
17+
public class CalendarEventFactory {
18+
19+
private final Map<CalendarEventType, CalendarEventFactoryService> calendarEventServiceMap = new HashMap<>();
20+
21+
private final PersonalEventService personalEventService;
22+
private final RelationshipEventService relationshipEventService;
23+
private final AnniversaryEventService anniversaryEventService;
24+
25+
@PostConstruct
26+
public void init() {
27+
calendarEventServiceMap.put(CalendarEventType.PERSONAL, personalEventService);
28+
calendarEventServiceMap.put(CalendarEventType.RELATIONSHIP_EVENT, relationshipEventService);
29+
calendarEventServiceMap.put(CalendarEventType.ANNIVERSARY_EVENT, anniversaryEventService);
30+
}
31+
32+
public CalendarEventFactoryService getCalendarEventService(CalendarEventType calendarEventType) {
33+
CalendarEventFactoryService service = calendarEventServiceMap.get(calendarEventType);
34+
if (service == null) {
35+
throw new ValidationException("지원하지 않는 캘린더 이벤트 타입입니다: " + calendarEventType);
36+
}
37+
return service;
38+
}
39+
40+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.memory.service.calendar.factory;
2+
3+
import com.memory.dto.calendar.CalendarEventRequest;
4+
import com.memory.dto.calendar.response.BaseCalendarEventResponse;
5+
6+
public interface CalendarEventFactoryService {
7+
8+
BaseCalendarEventResponse createCalendarEvent(Long memberId, CalendarEventRequest.Create request);
9+
10+
}

0 commit comments

Comments
 (0)