mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 06:46:24 +00:00
Feat: 회의 중 메모 저장 API 구현
This commit is contained in:
parent
764a620980
commit
d0aa6353da
@ -650,8 +650,7 @@ code + .copy-button {
|
||||
<script type="text/javascript">
|
||||
function configurationCacheProblems() { return (
|
||||
// begin-report-data
|
||||
{"diagnostics":[{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: /Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}],"contextualLabel":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.filename","displayName":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java uses or overrides a deprecated API."}]},{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/gateway/entity/MinutesSectionEntity.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]}],"problemsReport":{"totalProblemCount":2,"buildName":"hgzero","requestedTasks":":meeting:bootRun","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
|
||||
{"diagnostics":[{"locations":[{"path":"/Users/daewoong/home/workspace/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/request/InviteParticipantRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Some input files use or override a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Some input files use or override a deprecated API."}],"contextualLabel":"Some input files use or override a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.plural","displayName":"Some input files use or override a deprecated API."}]},{"locations":[{"path":"/Users/daewoong/home/workspace/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/infra/dto/request/InviteParticipantRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]}],"problemsReport":{"totalProblemCount":2,"buildName":"hgzero","requestedTasks":":meeting:bootRun","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
|
||||
{"diagnostics":[{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java uses or overrides a deprecated API."}],"severity":"ADVICE","problemDetails":[{"text":"Note: /Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java uses or overrides a deprecated API."}],"contextualLabel":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java uses or overrides a deprecated API.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.filename","displayName":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java uses or overrides a deprecated API."}]},{"locations":[{"path":"/Users/adela/home/workspace/recent/HGZero/meeting/src/main/java/com/unicorn/hgzero/meeting/biz/dto/request/MeetingMemoRequest.java"},{"taskPath":":meeting:compileJava"}],"problem":[{"text":"Recompile with -Xlint:deprecation for details."}],"severity":"ADVICE","problemDetails":[{"text":"Note: Recompile with -Xlint:deprecation for details."}],"contextualLabel":"Recompile with -Xlint:deprecation for details.","problemId":[{"name":"java","displayName":"Java compilation"},{"name":"compilation","displayName":"Compilation"},{"name":"compiler.note.deprecated.recompile","displayName":"Recompile with -Xlint:deprecation for details."}]}],"problemsReport":{"totalProblemCount":2,"buildName":"hgzero","requestedTasks":":meeting:compileJava","documentationLink":"https://docs.gradle.org/8.14/userguide/reporting_problems.html","documentationLinkCaption":"Problem report","summaries":[]}}
|
||||
// end-report-data
|
||||
);}
|
||||
</script>
|
||||
|
||||
@ -41,6 +41,8 @@ public enum ErrorCode {
|
||||
INVALID_MEETING_TIME(HttpStatus.BAD_REQUEST, "M002", "회의 시작 시간이 종료 시간보다 늦습니다."),
|
||||
MEETING_NOT_FOUND(HttpStatus.NOT_FOUND, "M003", "회의를 찾을 수 없습니다."),
|
||||
INVALID_MEETING_STATUS(HttpStatus.BAD_REQUEST, "M004", "유효하지 않은 회의 상태입니다."),
|
||||
MEETING_NOT_IN_PROGRESS(HttpStatus.BAD_REQUEST, "M005", "회의가 진행 중이 아닙니다."),
|
||||
MINUTES_NOT_FOUND(HttpStatus.NOT_FOUND, "M006", "회의록을 찾을 수 없습니다."),
|
||||
|
||||
// 외부 시스템 에러 (4xxx)
|
||||
EXTERNAL_API_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "E001", "외부 API 호출 중 오류가 발생했습니다."),
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,21 @@
|
||||
package com.unicorn.hgzero.meeting.biz.dto.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "회의 메모 저장 요청")
|
||||
public class MeetingMemoRequest {
|
||||
|
||||
@NotBlank(message = "회의 ID는 필수입니다.")
|
||||
@Schema(description = "회의 ID", example = "meeting-123", required = true)
|
||||
private String meetingId;
|
||||
|
||||
@NotBlank(message = "메모 내용은 필수입니다.")
|
||||
@Schema(description = "메모 내용", example = "[00:15] 신제품의 타겟 고객층을 20-30대로 설정하고, 모바일 우선 전략을 취하기로 논의 중입니다.", required = true)
|
||||
private String memo;
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.unicorn.hgzero.meeting.biz.dto.response;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
@Schema(description = "회의 메모 저장 응답")
|
||||
public class MeetingMemoResponse {
|
||||
|
||||
@Schema(description = "회의 ID", example = "meeting-123")
|
||||
private String meetingId;
|
||||
|
||||
@Schema(description = "저장된 섹션 ID", example = "section-456")
|
||||
private String sectionId;
|
||||
|
||||
@Schema(description = "저장 완료 메시지", example = "메모가 성공적으로 저장되었습니다.")
|
||||
private String message;
|
||||
|
||||
public static MeetingMemoResponse of(String meetingId, String sectionId) {
|
||||
return MeetingMemoResponse.builder()
|
||||
.meetingId(meetingId)
|
||||
.sectionId(sectionId)
|
||||
.message("메모가 성공적으로 저장되었습니다.")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,144 @@
|
||||
package com.unicorn.hgzero.meeting.biz.service;
|
||||
|
||||
import com.unicorn.hgzero.common.exception.BusinessException;
|
||||
import com.unicorn.hgzero.common.exception.ErrorCode;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.meeting.SaveMeetingMemoUseCase;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionWriter;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 회의 메모 관리 Service
|
||||
* 회의 중 작성되는 메모를 MinutesSection에 저장하고 관리합니다.
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MeetingMemoService implements SaveMeetingMemoUseCase {
|
||||
|
||||
private final MeetingReader meetingReader;
|
||||
private final MinutesReader minutesReader;
|
||||
private final MinutesSectionReader minutesSectionReader;
|
||||
private final MinutesSectionWriter minutesSectionWriter;
|
||||
|
||||
/**
|
||||
* 회의 중 AI 메모 저장
|
||||
* @param command 메모 저장 명령
|
||||
* @return 저장된 섹션 ID
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public String saveMemo(SaveMemoCommand command) {
|
||||
log.info("Saving meeting memo for meetingId: {}", command.getMeetingId());
|
||||
|
||||
// 1. 회의 존재 여부 및 진행 중 상태 확인
|
||||
Meeting meeting = meetingReader.findById(command.getMeetingId())
|
||||
.orElseThrow(() -> {
|
||||
log.error("Meeting not found: {}", command.getMeetingId());
|
||||
return new BusinessException(ErrorCode.MEETING_NOT_FOUND);
|
||||
});
|
||||
|
||||
// 2. 회의가 진행 중인지 확인
|
||||
if (!meeting.isInProgress()) {
|
||||
log.warn("Meeting is not in progress: meetingId={}", command.getMeetingId());
|
||||
throw new BusinessException(ErrorCode.MEETING_NOT_IN_PROGRESS);
|
||||
}
|
||||
|
||||
// 3. 해당 회의의 회의록 조회
|
||||
List<Minutes> minutesList = minutesReader.findByMeetingId(command.getMeetingId());
|
||||
if (minutesList.isEmpty()) {
|
||||
log.error("Minutes not found for meetingId: {}", command.getMeetingId());
|
||||
throw new BusinessException(ErrorCode.MINUTES_NOT_FOUND);
|
||||
}
|
||||
Minutes minutes = minutesList.get(0); // 첫 번째 회의록 사용
|
||||
|
||||
// 4. 기존 메모 섹션 조회 또는 새로운 섹션 생성
|
||||
MinutesSection memoSection = minutesSectionReader.findFirstByMinutesIdAndType(
|
||||
minutes.getMinutesId(),
|
||||
"AI_MEMO"
|
||||
).orElseGet(() -> createNewMemoSection(minutes.getMinutesId()));
|
||||
|
||||
// 5. 메모 내용 업데이트 (기존 내용에 추가)
|
||||
String updatedContent = updateMemoContent(
|
||||
memoSection.getContent(),
|
||||
command.getMemo(),
|
||||
command.getUserId()
|
||||
);
|
||||
|
||||
// 6. 섹션 업데이트 및 저장
|
||||
MinutesSection updatedSection = MinutesSection.builder()
|
||||
.sectionId(memoSection.getSectionId())
|
||||
.minutesId(memoSection.getMinutesId())
|
||||
.type("AI_MEMO")
|
||||
.title("회의 메모")
|
||||
.content(updatedContent)
|
||||
.order(memoSection.getOrder())
|
||||
.verified(false)
|
||||
.locked(false)
|
||||
.build();
|
||||
|
||||
minutesSectionWriter.save(updatedSection);
|
||||
|
||||
log.info("Meeting memo saved successfully: sectionId={}", updatedSection.getSectionId());
|
||||
return updatedSection.getSectionId();
|
||||
}
|
||||
|
||||
/**
|
||||
* 새로운 메모 섹션 생성
|
||||
*/
|
||||
private MinutesSection createNewMemoSection(String minutesId) {
|
||||
// 해당 회의록의 최대 order 값 조회
|
||||
int maxOrder = minutesSectionReader.getMaxOrderByMinutesId(minutesId);
|
||||
|
||||
return MinutesSection.builder()
|
||||
.sectionId(generateSectionId())
|
||||
.minutesId(minutesId)
|
||||
.type("AI_MEMO")
|
||||
.title("회의 메모")
|
||||
.content("")
|
||||
.order(maxOrder + 1)
|
||||
.verified(false)
|
||||
.locked(false)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 메모 내용 업데이트
|
||||
* 기존 내용이 있으면 추가하고, 없으면 새로 작성
|
||||
*/
|
||||
private String updateMemoContent(String existingContent, String newMemo, String userId) {
|
||||
StringBuilder content = new StringBuilder();
|
||||
|
||||
if (existingContent != null && !existingContent.trim().isEmpty()) {
|
||||
content.append(existingContent);
|
||||
content.append("\n\n");
|
||||
}
|
||||
|
||||
// 타임스탬프와 함께 새로운 메모 추가
|
||||
content.append(String.format("[%s - %s]\n%s",
|
||||
LocalDateTime.now().toString().substring(11, 19), // HH:mm:ss
|
||||
userId,
|
||||
newMemo));
|
||||
|
||||
return content.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 ID 생성
|
||||
*/
|
||||
private String generateSectionId() {
|
||||
return "section-" + UUID.randomUUID().toString();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* 회의 중 AI 메모 저장 UseCase
|
||||
* 회의 진행 중 사용자가 작성한 메모를 MinutesSection의 content에 저장합니다.
|
||||
*/
|
||||
public interface SaveMeetingMemoUseCase {
|
||||
|
||||
/**
|
||||
* 회의 메모 저장
|
||||
* @param command 메모 저장 명령
|
||||
* @return 저장된 메모의 섹션 ID
|
||||
*/
|
||||
String saveMemo(SaveMemoCommand command);
|
||||
|
||||
/**
|
||||
* 메모 저장 명령
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
class SaveMemoCommand {
|
||||
private final String meetingId;
|
||||
private final String memo;
|
||||
private final String userId;
|
||||
}
|
||||
}
|
||||
@ -39,4 +39,14 @@ public interface MinutesSectionReader {
|
||||
* 잠금한 사용자 ID로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> findByLockedBy(String lockedBy);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 단일 섹션 조회
|
||||
*/
|
||||
Optional<MinutesSection> findFirstByMinutesIdAndType(String minutesId, String type);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 최대 order 값 조회
|
||||
*/
|
||||
int getMaxOrderByMinutesId(String minutesId);
|
||||
}
|
||||
|
||||
@ -13,6 +13,8 @@ import com.unicorn.hgzero.meeting.infra.dto.response.InviteParticipantResponse;
|
||||
import com.unicorn.hgzero.meeting.infra.dto.response.MeetingResponse;
|
||||
import com.unicorn.hgzero.meeting.infra.dto.response.MeetingEndResponse;
|
||||
import com.unicorn.hgzero.meeting.infra.dto.response.SessionResponse;
|
||||
import com.unicorn.hgzero.meeting.biz.dto.request.MeetingMemoRequest;
|
||||
import com.unicorn.hgzero.meeting.biz.dto.response.MeetingMemoResponse;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
|
||||
@ -44,6 +46,7 @@ public class MeetingController {
|
||||
private final CancelMeetingUseCase cancelMeetingUseCase;
|
||||
private final InviteParticipantUseCase inviteParticipantUseCase;
|
||||
private final ApplyTemplateUseCase applyTemplateUseCase;
|
||||
private final SaveMeetingMemoUseCase saveMeetingMemoUseCase;
|
||||
|
||||
/**
|
||||
* 회의 예약
|
||||
@ -319,4 +322,48 @@ public class MeetingController {
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 중 AI 메모 저장
|
||||
*
|
||||
* @param meetingId 회의 ID
|
||||
* @param userId 사용자 ID
|
||||
* @param userName 사용자명
|
||||
* @param userEmail 사용자 이메일
|
||||
* @param request 메모 저장 요청
|
||||
* @return 메모 저장 결과
|
||||
*/
|
||||
@Operation(
|
||||
summary = "회의 중 AI 메모 저장",
|
||||
description = "회의 진행 중 사용자가 작성한 메모를 MinutesSection에 저장합니다. AI가 감지한 주요 내용과 사용자가 직접 입력한 내용을 함께 저장할 수 있습니다.",
|
||||
security = @SecurityRequirement(name = "bearerAuth")
|
||||
)
|
||||
@PostMapping("/{meetingId}/memo")
|
||||
public ResponseEntity<ApiResponse<MeetingMemoResponse>> saveMeetingMemo(
|
||||
@Parameter(description = "회의 ID", required = true)
|
||||
@PathVariable String meetingId,
|
||||
@Parameter(description = "사용자 ID", required = true)
|
||||
@RequestHeader("X-User-Id") String userId,
|
||||
@Parameter(description = "사용자명", required = true)
|
||||
@RequestHeader("X-User-Name") String userName,
|
||||
@Parameter(description = "사용자 이메일", required = true)
|
||||
@RequestHeader("X-User-Email") String userEmail,
|
||||
@Valid @RequestBody MeetingMemoRequest request) {
|
||||
|
||||
log.info("회의 메모 저장 요청 - meetingId: {}, userId: {}", meetingId, userId);
|
||||
|
||||
String sectionId = saveMeetingMemoUseCase.saveMemo(
|
||||
SaveMeetingMemoUseCase.SaveMemoCommand.builder()
|
||||
.meetingId(meetingId)
|
||||
.memo(request.getMemo())
|
||||
.userId(userId)
|
||||
.build()
|
||||
);
|
||||
|
||||
var response = MeetingMemoResponse.of(meetingId, sectionId);
|
||||
|
||||
log.info("회의 메모 저장 완료 - meetingId: {}, sectionId: {}", meetingId, sectionId);
|
||||
|
||||
return ResponseEntity.ok(ApiResponse.success(response));
|
||||
}
|
||||
}
|
||||
@ -4,7 +4,9 @@ import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionWriter;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesSectionEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MinutesSectionJpaRepository;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MinutesJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
@ -23,6 +25,7 @@ import java.util.stream.Collectors;
|
||||
public class MinutesSectionGateway implements MinutesSectionReader, MinutesSectionWriter {
|
||||
|
||||
private final MinutesSectionJpaRepository sectionJpaRepository;
|
||||
private final MinutesJpaRepository minutesJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<MinutesSection> findById(String sectionId) {
|
||||
@ -37,7 +40,6 @@ public class MinutesSectionGateway implements MinutesSectionReader, MinutesSecti
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByMinutesIdAndType(String minutesId, String type) {
|
||||
return sectionJpaRepository.findByMinutesIdAndType(minutesId, type).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
@ -73,6 +75,8 @@ public class MinutesSectionGateway implements MinutesSectionReader, MinutesSecti
|
||||
|
||||
if (entity != null) {
|
||||
// 기존 엔티티 업데이트 (minutes 연관관계 유지)
|
||||
entity.updateContent(section.getContent());
|
||||
|
||||
if (section.getLocked() != null && section.getLocked()) {
|
||||
entity.lock(section.getLockedBy());
|
||||
} else if (section.getLocked() != null && !section.getLocked()) {
|
||||
@ -82,14 +86,41 @@ public class MinutesSectionGateway implements MinutesSectionReader, MinutesSecti
|
||||
entity.verify();
|
||||
}
|
||||
} else {
|
||||
// 새 엔티티 생성
|
||||
// 새 엔티티 생성 - minutes 연관관계 설정 필요
|
||||
entity = MinutesSectionEntity.fromDomain(section);
|
||||
|
||||
// Minutes 엔티티 조회하여 연관관계 설정
|
||||
MinutesEntity minutesEntity = minutesJpaRepository.findById(section.getMinutesId())
|
||||
.orElseThrow(() -> new IllegalArgumentException("Minutes not found: " + section.getMinutesId()));
|
||||
entity = MinutesSectionEntity.builder()
|
||||
.sectionId(section.getSectionId())
|
||||
.id(section.getSectionId()) // id와 sectionId를 동일하게 설정
|
||||
.minutes(minutesEntity) // 연관관계 설정
|
||||
.type(section.getType())
|
||||
.title(section.getTitle())
|
||||
.content(section.getContent())
|
||||
.order(section.getOrder())
|
||||
.verified(section.getVerified())
|
||||
.locked(section.getLocked())
|
||||
.lockedBy(section.getLockedBy())
|
||||
.build();
|
||||
}
|
||||
|
||||
MinutesSectionEntity savedEntity = sectionJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<MinutesSection> findFirstByMinutesIdAndType(String minutesId, String type) {
|
||||
return sectionJpaRepository.findFirstByMinutesIdAndType(minutesId, type)
|
||||
.map(MinutesSectionEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxOrderByMinutesId(String minutesId) {
|
||||
return sectionJpaRepository.getMaxOrderByMinutesId(minutesId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String sectionId) {
|
||||
sectionJpaRepository.deleteById(sectionId);
|
||||
|
||||
@ -20,8 +20,11 @@ import lombok.NoArgsConstructor;
|
||||
public class MinutesSectionEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "id", length = 50)
|
||||
@Column(name = "section_id", length = 50)
|
||||
private String sectionId;
|
||||
|
||||
@Column(name = "id", length = 50)
|
||||
private String id;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "minutes_id", nullable = false)
|
||||
@ -71,6 +74,7 @@ public class MinutesSectionEntity extends BaseTimeEntity {
|
||||
public static MinutesSectionEntity fromDomain(MinutesSection section) {
|
||||
return MinutesSectionEntity.builder()
|
||||
.sectionId(section.getSectionId())
|
||||
.id(section.getSectionId()) // id와 sectionId를 동일하게 설정
|
||||
.minutesId(section.getMinutesId())
|
||||
.type(section.getType())
|
||||
.title(section.getTitle())
|
||||
@ -95,4 +99,8 @@ public class MinutesSectionEntity extends BaseTimeEntity {
|
||||
public void verify() {
|
||||
this.verified = true;
|
||||
}
|
||||
|
||||
public void updateContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 JPA Repository
|
||||
@ -39,4 +40,15 @@ public interface MinutesSectionJpaRepository extends JpaRepository<MinutesSectio
|
||||
* 잠금한 사용자 ID로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSectionEntity> findByLockedBy(String lockedBy);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 단일 섹션 조회
|
||||
*/
|
||||
Optional<MinutesSectionEntity> findFirstByMinutesIdAndType(String minutesId, String type);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 최대 order 값 조회
|
||||
*/
|
||||
@Query("SELECT COALESCE(MAX(m.order), 0) FROM MinutesSectionEntity m WHERE m.minutesId = :minutesId")
|
||||
int getMaxOrderByMinutesId(@Param("minutesId") String minutesId);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user