Skip to content
Merged
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(
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