Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.haru.api.domain.meeting.controller;

import com.haru.api.domain.meeting.dto.MeetingRequestDTO;
import com.haru.api.domain.meeting.dto.MeetingResponseDTO;
import com.haru.api.domain.meeting.service.MeetingService;
import com.haru.api.domain.user.security.jwt.SecurityUtil;
import com.haru.api.global.apiPayload.ApiResponse;
import com.haru.api.global.apiPayload.code.status.ErrorStatus;
import com.haru.api.global.apiPayload.exception.GeneralException;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

@RestController
@RequestMapping("/api/v1/meetings")
@RequiredArgsConstructor
public class MeetingController {

private final MeetingService meetingService;

@PostMapping(
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE },
produces = MediaType.APPLICATION_JSON_VALUE
)
public ApiResponse<MeetingResponseDTO.createMeetingResponse> createMeeting(
@RequestPart("agendaFile") MultipartFile agendaFile,
@RequestPart("request") MeetingRequestDTO.createMeetingRequest request) {

// file업로드가 되지 않는 경우 controller단에서 요청 처리
if (agendaFile == null || agendaFile.isEmpty()) {
throw new GeneralException(ErrorStatus.MEETING_FILE_NOT_FOUND);
}
Long userId = SecurityUtil.getCurrentUserId();

MeetingResponseDTO.createMeetingResponse response = meetingService.createMeeting(userId, agendaFile, request);

return ApiResponse.onSuccess(response);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.haru.api.domain.meeting.converter;

import com.haru.api.domain.meeting.dto.MeetingResponseDTO;
import com.haru.api.domain.meeting.entity.Meetings;
import org.springframework.stereotype.Component;

@Component
public class MeetingConverter {

// Entity -> ResponseDTO
public static MeetingResponseDTO.createMeetingResponse toCreateMeetingResponse(Meetings meeting) {
return MeetingResponseDTO.createMeetingResponse.builder()
.meetingId(meeting.getId())
.title(meeting.getTitle())
.updatedAt(meeting.getCreatedAt())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.haru.api.domain.meeting.dto;

import lombok.Builder;
import lombok.Getter;

public class MeetingRequestDTO {

@Getter
@Builder
public static class createMeetingRequest{
private Long workspaceId;
private String title;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.haru.api.domain.meeting.dto;

import com.haru.api.domain.meeting.entity.Meetings;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDateTime;


public class MeetingResponseDTO {
@Getter
@Builder
public static class createMeetingResponse{
private String title;
private Long meetingId;
private LocalDateTime updatedAt;

}
}
53 changes: 53 additions & 0 deletions src/main/java/com/haru/api/domain/meeting/entity/Meetings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.haru.api.domain.meeting.entity;

import com.haru.api.domain.user.entity.Users;
import com.haru.api.domain.workspace.entity.Workspace;
import com.haru.api.global.common.entity.BaseEntity;
import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.DynamicInsert;
import org.hibernate.annotations.DynamicUpdate;

@Entity
@Table(name = "meetings")
@Getter
@DynamicUpdate
@DynamicInsert
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Meetings extends BaseEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false, length = 50)
private String title;

//file을 직접 저장하면 db의 용량이 커지고 조회때마다 io가 커지므로 저장하지 않도록 함
//private String agendaFile;

private String agendaResult;

@Column(columnDefinition="TEXT")
private String proceeding;

@ManyToOne(fetch = FetchType.LAZY)
private Users users;

@ManyToOne(fetch = FetchType.LAZY)
private Workspace workspaces;

private Meetings(String title, String agendaResult, Users users, Workspace workspaces) {
this.title = title;
this.agendaResult = agendaResult;
this.users = users;
this.workspaces = workspaces;
}

public static Meetings createInitialMeeting(String title, String agendaResult, Users users, Workspace workspaces) {
return new Meetings(title, agendaResult, users, workspaces);
}

public void updateProceeding(String proceeding) {
this.proceeding = proceeding;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.haru.api.domain.meeting.repository;

import com.haru.api.domain.meeting.entity.Meetings;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface MeetingRepository extends JpaRepository<Meetings, Long> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.haru.api.domain.meeting.service;

import com.haru.api.domain.meeting.dto.MeetingRequestDTO;
import com.haru.api.domain.meeting.dto.MeetingResponseDTO;
import org.springframework.web.multipart.MultipartFile;

public interface MeetingService {

public MeetingResponseDTO.createMeetingResponse createMeeting(Long userId, MultipartFile agendaFile, MeetingRequestDTO.createMeetingRequest request);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.haru.api.domain.meeting.service;

import com.haru.api.domain.meeting.converter.MeetingConverter;
import com.haru.api.domain.meeting.dto.MeetingRequestDTO;
import com.haru.api.domain.meeting.dto.MeetingResponseDTO;
import com.haru.api.domain.meeting.entity.Meetings;
import com.haru.api.domain.meeting.repository.MeetingRepository;
import com.haru.api.domain.user.entity.Users;
import com.haru.api.domain.user.repository.UserRepository;
import com.haru.api.domain.workspace.entity.Workspace;
import com.haru.api.domain.workspace.repository.WorkspaceRepository;
import com.haru.api.global.apiPayload.code.status.ErrorStatus;
import com.haru.api.global.apiPayload.exception.handler.MemberHandler;
import com.haru.api.global.apiPayload.exception.handler.TempHandler;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class MeetingServiceImpl implements MeetingService{

private final UserRepository userRepository;
private final WorkspaceRepository workspaceRepository;
private final MeetingRepository meetingRepository;

@Override
@Transactional
public MeetingResponseDTO.createMeetingResponse createMeeting(
Long userId,
MultipartFile agendaFile,
MeetingRequestDTO.createMeetingRequest request)
{
Users foundUser = userRepository.findById(userId)
.orElseThrow(() -> new MemberHandler(ErrorStatus.MEMBER_NOT_FOUND));

Workspace foundWorkspace = workspaceRepository.findById(request.getWorkspaceId())
.orElseThrow(() -> new TempHandler(ErrorStatus.WORKSPACE_NOT_FOUND));

// agendaFile을 openAi 활용하여 요약 - 미구현
String agendaResult = "안건지 요약 - 미구현";


Meetings newMeetings = Meetings.createInitialMeeting(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Builder사용해주시면 좋을 것 같습니다!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DDD설계 원칙에 따라 유연성 있는 builder패턴을 사용하지 않고
정적팩토리메서드를 통해 Meeting 객체가 생성될 때 필수 필드들을 강제하고, 도메인 객체 자체가 자신의 생성규칙을 책임지며 초기상태 회의를 생성한다는 의도를 드러내어줄 수 있습니다!

request.getTitle(),
agendaResult,
foundUser,
foundWorkspace
);

Meetings savedMeeting = meetingRepository.save(newMeetings);


return MeetingConverter.toCreateMeetingResponse(savedMeeting);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ public enum ErrorStatus implements BaseErrorCode {
MEMBER_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEMBER1001", "사용자가 없습니다."),
REFRESHTOKEN_NOT_EQUAL(HttpStatus.BAD_REQUEST, "MEMBER1002", "리프레시 토큰이 일치하지 않습니다."),

// Workspace 관련 에러
WORKSPACE_NOT_FOUND(HttpStatus.BAD_REQUEST,"WORKSPACE1001", "워크스페이스가 없습니다."),

// AI회의 Meetings 관련 에러
MEETING_FILE_NOT_FOUND(HttpStatus.BAD_REQUEST, "MEETING1001", "안건지가 업로드되지 않았습니다."),

// 예시,,,
ARTICLE_NOT_FOUND(HttpStatus.NOT_FOUND, "ARTICLE4001", "게시글이 없습니다.");

Expand Down