mirror of
https://github.com/hwanny1128/HGZero.git
synced 2026-06-13 08:19:10 +00:00
develop
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 대시보드 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Dashboard {
|
||||
|
||||
/**
|
||||
* 다가오는 회의 목록
|
||||
*/
|
||||
private List<Meeting> upcomingMeetings;
|
||||
|
||||
/**
|
||||
* 최근 회의록 목록
|
||||
*/
|
||||
private List<Minutes> recentMinutes;
|
||||
|
||||
/**
|
||||
* 할당된 Todo 목록
|
||||
*/
|
||||
private List<Todo> assignedTodos;
|
||||
|
||||
/**
|
||||
* 통계 정보
|
||||
*/
|
||||
private Statistics statistics;
|
||||
|
||||
/**
|
||||
* 통계 정보 내부 클래스
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class Statistics {
|
||||
/**
|
||||
* 전체 회의 수
|
||||
*/
|
||||
private Integer totalMeetings;
|
||||
|
||||
/**
|
||||
* 진행 중인 회의 수
|
||||
*/
|
||||
private Integer inProgressMeetings;
|
||||
|
||||
/**
|
||||
* 완료된 회의 수
|
||||
*/
|
||||
private Integer completedMeetings;
|
||||
|
||||
/**
|
||||
* 전체 Todo 수
|
||||
*/
|
||||
private Integer totalTodos;
|
||||
|
||||
/**
|
||||
* 완료된 Todo 수
|
||||
*/
|
||||
private Integer completedTodos;
|
||||
|
||||
/**
|
||||
* 지연된 Todo 수
|
||||
*/
|
||||
private Integer overdueTodos;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Meeting {
|
||||
|
||||
/**
|
||||
* 회의 ID
|
||||
*/
|
||||
private String meetingId;
|
||||
|
||||
/**
|
||||
* 회의 제목
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 회의 설명
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 회의 일시
|
||||
*/
|
||||
private LocalDateTime scheduledAt;
|
||||
|
||||
/**
|
||||
* 회의 시작 일시
|
||||
*/
|
||||
private LocalDateTime startedAt;
|
||||
|
||||
/**
|
||||
* 회의 종료 일시
|
||||
*/
|
||||
private LocalDateTime endedAt;
|
||||
|
||||
/**
|
||||
* 회의 상태 (SCHEDULED, IN_PROGRESS, COMPLETED, CANCELLED)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 주최자 ID
|
||||
*/
|
||||
private String organizerId;
|
||||
|
||||
/**
|
||||
* 참석자 ID 목록
|
||||
*/
|
||||
private List<String> participants;
|
||||
|
||||
/**
|
||||
* 템플릿 ID
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 회의 시작
|
||||
*/
|
||||
public void start() {
|
||||
this.status = "IN_PROGRESS";
|
||||
this.startedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 종료
|
||||
*/
|
||||
public void end() {
|
||||
this.status = "COMPLETED";
|
||||
this.endedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 취소
|
||||
*/
|
||||
public void cancel() {
|
||||
this.status = "CANCELLED";
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 진행 중 여부 확인
|
||||
*/
|
||||
public boolean isInProgress() {
|
||||
return "IN_PROGRESS".equals(this.status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의록 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Minutes {
|
||||
|
||||
/**
|
||||
* 회의록 ID
|
||||
*/
|
||||
private String minutesId;
|
||||
|
||||
/**
|
||||
* 회의 ID
|
||||
*/
|
||||
private String meetingId;
|
||||
|
||||
/**
|
||||
* 회의록 제목
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 목록
|
||||
*/
|
||||
private List<MinutesSection> sections;
|
||||
|
||||
/**
|
||||
* 회의록 상태 (DRAFT, FINALIZED)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 버전
|
||||
*/
|
||||
private Integer version;
|
||||
|
||||
/**
|
||||
* 작성자 ID
|
||||
*/
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 확정자 ID
|
||||
*/
|
||||
private String finalizedBy;
|
||||
|
||||
/**
|
||||
* 확정 일시
|
||||
*/
|
||||
private LocalDateTime finalizedAt;
|
||||
|
||||
/**
|
||||
* 회의록 확정
|
||||
*/
|
||||
public void finalize(String userId) {
|
||||
this.status = "FINALIZED";
|
||||
this.finalizedBy = userId;
|
||||
this.finalizedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 수정
|
||||
*/
|
||||
public void update() {
|
||||
this.version++;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 확정 여부 확인
|
||||
*/
|
||||
public boolean isFinalized() {
|
||||
return "FINALIZED".equals(this.status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MinutesSection {
|
||||
|
||||
/**
|
||||
* 섹션 ID
|
||||
*/
|
||||
private String sectionId;
|
||||
|
||||
/**
|
||||
* 회의록 ID
|
||||
*/
|
||||
private String minutesId;
|
||||
|
||||
/**
|
||||
* 섹션 유형 (AGENDA, DISCUSSION, DECISION, ACTION_ITEM)
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 섹션 제목
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 섹션 내용
|
||||
*/
|
||||
private String content;
|
||||
|
||||
/**
|
||||
* 섹션 순서
|
||||
*/
|
||||
private Integer order;
|
||||
|
||||
/**
|
||||
* 검증 완료 여부
|
||||
*/
|
||||
private Boolean verified;
|
||||
|
||||
/**
|
||||
* 잠금 여부
|
||||
*/
|
||||
private Boolean locked;
|
||||
|
||||
/**
|
||||
* 잠금 사용자 ID
|
||||
*/
|
||||
private String lockedBy;
|
||||
|
||||
/**
|
||||
* 섹션 잠금
|
||||
*/
|
||||
public void lock(String userId) {
|
||||
this.locked = true;
|
||||
this.lockedBy = userId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 잠금 해제
|
||||
*/
|
||||
public void unlock() {
|
||||
this.locked = false;
|
||||
this.lockedBy = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 검증 완료
|
||||
*/
|
||||
public void verify() {
|
||||
this.verified = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 잠금 여부 확인
|
||||
*/
|
||||
public boolean isLocked() {
|
||||
return Boolean.TRUE.equals(this.locked);
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 검증 완료 여부 확인
|
||||
*/
|
||||
public boolean isVerified() {
|
||||
return Boolean.TRUE.equals(this.verified);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의록 템플릿 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Template {
|
||||
|
||||
/**
|
||||
* 템플릿 ID
|
||||
*/
|
||||
private String templateId;
|
||||
|
||||
/**
|
||||
* 템플릿 이름
|
||||
*/
|
||||
private String name;
|
||||
|
||||
/**
|
||||
* 템플릿 설명
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 템플릿 카테고리 (GENERAL, TECHNICAL, MANAGEMENT, SALES)
|
||||
*/
|
||||
private String category;
|
||||
|
||||
/**
|
||||
* 템플릿 섹션 목록
|
||||
*/
|
||||
private List<TemplateSection> sections;
|
||||
|
||||
/**
|
||||
* 공개 여부
|
||||
*/
|
||||
private Boolean isPublic;
|
||||
|
||||
/**
|
||||
* 생성자 ID
|
||||
*/
|
||||
private String createdBy;
|
||||
|
||||
/**
|
||||
* 템플릿 섹션 내부 클래스
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class TemplateSection {
|
||||
/**
|
||||
* 섹션 유형
|
||||
*/
|
||||
private String type;
|
||||
|
||||
/**
|
||||
* 섹션 제목
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* 섹션 순서
|
||||
*/
|
||||
private Integer order;
|
||||
|
||||
/**
|
||||
* 기본 내용
|
||||
*/
|
||||
private String defaultContent;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Todo 도메인 모델
|
||||
*/
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class Todo {
|
||||
|
||||
/**
|
||||
* Todo ID
|
||||
*/
|
||||
private String todoId;
|
||||
|
||||
/**
|
||||
* 회의록 ID
|
||||
*/
|
||||
private String minutesId;
|
||||
|
||||
/**
|
||||
* 회의 ID
|
||||
*/
|
||||
private String meetingId;
|
||||
|
||||
/**
|
||||
* Todo 제목
|
||||
*/
|
||||
private String title;
|
||||
|
||||
/**
|
||||
* Todo 설명
|
||||
*/
|
||||
private String description;
|
||||
|
||||
/**
|
||||
* 담당자 ID
|
||||
*/
|
||||
private String assigneeId;
|
||||
|
||||
/**
|
||||
* 마감일
|
||||
*/
|
||||
private LocalDate dueDate;
|
||||
|
||||
/**
|
||||
* Todo 상태 (PENDING, IN_PROGRESS, COMPLETED, CANCELLED)
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/**
|
||||
* 우선순위 (HIGH, MEDIUM, LOW)
|
||||
*/
|
||||
private String priority;
|
||||
|
||||
/**
|
||||
* 완료 일시
|
||||
*/
|
||||
private LocalDateTime completedAt;
|
||||
|
||||
/**
|
||||
* Todo 완료
|
||||
*/
|
||||
public void complete() {
|
||||
this.status = "COMPLETED";
|
||||
this.completedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo 취소
|
||||
*/
|
||||
public void cancel() {
|
||||
this.status = "CANCELLED";
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo 완료 여부 확인
|
||||
*/
|
||||
public boolean isCompleted() {
|
||||
return "COMPLETED".equals(this.status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 마감일 지남 여부 확인
|
||||
*/
|
||||
public boolean isOverdue() {
|
||||
return this.dueDate != null &&
|
||||
LocalDate.now().isAfter(this.dueDate) &&
|
||||
!isCompleted();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.unicorn.hgzero.meeting.biz.service;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.dashboard.GetDashboardUseCase;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.DashboardReader;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
/**
|
||||
* 대시보드 Service
|
||||
* Dashboard 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class DashboardService implements GetDashboardUseCase {
|
||||
|
||||
private final DashboardReader dashboardReader;
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Dashboard getDashboard(String userId) {
|
||||
log.debug("Getting dashboard for user: {}", userId);
|
||||
|
||||
return dashboardReader.getDashboardByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 (기간 필터) 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Dashboard getDashboardByPeriod(String userId, String period) {
|
||||
log.debug("Getting dashboard for user: {} with period: {}", userId, period);
|
||||
|
||||
return dashboardReader.getDashboardByUserIdAndPeriod(userId, period);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
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.usecase.in.meeting.*;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
|
||||
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
|
||||
* Meeting 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MeetingService implements
|
||||
CreateMeetingUseCase,
|
||||
StartMeetingUseCase,
|
||||
EndMeetingUseCase,
|
||||
CancelMeetingUseCase,
|
||||
GetMeetingUseCase {
|
||||
|
||||
private final MeetingReader meetingReader;
|
||||
private final MeetingWriter meetingWriter;
|
||||
|
||||
/**
|
||||
* 회의 생성
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Meeting createMeeting(CreateMeetingCommand command) {
|
||||
log.info("Creating meeting: {}", command.title());
|
||||
|
||||
// 회의 ID 생성
|
||||
String meetingId = UUID.randomUUID().toString();
|
||||
|
||||
// 회의 도메인 객체 생성
|
||||
Meeting meeting = Meeting.builder()
|
||||
.meetingId(meetingId)
|
||||
.title(command.title())
|
||||
.description(command.description())
|
||||
.scheduledAt(command.scheduledAt())
|
||||
.status("SCHEDULED")
|
||||
.organizerId(command.organizerId())
|
||||
.participants(command.participants())
|
||||
.templateId(command.templateId())
|
||||
.build();
|
||||
|
||||
// 회의 저장
|
||||
Meeting savedMeeting = meetingWriter.save(meeting);
|
||||
|
||||
log.info("Meeting created successfully: {}", savedMeeting.getMeetingId());
|
||||
return savedMeeting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 시작
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Meeting startMeeting(String meetingId) {
|
||||
log.info("Starting meeting: {}", meetingId);
|
||||
|
||||
// 회의 조회
|
||||
Meeting meeting = meetingReader.findById(meetingId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 회의 상태 검증
|
||||
if (!"SCHEDULED".equals(meeting.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 회의 시작
|
||||
meeting.start();
|
||||
|
||||
// 저장
|
||||
Meeting updatedMeeting = meetingWriter.save(meeting);
|
||||
|
||||
log.info("Meeting started successfully: {}", meetingId);
|
||||
return updatedMeeting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 종료
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Meeting endMeeting(String meetingId) {
|
||||
log.info("Ending meeting: {}", meetingId);
|
||||
|
||||
// 회의 조회
|
||||
Meeting meeting = meetingReader.findById(meetingId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 회의 상태 검증
|
||||
if (!"IN_PROGRESS".equals(meeting.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 회의 종료
|
||||
meeting.end();
|
||||
|
||||
// 저장
|
||||
Meeting updatedMeeting = meetingWriter.save(meeting);
|
||||
|
||||
log.info("Meeting ended successfully: {}", meetingId);
|
||||
return updatedMeeting;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 취소
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Meeting cancelMeeting(String meetingId) {
|
||||
log.info("Canceling meeting: {}", meetingId);
|
||||
|
||||
// 회의 조회
|
||||
Meeting meeting = meetingReader.findById(meetingId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 회의 취소 가능 상태 검증
|
||||
if ("COMPLETED".equals(meeting.getStatus()) || "CANCELLED".equals(meeting.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 회의 취소
|
||||
meeting.cancel();
|
||||
|
||||
// 저장
|
||||
Meeting updatedMeeting = meetingWriter.save(meeting);
|
||||
|
||||
log.info("Meeting cancelled successfully: {}", meetingId);
|
||||
return updatedMeeting;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 회의 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Meeting getMeeting(String meetingId) {
|
||||
log.debug("Getting meeting: {}", meetingId);
|
||||
|
||||
return meetingReader.findById(meetingId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 주최자 ID로 회의 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Meeting> getMeetingsByOrganizer(String organizerId) {
|
||||
log.debug("Getting meetings by organizer: {}", organizerId);
|
||||
|
||||
return meetingReader.findByOrganizerId(organizerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태로 회의 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Meeting> getMeetingsByStatus(String status) {
|
||||
log.debug("Getting meetings by status: {}", status);
|
||||
|
||||
return meetingReader.findByStatus(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 일정 시간 범위로 회의 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Meeting> getMeetingsByScheduledTime(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
log.debug("Getting meetings by scheduled time: {} ~ {}", startTime, endTime);
|
||||
|
||||
return meetingReader.findByScheduledTimeBetween(startTime, endTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 주최자 ID와 상태로 회의 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Meeting> getMeetingsByOrganizerAndStatus(String organizerId, String status) {
|
||||
log.debug("Getting meetings by organizer: {} and status: {}", organizerId, status);
|
||||
|
||||
return meetingReader.findByOrganizerIdAndStatus(organizerId, status);
|
||||
}
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
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.MinutesSection;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.section.*;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesSectionWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 Service
|
||||
* MinutesSection 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MinutesSectionService implements
|
||||
CreateSectionUseCase,
|
||||
UpdateSectionUseCase,
|
||||
DeleteSectionUseCase,
|
||||
LockSectionUseCase,
|
||||
VerifySectionUseCase,
|
||||
GetSectionUseCase {
|
||||
|
||||
private final MinutesSectionReader sectionReader;
|
||||
private final MinutesSectionWriter sectionWriter;
|
||||
|
||||
/**
|
||||
* 섹션 생성
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MinutesSection createSection(CreateSectionCommand command) {
|
||||
log.info("Creating section for minutes: {}", command.minutesId());
|
||||
|
||||
// 섹션 ID 생성
|
||||
String sectionId = UUID.randomUUID().toString();
|
||||
|
||||
// 섹션 도메인 객체 생성
|
||||
MinutesSection section = MinutesSection.builder()
|
||||
.sectionId(sectionId)
|
||||
.minutesId(command.minutesId())
|
||||
.type(command.type())
|
||||
.title(command.title())
|
||||
.content(command.content())
|
||||
.order(command.order())
|
||||
.verified(false)
|
||||
.locked(false)
|
||||
.build();
|
||||
|
||||
// 섹션 저장
|
||||
MinutesSection savedSection = sectionWriter.save(section);
|
||||
|
||||
log.info("Section created successfully: {}", savedSection.getSectionId());
|
||||
return savedSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 내용 수정
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MinutesSection updateSection(UpdateSectionCommand command) {
|
||||
log.info("Updating section: {}", command.sectionId());
|
||||
|
||||
// 섹션 조회
|
||||
MinutesSection section = sectionReader.findById(command.sectionId())
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 잠금 상태 검증
|
||||
if (Boolean.TRUE.equals(section.getLocked())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 검증 상태 검증
|
||||
if (Boolean.TRUE.equals(section.getVerified())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 섹션 수정
|
||||
section.update(command.title(), command.content());
|
||||
|
||||
// 저장
|
||||
MinutesSection updatedSection = sectionWriter.save(section);
|
||||
|
||||
log.info("Section updated successfully: {}", command.sectionId());
|
||||
return updatedSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 삭제
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteSection(String sectionId) {
|
||||
log.info("Deleting section: {}", sectionId);
|
||||
|
||||
// 섹션 조회
|
||||
MinutesSection section = sectionReader.findById(sectionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 검증 상태 확인 (검증된 섹션은 삭제 불가)
|
||||
if (Boolean.TRUE.equals(section.getVerified())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 섹션 삭제
|
||||
sectionWriter.delete(sectionId);
|
||||
|
||||
log.info("Section deleted successfully: {}", sectionId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 잠금
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MinutesSection lockSection(String sectionId, String userId) {
|
||||
log.info("Locking section: {} by user: {}", sectionId, userId);
|
||||
|
||||
// 섹션 조회
|
||||
MinutesSection section = sectionReader.findById(sectionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 이미 잠금 상태 확인
|
||||
if (Boolean.TRUE.equals(section.getLocked())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 섹션 잠금
|
||||
section.lock(userId);
|
||||
|
||||
// 저장
|
||||
MinutesSection lockedSection = sectionWriter.save(section);
|
||||
|
||||
log.info("Section locked successfully: {}", sectionId);
|
||||
return lockedSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 잠금 해제
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MinutesSection unlockSection(String sectionId) {
|
||||
log.info("Unlocking section: {}", sectionId);
|
||||
|
||||
// 섹션 조회
|
||||
MinutesSection section = sectionReader.findById(sectionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 잠금 상태 확인
|
||||
if (Boolean.FALSE.equals(section.getLocked())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 섹션 잠금 해제
|
||||
section.unlock();
|
||||
|
||||
// 저장
|
||||
MinutesSection unlockedSection = sectionWriter.save(section);
|
||||
|
||||
log.info("Section unlocked successfully: {}", sectionId);
|
||||
return unlockedSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* 섹션 검증
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public MinutesSection verifySection(String sectionId) {
|
||||
log.info("Verifying section: {}", sectionId);
|
||||
|
||||
// 섹션 조회
|
||||
MinutesSection section = sectionReader.findById(sectionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 이미 검증 상태 확인
|
||||
if (Boolean.TRUE.equals(section.getVerified())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 섹션 검증
|
||||
section.verify();
|
||||
|
||||
// 저장
|
||||
MinutesSection verifiedSection = sectionWriter.save(section);
|
||||
|
||||
log.info("Section verified successfully: {}", sectionId);
|
||||
return verifiedSection;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 섹션 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public MinutesSection getSection(String sectionId) {
|
||||
log.debug("Getting section: {}", sectionId);
|
||||
|
||||
return sectionReader.findById(sectionId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 ID로 섹션 목록 조회 (순서대로)
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<MinutesSection> getSectionsByMinutes(String minutesId) {
|
||||
log.debug("Getting sections by minutes: {}", minutesId);
|
||||
|
||||
return sectionReader.findByMinutesIdOrderByOrder(minutesId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 섹션 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<MinutesSection> getSectionsByMinutesAndType(String minutesId, String type) {
|
||||
log.debug("Getting sections by minutes: {} and type: {}", minutesId, type);
|
||||
|
||||
return sectionReader.findByMinutesIdAndType(minutesId, type);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
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.Minutes;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.minutes.*;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 회의록 Service
|
||||
* Minutes 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class MinutesService implements
|
||||
CreateMinutesUseCase,
|
||||
UpdateMinutesUseCase,
|
||||
FinalizeMinutesUseCase,
|
||||
GetMinutesUseCase {
|
||||
|
||||
private final MinutesReader minutesReader;
|
||||
private final MinutesWriter minutesWriter;
|
||||
|
||||
/**
|
||||
* 회의록 생성
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Minutes createMinutes(CreateMinutesCommand command) {
|
||||
log.info("Creating minutes for meeting: {}", command.meetingId());
|
||||
|
||||
// 회의록 ID 생성
|
||||
String minutesId = UUID.randomUUID().toString();
|
||||
|
||||
// 회의록 도메인 객체 생성
|
||||
Minutes minutes = Minutes.builder()
|
||||
.minutesId(minutesId)
|
||||
.meetingId(command.meetingId())
|
||||
.title(command.title())
|
||||
.status("DRAFT")
|
||||
.version(1)
|
||||
.createdBy(command.createdBy())
|
||||
.build();
|
||||
|
||||
// 회의록 저장
|
||||
Minutes savedMinutes = minutesWriter.save(minutes);
|
||||
|
||||
log.info("Minutes created successfully: {}", savedMinutes.getMinutesId());
|
||||
return savedMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 제목 수정
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Minutes updateMinutesTitle(String minutesId, String title) {
|
||||
log.info("Updating minutes title: {}", minutesId);
|
||||
|
||||
// 회의록 조회
|
||||
Minutes minutes = minutesReader.findById(minutesId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 상태 검증 (확정된 회의록은 수정 불가)
|
||||
if ("FINALIZED".equals(minutes.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 제목 수정
|
||||
minutes.update(title, minutes.getSections());
|
||||
|
||||
// 저장
|
||||
Minutes updatedMinutes = minutesWriter.save(minutes);
|
||||
|
||||
log.info("Minutes title updated successfully: {}", minutesId);
|
||||
return updatedMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 버전 증가
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Minutes incrementVersion(String minutesId) {
|
||||
log.info("Incrementing minutes version: {}", minutesId);
|
||||
|
||||
// 회의록 조회
|
||||
Minutes minutes = minutesReader.findById(minutesId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 버전 증가
|
||||
minutes.incrementVersion();
|
||||
|
||||
// 저장
|
||||
Minutes updatedMinutes = minutesWriter.save(minutes);
|
||||
|
||||
log.info("Minutes version incremented: {} -> {}", minutesId, updatedMinutes.getVersion());
|
||||
return updatedMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 확정
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Minutes finalizeMinutes(String minutesId, String userId) {
|
||||
log.info("Finalizing minutes: {}", minutesId);
|
||||
|
||||
// 회의록 조회
|
||||
Minutes minutes = minutesReader.findById(minutesId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 상태 검증
|
||||
if ("FINALIZED".equals(minutes.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// 회의록 확정
|
||||
minutes.finalize(userId);
|
||||
|
||||
// 저장
|
||||
Minutes finalizedMinutes = minutesWriter.save(minutes);
|
||||
|
||||
log.info("Minutes finalized successfully: {}", minutesId);
|
||||
return finalizedMinutes;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 회의록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Minutes getMinutes(String minutesId) {
|
||||
log.debug("Getting minutes: {}", minutesId);
|
||||
|
||||
return minutesReader.findById(minutesId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 ID로 회의록 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Minutes> getMinutesByMeeting(String meetingId) {
|
||||
log.debug("Getting minutes by meeting: {}", meetingId);
|
||||
|
||||
return minutesReader.findByMeetingId(meetingId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 ID로 최신 회의록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Minutes getLatestMinutes(String meetingId) {
|
||||
log.debug("Getting latest minutes for meeting: {}", meetingId);
|
||||
|
||||
return minutesReader.findLatestByMeetingId(meetingId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 작성자 ID로 회의록 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Minutes> getMinutesByCreator(String createdBy) {
|
||||
log.debug("Getting minutes by creator: {}", createdBy);
|
||||
|
||||
return minutesReader.findByCreatedBy(createdBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 상태로 회의록 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Minutes> getMinutesByStatus(String status) {
|
||||
log.debug("Getting minutes by status: {}", status);
|
||||
|
||||
return minutesReader.findByStatus(status);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
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.Template;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.template.CreateTemplateUseCase;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.template.GetTemplateUseCase;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TemplateReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TemplateWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* 템플릿 Service
|
||||
* Template 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TemplateService implements
|
||||
CreateTemplateUseCase,
|
||||
GetTemplateUseCase {
|
||||
|
||||
private final TemplateReader templateReader;
|
||||
private final TemplateWriter templateWriter;
|
||||
|
||||
/**
|
||||
* 템플릿 생성
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Template createTemplate(CreateTemplateCommand command) {
|
||||
log.info("Creating template: {}", command.name());
|
||||
|
||||
// 템플릿 ID 생성
|
||||
String templateId = UUID.randomUUID().toString();
|
||||
|
||||
// 템플릿 도메인 객체 생성
|
||||
Template template = Template.builder()
|
||||
.templateId(templateId)
|
||||
.name(command.name())
|
||||
.description(command.description())
|
||||
.category(command.category())
|
||||
.sections(command.sections())
|
||||
.isPublic(command.isPublic() != null ? command.isPublic() : true)
|
||||
.createdBy(command.createdBy())
|
||||
.build();
|
||||
|
||||
// 템플릿 저장
|
||||
Template savedTemplate = templateWriter.save(template);
|
||||
|
||||
log.info("Template created successfully: {}", savedTemplate.getTemplateId());
|
||||
return savedTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 템플릿 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Template getTemplate(String templateId) {
|
||||
log.debug("Getting template: {}", templateId);
|
||||
|
||||
return templateReader.findById(templateId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 카테고리로 템플릿 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Template> getTemplatesByCategory(String category) {
|
||||
log.debug("Getting templates by category: {}", category);
|
||||
|
||||
return templateReader.findByCategory(category);
|
||||
}
|
||||
|
||||
/**
|
||||
* 공개 템플릿 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Template> getPublicTemplates() {
|
||||
log.debug("Getting public templates");
|
||||
|
||||
return templateReader.findByIsPublic(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 작성자 ID로 템플릿 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Template> getTemplatesByCreator(String createdBy) {
|
||||
log.debug("Getting templates by creator: {}", createdBy);
|
||||
|
||||
return templateReader.findByCreatedBy(createdBy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 이름으로 템플릿 검색
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Template> searchTemplatesByName(String name) {
|
||||
log.debug("Searching templates by name: {}", name);
|
||||
|
||||
return templateReader.findByNameContaining(name);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
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.Todo;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.in.todo.*;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TodoReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TodoWriter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Todo Service
|
||||
* Todo 관련 모든 UseCase 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class TodoService implements
|
||||
CreateTodoUseCase,
|
||||
UpdateTodoUseCase,
|
||||
CompleteTodoUseCase,
|
||||
CancelTodoUseCase,
|
||||
GetTodoUseCase {
|
||||
|
||||
private final TodoReader todoReader;
|
||||
private final TodoWriter todoWriter;
|
||||
|
||||
/**
|
||||
* Todo 생성
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Todo createTodo(CreateTodoCommand command) {
|
||||
log.info("Creating todo: {}", command.title());
|
||||
|
||||
// Todo ID 생성
|
||||
String todoId = UUID.randomUUID().toString();
|
||||
|
||||
// Todo 도메인 객체 생성
|
||||
Todo todo = Todo.builder()
|
||||
.todoId(todoId)
|
||||
.minutesId(command.minutesId())
|
||||
.meetingId(command.meetingId())
|
||||
.title(command.title())
|
||||
.description(command.description())
|
||||
.assigneeId(command.assigneeId())
|
||||
.dueDate(command.dueDate())
|
||||
.status("PENDING")
|
||||
.priority(command.priority() != null ? command.priority() : "MEDIUM")
|
||||
.build();
|
||||
|
||||
// Todo 저장
|
||||
Todo savedTodo = todoWriter.save(todo);
|
||||
|
||||
log.info("Todo created successfully: {}", savedTodo.getTodoId());
|
||||
return savedTodo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo 수정
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Todo updateTodo(UpdateTodoCommand command) {
|
||||
log.info("Updating todo: {}", command.todoId());
|
||||
|
||||
// Todo 조회
|
||||
Todo todo = todoReader.findById(command.todoId())
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 완료/취소 상태 검증
|
||||
if ("COMPLETED".equals(todo.getStatus()) || "CANCELLED".equals(todo.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// Todo 수정
|
||||
todo.update(
|
||||
command.title(),
|
||||
command.description(),
|
||||
command.assigneeId(),
|
||||
command.dueDate(),
|
||||
command.priority()
|
||||
);
|
||||
|
||||
// 저장
|
||||
Todo updatedTodo = todoWriter.save(todo);
|
||||
|
||||
log.info("Todo updated successfully: {}", command.todoId());
|
||||
return updatedTodo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo 완료
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Todo completeTodo(String todoId) {
|
||||
log.info("Completing todo: {}", todoId);
|
||||
|
||||
// Todo 조회
|
||||
Todo todo = todoReader.findById(todoId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 상태 검증
|
||||
if ("COMPLETED".equals(todo.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// Todo 완료
|
||||
todo.complete();
|
||||
|
||||
// 저장
|
||||
Todo completedTodo = todoWriter.save(todo);
|
||||
|
||||
log.info("Todo completed successfully: {}", todoId);
|
||||
return completedTodo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Todo 취소
|
||||
*/
|
||||
@Override
|
||||
@Transactional
|
||||
public Todo cancelTodo(String todoId) {
|
||||
log.info("Cancelling todo: {}", todoId);
|
||||
|
||||
// Todo 조회
|
||||
Todo todo = todoReader.findById(todoId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
|
||||
// 상태 검증
|
||||
if ("COMPLETED".equals(todo.getStatus()) || "CANCELLED".equals(todo.getStatus())) {
|
||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||
}
|
||||
|
||||
// Todo 취소
|
||||
todo.cancel();
|
||||
|
||||
// 저장
|
||||
Todo cancelledTodo = todoWriter.save(todo);
|
||||
|
||||
log.info("Todo cancelled successfully: {}", todoId);
|
||||
return cancelledTodo;
|
||||
}
|
||||
|
||||
/**
|
||||
* ID로 Todo 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public Todo getTodo(String todoId) {
|
||||
log.debug("Getting todo: {}", todoId);
|
||||
|
||||
return todoReader.findById(todoId)
|
||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의 ID로 Todo 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Todo> getTodosByMeeting(String meetingId) {
|
||||
log.debug("Getting todos by meeting: {}", meetingId);
|
||||
|
||||
return todoReader.findByMeetingId(meetingId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 회의록 ID로 Todo 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Todo> getTodosByMinutes(String minutesId) {
|
||||
log.debug("Getting todos by minutes: {}", minutesId);
|
||||
|
||||
return todoReader.findByMinutesId(minutesId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 담당자 ID로 Todo 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Todo> getTodosByAssignee(String assigneeId) {
|
||||
log.debug("Getting todos by assignee: {}", assigneeId);
|
||||
|
||||
return todoReader.findByAssigneeId(assigneeId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 담당자 ID와 상태로 Todo 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Todo> getTodosByAssigneeAndStatus(String assigneeId, String status) {
|
||||
log.debug("Getting todos by assignee: {} and status: {}", assigneeId, status);
|
||||
|
||||
return todoReader.findByAssigneeIdAndStatus(assigneeId, status);
|
||||
}
|
||||
|
||||
/**
|
||||
* 담당자 ID와 마감일 범위로 Todo 목록 조회
|
||||
*/
|
||||
@Override
|
||||
@Transactional(readOnly = true)
|
||||
public List<Todo> getTodosByAssigneeAndDueDateRange(String assigneeId, LocalDate startDate, LocalDate endDate) {
|
||||
log.debug("Getting todos by assignee: {} and due date range: {} ~ {}", assigneeId, startDate, endDate);
|
||||
|
||||
return todoReader.findByAssigneeIdAndDueDateBetween(assigneeId, startDate, endDate);
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.dashboard;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
|
||||
|
||||
/**
|
||||
* 대시보드 조회 UseCase
|
||||
*/
|
||||
public interface GetDashboardUseCase {
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 조회
|
||||
*/
|
||||
Dashboard getDashboard(String userId);
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 (기간 필터) 조회
|
||||
*/
|
||||
Dashboard getDashboardByPeriod(String userId, String period);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
/**
|
||||
* 회의 취소 UseCase
|
||||
*/
|
||||
public interface CancelMeetingUseCase {
|
||||
|
||||
/**
|
||||
* 회의 취소
|
||||
*/
|
||||
Meeting cancelMeeting(String meetingId);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의 생성 UseCase
|
||||
*/
|
||||
public interface CreateMeetingUseCase {
|
||||
|
||||
/**
|
||||
* 회의 생성
|
||||
*/
|
||||
Meeting createMeeting(CreateMeetingCommand command);
|
||||
|
||||
/**
|
||||
* 회의 생성 명령
|
||||
*/
|
||||
record CreateMeetingCommand(
|
||||
String title,
|
||||
String description,
|
||||
LocalDateTime scheduledAt,
|
||||
String organizerId,
|
||||
List<String> participants,
|
||||
String templateId
|
||||
) {}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
/**
|
||||
* 회의 종료 UseCase
|
||||
*/
|
||||
public interface EndMeetingUseCase {
|
||||
|
||||
/**
|
||||
* 회의 종료
|
||||
*/
|
||||
Meeting endMeeting(String meetingId);
|
||||
}
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의 조회 UseCase
|
||||
*/
|
||||
public interface GetMeetingUseCase {
|
||||
|
||||
/**
|
||||
* ID로 회의 조회
|
||||
*/
|
||||
Meeting getMeeting(String meetingId);
|
||||
|
||||
/**
|
||||
* 주최자 ID로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> getMeetingsByOrganizer(String organizerId);
|
||||
|
||||
/**
|
||||
* 상태로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> getMeetingsByStatus(String status);
|
||||
|
||||
/**
|
||||
* 일정 시간 범위로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> getMeetingsByScheduledTime(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 주최자 ID와 상태로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> getMeetingsByOrganizerAndStatus(String organizerId, String status);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.meeting;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
/**
|
||||
* 회의 시작 UseCase
|
||||
*/
|
||||
public interface StartMeetingUseCase {
|
||||
|
||||
/**
|
||||
* 회의 시작
|
||||
*/
|
||||
Meeting startMeeting(String meetingId);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.minutes;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
/**
|
||||
* 회의록 생성 UseCase
|
||||
*/
|
||||
public interface CreateMinutesUseCase {
|
||||
|
||||
/**
|
||||
* 회의록 생성
|
||||
*/
|
||||
Minutes createMinutes(CreateMinutesCommand command);
|
||||
|
||||
/**
|
||||
* 회의록 생성 명령
|
||||
*/
|
||||
record CreateMinutesCommand(
|
||||
String meetingId,
|
||||
String title,
|
||||
String createdBy
|
||||
) {}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.minutes;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
/**
|
||||
* 회의록 확정 UseCase
|
||||
*/
|
||||
public interface FinalizeMinutesUseCase {
|
||||
|
||||
/**
|
||||
* 회의록 확정
|
||||
*/
|
||||
Minutes finalizeMinutes(String minutesId, String userId);
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.minutes;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의록 조회 UseCase
|
||||
*/
|
||||
public interface GetMinutesUseCase {
|
||||
|
||||
/**
|
||||
* ID로 회의록 조회
|
||||
*/
|
||||
Minutes getMinutes(String minutesId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> getMinutesByMeeting(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 최신 회의록 조회
|
||||
*/
|
||||
Minutes getLatestMinutes(String meetingId);
|
||||
|
||||
/**
|
||||
* 작성자 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> getMinutesByCreator(String createdBy);
|
||||
|
||||
/**
|
||||
* 상태로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> getMinutesByStatus(String status);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.minutes;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
/**
|
||||
* 회의록 수정 UseCase
|
||||
*/
|
||||
public interface UpdateMinutesUseCase {
|
||||
|
||||
/**
|
||||
* 회의록 제목 수정
|
||||
*/
|
||||
Minutes updateMinutesTitle(String minutesId, String title);
|
||||
|
||||
/**
|
||||
* 회의록 버전 증가
|
||||
*/
|
||||
Minutes incrementVersion(String minutesId);
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 생성 UseCase
|
||||
*/
|
||||
public interface CreateSectionUseCase {
|
||||
|
||||
/**
|
||||
* 섹션 생성
|
||||
*/
|
||||
MinutesSection createSection(CreateSectionCommand command);
|
||||
|
||||
/**
|
||||
* 섹션 생성 명령
|
||||
*/
|
||||
record CreateSectionCommand(
|
||||
String minutesId,
|
||||
String type,
|
||||
String title,
|
||||
String content,
|
||||
Integer order
|
||||
) {}
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 삭제 UseCase
|
||||
*/
|
||||
public interface DeleteSectionUseCase {
|
||||
|
||||
/**
|
||||
* 섹션 삭제
|
||||
*/
|
||||
void deleteSection(String sectionId);
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 조회 UseCase
|
||||
*/
|
||||
public interface GetSectionUseCase {
|
||||
|
||||
/**
|
||||
* ID로 섹션 조회
|
||||
*/
|
||||
MinutesSection getSection(String sectionId);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 섹션 목록 조회 (순서대로)
|
||||
*/
|
||||
List<MinutesSection> getSectionsByMinutes(String minutesId);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> getSectionsByMinutesAndType(String minutesId, String type);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 잠금 UseCase
|
||||
*/
|
||||
public interface LockSectionUseCase {
|
||||
|
||||
/**
|
||||
* 섹션 잠금
|
||||
*/
|
||||
MinutesSection lockSection(String sectionId, String userId);
|
||||
|
||||
/**
|
||||
* 섹션 잠금 해제
|
||||
*/
|
||||
MinutesSection unlockSection(String sectionId);
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 수정 UseCase
|
||||
*/
|
||||
public interface UpdateSectionUseCase {
|
||||
|
||||
/**
|
||||
* 섹션 내용 수정
|
||||
*/
|
||||
MinutesSection updateSection(UpdateSectionCommand command);
|
||||
|
||||
/**
|
||||
* 섹션 수정 명령
|
||||
*/
|
||||
record UpdateSectionCommand(
|
||||
String sectionId,
|
||||
String title,
|
||||
String content
|
||||
) {}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.section;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 검증 UseCase
|
||||
*/
|
||||
public interface VerifySectionUseCase {
|
||||
|
||||
/**
|
||||
* 섹션 검증
|
||||
*/
|
||||
MinutesSection verifySection(String sectionId);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.template;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Template;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 템플릿 생성 UseCase
|
||||
*/
|
||||
public interface CreateTemplateUseCase {
|
||||
|
||||
/**
|
||||
* 템플릿 생성
|
||||
*/
|
||||
Template createTemplate(CreateTemplateCommand command);
|
||||
|
||||
/**
|
||||
* 템플릿 생성 명령
|
||||
*/
|
||||
record CreateTemplateCommand(
|
||||
String name,
|
||||
String description,
|
||||
String category,
|
||||
List<Template.TemplateSection> sections,
|
||||
Boolean isPublic,
|
||||
String createdBy
|
||||
) {}
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.template;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Template;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 템플릿 조회 UseCase
|
||||
*/
|
||||
public interface GetTemplateUseCase {
|
||||
|
||||
/**
|
||||
* ID로 템플릿 조회
|
||||
*/
|
||||
Template getTemplate(String templateId);
|
||||
|
||||
/**
|
||||
* 카테고리로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> getTemplatesByCategory(String category);
|
||||
|
||||
/**
|
||||
* 공개 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> getPublicTemplates();
|
||||
|
||||
/**
|
||||
* 작성자 ID로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> getTemplatesByCreator(String createdBy);
|
||||
|
||||
/**
|
||||
* 이름으로 템플릿 검색
|
||||
*/
|
||||
List<Template> searchTemplatesByName(String name);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.todo;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
/**
|
||||
* Todo 취소 UseCase
|
||||
*/
|
||||
public interface CancelTodoUseCase {
|
||||
|
||||
/**
|
||||
* Todo 취소
|
||||
*/
|
||||
Todo cancelTodo(String todoId);
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.todo;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
/**
|
||||
* Todo 완료 UseCase
|
||||
*/
|
||||
public interface CompleteTodoUseCase {
|
||||
|
||||
/**
|
||||
* Todo 완료
|
||||
*/
|
||||
Todo completeTodo(String todoId);
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.todo;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Todo 생성 UseCase
|
||||
*/
|
||||
public interface CreateTodoUseCase {
|
||||
|
||||
/**
|
||||
* Todo 생성
|
||||
*/
|
||||
Todo createTodo(CreateTodoCommand command);
|
||||
|
||||
/**
|
||||
* Todo 생성 명령
|
||||
*/
|
||||
record CreateTodoCommand(
|
||||
String minutesId,
|
||||
String meetingId,
|
||||
String title,
|
||||
String description,
|
||||
String assigneeId,
|
||||
LocalDate dueDate,
|
||||
String priority
|
||||
) {}
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.todo;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Todo 조회 UseCase
|
||||
*/
|
||||
public interface GetTodoUseCase {
|
||||
|
||||
/**
|
||||
* ID로 Todo 조회
|
||||
*/
|
||||
Todo getTodo(String todoId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> getTodosByMeeting(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> getTodosByMinutes(String minutesId);
|
||||
|
||||
/**
|
||||
* 담당자 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> getTodosByAssignee(String assigneeId);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 상태로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> getTodosByAssigneeAndStatus(String assigneeId, String status);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 마감일 범위로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> getTodosByAssigneeAndDueDateRange(String assigneeId, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.in.todo;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* Todo 수정 UseCase
|
||||
*/
|
||||
public interface UpdateTodoUseCase {
|
||||
|
||||
/**
|
||||
* Todo 수정
|
||||
*/
|
||||
Todo updateTodo(UpdateTodoCommand command);
|
||||
|
||||
/**
|
||||
* Todo 수정 명령
|
||||
*/
|
||||
record UpdateTodoCommand(
|
||||
String todoId,
|
||||
String title,
|
||||
String description,
|
||||
String assigneeId,
|
||||
LocalDate dueDate,
|
||||
String priority
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
|
||||
|
||||
/**
|
||||
* 대시보드 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface DashboardReader {
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 조회
|
||||
*/
|
||||
Dashboard getDashboardByUserId(String userId);
|
||||
|
||||
/**
|
||||
* 사용자 대시보드 (기간 필터) 조회
|
||||
*/
|
||||
Dashboard getDashboardByUserIdAndPeriod(String userId, String period);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 회의 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MeetingReader {
|
||||
|
||||
/**
|
||||
* ID로 회의 조회
|
||||
*/
|
||||
Optional<Meeting> findById(String meetingId);
|
||||
|
||||
/**
|
||||
* 주최자 ID로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> findByOrganizerId(String organizerId);
|
||||
|
||||
/**
|
||||
* 상태로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 주최자 ID와 상태로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> findByOrganizerIdAndStatus(String organizerId, String status);
|
||||
|
||||
/**
|
||||
* 일정 시간 범위로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> findByScheduledTimeBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 템플릿 ID로 회의 목록 조회
|
||||
*/
|
||||
List<Meeting> findByTemplateId(String templateId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
|
||||
/**
|
||||
* 회의 저장 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MeetingWriter {
|
||||
|
||||
/**
|
||||
* 회의 저장
|
||||
*/
|
||||
Meeting save(Meeting meeting);
|
||||
|
||||
/**
|
||||
* 회의 삭제
|
||||
*/
|
||||
void delete(String meetingId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 회의록 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MinutesReader {
|
||||
|
||||
/**
|
||||
* ID로 회의록 조회
|
||||
*/
|
||||
Optional<Minutes> findById(String minutesId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> findByMeetingId(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 최신 회의록 조회
|
||||
*/
|
||||
Optional<Minutes> findLatestByMeetingId(String meetingId);
|
||||
|
||||
/**
|
||||
* 작성자 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> findByCreatedBy(String createdBy);
|
||||
|
||||
/**
|
||||
* 상태로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 확정자 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<Minutes> findByFinalizedBy(String finalizedBy);
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MinutesSectionReader {
|
||||
|
||||
/**
|
||||
* ID로 섹션 조회
|
||||
*/
|
||||
Optional<MinutesSection> findById(String sectionId);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 섹션 목록 조회 (순서대로)
|
||||
*/
|
||||
List<MinutesSection> findByMinutesIdOrderByOrder(String minutesId);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> findByMinutesIdAndType(String minutesId, String type);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 검증 여부로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> findByMinutesIdAndVerified(String minutesId, Boolean verified);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 잠금 여부로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> findByMinutesIdAndLocked(String minutesId, Boolean locked);
|
||||
|
||||
/**
|
||||
* 잠금한 사용자 ID로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSection> findByLockedBy(String lockedBy);
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 저장 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MinutesSectionWriter {
|
||||
|
||||
/**
|
||||
* 섹션 저장
|
||||
*/
|
||||
MinutesSection save(MinutesSection section);
|
||||
|
||||
/**
|
||||
* 섹션 삭제
|
||||
*/
|
||||
void delete(String sectionId);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
|
||||
/**
|
||||
* 회의록 저장 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface MinutesWriter {
|
||||
|
||||
/**
|
||||
* 회의록 저장
|
||||
*/
|
||||
Minutes save(Minutes minutes);
|
||||
|
||||
/**
|
||||
* 회의록 삭제
|
||||
*/
|
||||
void delete(String minutesId);
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Template;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 템플릿 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface TemplateReader {
|
||||
|
||||
/**
|
||||
* ID로 템플릿 조회
|
||||
*/
|
||||
Optional<Template> findById(String templateId);
|
||||
|
||||
/**
|
||||
* 카테고리로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> findByCategory(String category);
|
||||
|
||||
/**
|
||||
* 공개 여부로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> findByIsPublic(Boolean isPublic);
|
||||
|
||||
/**
|
||||
* 작성자 ID로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> findByCreatedBy(String createdBy);
|
||||
|
||||
/**
|
||||
* 카테고리와 공개 여부로 템플릿 목록 조회
|
||||
*/
|
||||
List<Template> findByCategoryAndIsPublic(String category, Boolean isPublic);
|
||||
|
||||
/**
|
||||
* 이름으로 템플릿 검색 (부분 일치)
|
||||
*/
|
||||
List<Template> findByNameContaining(String name);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Template;
|
||||
|
||||
/**
|
||||
* 템플릿 저장 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface TemplateWriter {
|
||||
|
||||
/**
|
||||
* 템플릿 저장
|
||||
*/
|
||||
Template save(Template template);
|
||||
|
||||
/**
|
||||
* 템플릿 삭제
|
||||
*/
|
||||
void delete(String templateId);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Todo 조회 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface TodoReader {
|
||||
|
||||
/**
|
||||
* ID로 Todo 조회
|
||||
*/
|
||||
Optional<Todo> findById(String todoId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByMeetingId(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByMinutesId(String minutesId);
|
||||
|
||||
/**
|
||||
* 담당자 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByAssigneeId(String assigneeId);
|
||||
|
||||
/**
|
||||
* 상태로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 상태로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByAssigneeIdAndStatus(String assigneeId, String status);
|
||||
|
||||
/**
|
||||
* 마감일로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByDueDate(LocalDate dueDate);
|
||||
|
||||
/**
|
||||
* 마감일 이전 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByDueDateBefore(LocalDate date);
|
||||
|
||||
/**
|
||||
* 우선순위로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByPriority(String priority);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 마감일 범위로 Todo 목록 조회
|
||||
*/
|
||||
List<Todo> findByAssigneeIdAndDueDateBetween(String assigneeId, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.unicorn.hgzero.meeting.biz.usecase.out;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
|
||||
/**
|
||||
* Todo 저장 Gateway Interface (Out Port)
|
||||
*/
|
||||
public interface TodoWriter {
|
||||
|
||||
/**
|
||||
* Todo 저장
|
||||
*/
|
||||
Todo save(Todo todo);
|
||||
|
||||
/**
|
||||
* Todo 삭제
|
||||
*/
|
||||
void delete(String todoId);
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Dashboard;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.DashboardReader;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MeetingJpaRepository;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MinutesJpaRepository;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.TodoJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 대시보드 Gateway 구현체
|
||||
* DashboardReader 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class DashboardGateway implements DashboardReader {
|
||||
|
||||
private final MeetingJpaRepository meetingJpaRepository;
|
||||
private final MinutesJpaRepository minutesJpaRepository;
|
||||
private final TodoJpaRepository todoJpaRepository;
|
||||
|
||||
@Override
|
||||
public Dashboard getDashboardByUserId(String userId) {
|
||||
log.debug("Getting dashboard for user: {}", userId);
|
||||
|
||||
// 회의 통계 조회
|
||||
long totalMeetings = meetingJpaRepository.findByOrganizerId(userId).size();
|
||||
long scheduledMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "SCHEDULED").size();
|
||||
long inProgressMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "IN_PROGRESS").size();
|
||||
long completedMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "COMPLETED").size();
|
||||
|
||||
// 회의록 통계 조회
|
||||
long totalMinutes = minutesJpaRepository.findByCreatedBy(userId).size();
|
||||
long draftMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
||||
.filter(m -> "DRAFT".equals(m.getStatus()))
|
||||
.count();
|
||||
long finalizedMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
||||
.filter(m -> "FINALIZED".equals(m.getStatus()))
|
||||
.count();
|
||||
|
||||
// Todo 통계 조회
|
||||
long totalTodos = todoJpaRepository.findByAssigneeId(userId).size();
|
||||
long pendingTodos = todoJpaRepository.findByAssigneeIdAndStatus(userId, "PENDING").size();
|
||||
long completedTodos = todoJpaRepository.findByAssigneeIdAndStatus(userId, "COMPLETED").size();
|
||||
long overdueTodos = todoJpaRepository.findByAssigneeId(userId).stream()
|
||||
.filter(todo -> todo.getDueDate() != null
|
||||
&& LocalDate.now().isAfter(todo.getDueDate())
|
||||
&& !"COMPLETED".equals(todo.getStatus()))
|
||||
.count();
|
||||
|
||||
// 통계 객체 생성
|
||||
Dashboard.Statistics statistics = new Dashboard.Statistics(
|
||||
totalMeetings,
|
||||
scheduledMeetings,
|
||||
inProgressMeetings,
|
||||
completedMeetings,
|
||||
totalMinutes,
|
||||
draftMinutes,
|
||||
finalizedMinutes,
|
||||
totalTodos,
|
||||
pendingTodos,
|
||||
completedTodos,
|
||||
overdueTodos
|
||||
);
|
||||
|
||||
// 대시보드 생성
|
||||
return Dashboard.builder()
|
||||
.userId(userId)
|
||||
.statistics(statistics)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dashboard getDashboardByUserIdAndPeriod(String userId, String period) {
|
||||
log.debug("Getting dashboard for user: {} with period: {}", userId, period);
|
||||
|
||||
// 기간 계산
|
||||
LocalDateTime startTime = calculateStartTime(period);
|
||||
LocalDateTime endTime = LocalDateTime.now();
|
||||
|
||||
// 기간 내 회의 통계 조회
|
||||
long totalMeetings = meetingJpaRepository.findByOrganizerId(userId).stream()
|
||||
.filter(m -> m.getScheduledAt().isAfter(startTime) && m.getScheduledAt().isBefore(endTime))
|
||||
.count();
|
||||
|
||||
long scheduledMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "SCHEDULED").stream()
|
||||
.filter(m -> m.getScheduledAt().isAfter(startTime) && m.getScheduledAt().isBefore(endTime))
|
||||
.count();
|
||||
|
||||
long inProgressMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "IN_PROGRESS").stream()
|
||||
.filter(m -> m.getScheduledAt().isAfter(startTime) && m.getScheduledAt().isBefore(endTime))
|
||||
.count();
|
||||
|
||||
long completedMeetings = meetingJpaRepository.findByOrganizerIdAndStatus(userId, "COMPLETED").stream()
|
||||
.filter(m -> m.getScheduledAt().isAfter(startTime) && m.getScheduledAt().isBefore(endTime))
|
||||
.count();
|
||||
|
||||
// 회의록 통계 조회 (전체 기간)
|
||||
long totalMinutes = minutesJpaRepository.findByCreatedBy(userId).size();
|
||||
long draftMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
||||
.filter(m -> "DRAFT".equals(m.getStatus()))
|
||||
.count();
|
||||
long finalizedMinutes = minutesJpaRepository.findByCreatedBy(userId).stream()
|
||||
.filter(m -> "FINALIZED".equals(m.getStatus()))
|
||||
.count();
|
||||
|
||||
// Todo 통계 조회 (전체 기간)
|
||||
long totalTodos = todoJpaRepository.findByAssigneeId(userId).size();
|
||||
long pendingTodos = todoJpaRepository.findByAssigneeIdAndStatus(userId, "PENDING").size();
|
||||
long completedTodos = todoJpaRepository.findByAssigneeIdAndStatus(userId, "COMPLETED").size();
|
||||
long overdueTodos = todoJpaRepository.findByAssigneeId(userId).stream()
|
||||
.filter(todo -> todo.getDueDate() != null
|
||||
&& LocalDate.now().isAfter(todo.getDueDate())
|
||||
&& !"COMPLETED".equals(todo.getStatus()))
|
||||
.count();
|
||||
|
||||
// 통계 객체 생성
|
||||
Dashboard.Statistics statistics = new Dashboard.Statistics(
|
||||
totalMeetings,
|
||||
scheduledMeetings,
|
||||
inProgressMeetings,
|
||||
completedMeetings,
|
||||
totalMinutes,
|
||||
draftMinutes,
|
||||
finalizedMinutes,
|
||||
totalTodos,
|
||||
pendingTodos,
|
||||
completedTodos,
|
||||
overdueTodos
|
||||
);
|
||||
|
||||
// 대시보드 생성
|
||||
return Dashboard.builder()
|
||||
.userId(userId)
|
||||
.period(period)
|
||||
.statistics(statistics)
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 기간 문자열로부터 시작 시간 계산
|
||||
*/
|
||||
private LocalDateTime calculateStartTime(String period) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
|
||||
return switch (period.toUpperCase()) {
|
||||
case "WEEK" -> now.minusWeeks(1);
|
||||
case "MONTH" -> now.minusMonths(1);
|
||||
case "QUARTER" -> now.minusMonths(3);
|
||||
case "YEAR" -> now.minusYears(1);
|
||||
default -> now.minusMonths(1); // 기본값: 1개월
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MeetingEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MeetingJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 회의 Gateway 구현체
|
||||
* MeetingReader, MeetingWriter 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MeetingGateway implements MeetingReader, MeetingWriter {
|
||||
|
||||
private final MeetingJpaRepository meetingJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<Meeting> findById(String meetingId) {
|
||||
return meetingJpaRepository.findById(meetingId)
|
||||
.map(MeetingEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Meeting> findByOrganizerId(String organizerId) {
|
||||
return meetingJpaRepository.findByOrganizerId(organizerId).stream()
|
||||
.map(MeetingEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Meeting> findByStatus(String status) {
|
||||
return meetingJpaRepository.findByStatus(status).stream()
|
||||
.map(MeetingEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Meeting> findByOrganizerIdAndStatus(String organizerId, String status) {
|
||||
return meetingJpaRepository.findByOrganizerIdAndStatus(organizerId, status).stream()
|
||||
.map(MeetingEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Meeting> findByScheduledTimeBetween(LocalDateTime startTime, LocalDateTime endTime) {
|
||||
return meetingJpaRepository.findByScheduledAtBetween(startTime, endTime).stream()
|
||||
.map(MeetingEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Meeting> findByTemplateId(String templateId) {
|
||||
return meetingJpaRepository.findByTemplateId(templateId).stream()
|
||||
.map(MeetingEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Meeting save(Meeting meeting) {
|
||||
MeetingEntity entity = MeetingEntity.fromDomain(meeting);
|
||||
MeetingEntity savedEntity = meetingJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String meetingId) {
|
||||
meetingJpaRepository.deleteById(meetingId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MinutesWriter;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.MinutesJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 회의록 Gateway 구현체
|
||||
* MinutesReader, MinutesWriter 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MinutesGateway implements MinutesReader, MinutesWriter {
|
||||
|
||||
private final MinutesJpaRepository minutesJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<Minutes> findById(String minutesId) {
|
||||
return minutesJpaRepository.findById(minutesId)
|
||||
.map(MinutesEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Minutes> findByMeetingId(String meetingId) {
|
||||
return minutesJpaRepository.findByMeetingId(meetingId).stream()
|
||||
.map(MinutesEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<Minutes> findLatestByMeetingId(String meetingId) {
|
||||
return minutesJpaRepository.findFirstByMeetingIdOrderByVersionDesc(meetingId)
|
||||
.map(MinutesEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Minutes> findByCreatedBy(String createdBy) {
|
||||
return minutesJpaRepository.findByCreatedBy(createdBy).stream()
|
||||
.map(MinutesEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Minutes> findByStatus(String status) {
|
||||
return minutesJpaRepository.findByStatus(status).stream()
|
||||
.map(MinutesEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Minutes> findByFinalizedBy(String finalizedBy) {
|
||||
return minutesJpaRepository.findByFinalizedBy(finalizedBy).stream()
|
||||
.map(MinutesEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Minutes save(Minutes minutes) {
|
||||
MinutesEntity entity = MinutesEntity.fromDomain(minutes);
|
||||
MinutesEntity savedEntity = minutesJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String minutesId) {
|
||||
minutesJpaRepository.deleteById(minutesId);
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
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.repository.MinutesSectionJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 Gateway 구현체
|
||||
* MinutesSectionReader, MinutesSectionWriter 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class MinutesSectionGateway implements MinutesSectionReader, MinutesSectionWriter {
|
||||
|
||||
private final MinutesSectionJpaRepository sectionJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<MinutesSection> findById(String sectionId) {
|
||||
return sectionJpaRepository.findById(sectionId)
|
||||
.map(MinutesSectionEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByMinutesIdOrderByOrder(String minutesId) {
|
||||
return sectionJpaRepository.findByMinutesIdOrderByOrderAsc(minutesId).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByMinutesIdAndType(String minutesId, String type) {
|
||||
return sectionJpaRepository.findByMinutesIdAndType(minutesId, type).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByMinutesIdAndVerified(String minutesId, Boolean verified) {
|
||||
return sectionJpaRepository.findByMinutesIdAndVerified(minutesId, verified).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByMinutesIdAndLocked(String minutesId, Boolean locked) {
|
||||
return sectionJpaRepository.findByMinutesIdAndLocked(minutesId, locked).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MinutesSection> findByLockedBy(String lockedBy) {
|
||||
return sectionJpaRepository.findByLockedBy(lockedBy).stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public MinutesSection save(MinutesSection section) {
|
||||
MinutesSectionEntity entity = MinutesSectionEntity.fromDomain(section);
|
||||
MinutesSectionEntity savedEntity = sectionJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String sectionId) {
|
||||
sectionJpaRepository.deleteById(sectionId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Template;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TemplateReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TemplateWriter;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.TemplateEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.TemplateJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 템플릿 Gateway 구현체
|
||||
* TemplateReader, TemplateWriter 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TemplateGateway implements TemplateReader, TemplateWriter {
|
||||
|
||||
private final TemplateJpaRepository templateJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<Template> findById(String templateId) {
|
||||
return templateJpaRepository.findById(templateId)
|
||||
.map(TemplateEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Template> findByCategory(String category) {
|
||||
return templateJpaRepository.findByCategory(category).stream()
|
||||
.map(TemplateEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Template> findByIsPublic(Boolean isPublic) {
|
||||
return templateJpaRepository.findByIsPublic(isPublic).stream()
|
||||
.map(TemplateEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Template> findByCreatedBy(String createdBy) {
|
||||
return templateJpaRepository.findByCreatedBy(createdBy).stream()
|
||||
.map(TemplateEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Template> findByCategoryAndIsPublic(String category, Boolean isPublic) {
|
||||
return templateJpaRepository.findByCategoryAndIsPublic(category, isPublic).stream()
|
||||
.map(TemplateEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Template> findByNameContaining(String name) {
|
||||
return templateJpaRepository.findByNameContaining(name).stream()
|
||||
.map(TemplateEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Template save(Template template) {
|
||||
TemplateEntity entity = TemplateEntity.fromDomain(template);
|
||||
TemplateEntity savedEntity = templateJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String templateId) {
|
||||
templateJpaRepository.deleteById(templateId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway;
|
||||
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TodoReader;
|
||||
import com.unicorn.hgzero.meeting.biz.usecase.out.TodoWriter;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.TodoEntity;
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.repository.TodoJpaRepository;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Todo Gateway 구현체
|
||||
* TodoReader, TodoWriter 인터페이스 구현
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
public class TodoGateway implements TodoReader, TodoWriter {
|
||||
|
||||
private final TodoJpaRepository todoJpaRepository;
|
||||
|
||||
@Override
|
||||
public Optional<Todo> findById(String todoId) {
|
||||
return todoJpaRepository.findById(todoId)
|
||||
.map(TodoEntity::toDomain);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByMeetingId(String meetingId) {
|
||||
return todoJpaRepository.findByMeetingId(meetingId).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByMinutesId(String minutesId) {
|
||||
return todoJpaRepository.findByMinutesId(minutesId).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByAssigneeId(String assigneeId) {
|
||||
return todoJpaRepository.findByAssigneeId(assigneeId).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByStatus(String status) {
|
||||
return todoJpaRepository.findByStatus(status).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByAssigneeIdAndStatus(String assigneeId, String status) {
|
||||
return todoJpaRepository.findByAssigneeIdAndStatus(assigneeId, status).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByDueDate(LocalDate dueDate) {
|
||||
return todoJpaRepository.findByDueDate(dueDate).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByDueDateBefore(LocalDate date) {
|
||||
return todoJpaRepository.findByDueDateBefore(date).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByPriority(String priority) {
|
||||
return todoJpaRepository.findByPriority(priority).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Todo> findByAssigneeIdAndDueDateBetween(String assigneeId, LocalDate startDate, LocalDate endDate) {
|
||||
return todoJpaRepository.findByAssigneeIdAndDueDateBetween(assigneeId, startDate, endDate).stream()
|
||||
.map(TodoEntity::toDomain)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Todo save(Todo todo) {
|
||||
TodoEntity entity = TodoEntity.fromDomain(todo);
|
||||
TodoEntity savedEntity = todoJpaRepository.save(entity);
|
||||
return savedEntity.toDomain();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String todoId) {
|
||||
todoJpaRepository.deleteById(todoId);
|
||||
}
|
||||
}
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.entity;
|
||||
|
||||
import com.unicorn.hgzero.common.entity.BaseTimeEntity;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 회의 Entity
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "meetings")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MeetingEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "meeting_id", length = 50)
|
||||
private String meetingId;
|
||||
|
||||
@Column(name = "title", length = 200, nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "scheduled_at", nullable = false)
|
||||
private LocalDateTime scheduledAt;
|
||||
|
||||
@Column(name = "started_at")
|
||||
private LocalDateTime startedAt;
|
||||
|
||||
@Column(name = "ended_at")
|
||||
private LocalDateTime endedAt;
|
||||
|
||||
@Column(name = "status", length = 20, nullable = false)
|
||||
@Builder.Default
|
||||
private String status = "SCHEDULED";
|
||||
|
||||
@Column(name = "organizer_id", length = 50, nullable = false)
|
||||
private String organizerId;
|
||||
|
||||
@Column(name = "participants", columnDefinition = "TEXT")
|
||||
private String participants;
|
||||
|
||||
@Column(name = "template_id", length = 50)
|
||||
private String templateId;
|
||||
|
||||
public Meeting toDomain() {
|
||||
return Meeting.builder()
|
||||
.meetingId(this.meetingId)
|
||||
.title(this.title)
|
||||
.description(this.description)
|
||||
.scheduledAt(this.scheduledAt)
|
||||
.startedAt(this.startedAt)
|
||||
.endedAt(this.endedAt)
|
||||
.status(this.status)
|
||||
.organizerId(this.organizerId)
|
||||
.participants(parseParticipants(this.participants))
|
||||
.templateId(this.templateId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static MeetingEntity fromDomain(Meeting meeting) {
|
||||
return MeetingEntity.builder()
|
||||
.meetingId(meeting.getMeetingId())
|
||||
.title(meeting.getTitle())
|
||||
.description(meeting.getDescription())
|
||||
.scheduledAt(meeting.getScheduledAt())
|
||||
.startedAt(meeting.getStartedAt())
|
||||
.endedAt(meeting.getEndedAt())
|
||||
.status(meeting.getStatus())
|
||||
.organizerId(meeting.getOrganizerId())
|
||||
.participants(formatParticipants(meeting.getParticipants()))
|
||||
.templateId(meeting.getTemplateId())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void start() {
|
||||
this.status = "IN_PROGRESS";
|
||||
this.startedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void end() {
|
||||
this.status = "COMPLETED";
|
||||
this.endedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
private static List<String> parseParticipants(String participants) {
|
||||
if (participants == null || participants.isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
return Arrays.asList(participants.split(","));
|
||||
}
|
||||
|
||||
private static String formatParticipants(List<String> participants) {
|
||||
if (participants == null || participants.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return String.join(",", participants);
|
||||
}
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.entity;
|
||||
|
||||
import com.unicorn.hgzero.common.entity.BaseTimeEntity;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Minutes;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* 회의록 Entity
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "minutes")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MinutesEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "minutes_id", length = 50)
|
||||
private String minutesId;
|
||||
|
||||
@Column(name = "meeting_id", length = 50, nullable = false)
|
||||
private String meetingId;
|
||||
|
||||
@Column(name = "title", length = 200, nullable = false)
|
||||
private String title;
|
||||
|
||||
@OneToMany(mappedBy = "minutes", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
@OrderBy("order ASC")
|
||||
private List<MinutesSectionEntity> sections = new ArrayList<>();
|
||||
|
||||
@Column(name = "status", length = 20, nullable = false)
|
||||
@Builder.Default
|
||||
private String status = "DRAFT";
|
||||
|
||||
@Column(name = "version", nullable = false)
|
||||
@Builder.Default
|
||||
private Integer version = 1;
|
||||
|
||||
@Column(name = "created_by", length = 50, nullable = false)
|
||||
private String createdBy;
|
||||
|
||||
@Column(name = "finalized_by", length = 50)
|
||||
private String finalizedBy;
|
||||
|
||||
@Column(name = "finalized_at")
|
||||
private LocalDateTime finalizedAt;
|
||||
|
||||
public Minutes toDomain() {
|
||||
return Minutes.builder()
|
||||
.minutesId(this.minutesId)
|
||||
.meetingId(this.meetingId)
|
||||
.title(this.title)
|
||||
.sections(this.sections.stream()
|
||||
.map(MinutesSectionEntity::toDomain)
|
||||
.collect(Collectors.toList()))
|
||||
.status(this.status)
|
||||
.version(this.version)
|
||||
.createdBy(this.createdBy)
|
||||
.finalizedBy(this.finalizedBy)
|
||||
.finalizedAt(this.finalizedAt)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static MinutesEntity fromDomain(Minutes minutes) {
|
||||
return MinutesEntity.builder()
|
||||
.minutesId(minutes.getMinutesId())
|
||||
.meetingId(minutes.getMeetingId())
|
||||
.title(minutes.getTitle())
|
||||
.status(minutes.getStatus())
|
||||
.version(minutes.getVersion())
|
||||
.createdBy(minutes.getCreatedBy())
|
||||
.finalizedBy(minutes.getFinalizedBy())
|
||||
.finalizedAt(minutes.getFinalizedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void finalize(String userId) {
|
||||
this.status = "FINALIZED";
|
||||
this.finalizedBy = userId;
|
||||
this.finalizedAt = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public void updateVersion() {
|
||||
this.version++;
|
||||
}
|
||||
}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.entity;
|
||||
|
||||
import com.unicorn.hgzero.common.entity.BaseTimeEntity;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.MinutesSection;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 Entity
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "minutes_sections")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class MinutesSectionEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "section_id", length = 50)
|
||||
private String sectionId;
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "minutes_id", nullable = false)
|
||||
private MinutesEntity minutes;
|
||||
|
||||
@Column(name = "minutes_id", insertable = false, updatable = false)
|
||||
private String minutesId;
|
||||
|
||||
@Column(name = "type", length = 50, nullable = false)
|
||||
private String type;
|
||||
|
||||
@Column(name = "title", length = 200, nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(name = "content", columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
@Column(name = "order", nullable = false)
|
||||
private Integer order;
|
||||
|
||||
@Column(name = "verified", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean verified = false;
|
||||
|
||||
@Column(name = "locked", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean locked = false;
|
||||
|
||||
@Column(name = "locked_by", length = 50)
|
||||
private String lockedBy;
|
||||
|
||||
public MinutesSection toDomain() {
|
||||
return MinutesSection.builder()
|
||||
.sectionId(this.sectionId)
|
||||
.minutesId(this.minutesId)
|
||||
.type(this.type)
|
||||
.title(this.title)
|
||||
.content(this.content)
|
||||
.order(this.order)
|
||||
.verified(this.verified)
|
||||
.locked(this.locked)
|
||||
.lockedBy(this.lockedBy)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static MinutesSectionEntity fromDomain(MinutesSection section) {
|
||||
return MinutesSectionEntity.builder()
|
||||
.sectionId(section.getSectionId())
|
||||
.minutesId(section.getMinutesId())
|
||||
.type(section.getType())
|
||||
.title(section.getTitle())
|
||||
.content(section.getContent())
|
||||
.order(section.getOrder())
|
||||
.verified(section.getVerified())
|
||||
.locked(section.getLocked())
|
||||
.lockedBy(section.getLockedBy())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void lock(String userId) {
|
||||
this.locked = true;
|
||||
this.lockedBy = userId;
|
||||
}
|
||||
|
||||
public void unlock() {
|
||||
this.locked = false;
|
||||
this.lockedBy = null;
|
||||
}
|
||||
|
||||
public void verify() {
|
||||
this.verified = true;
|
||||
}
|
||||
}
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
package com.unicorn.hgzero.meeting.biz.domain;
|
||||
|
||||
import com.unicorn.hgzero.common.entity.BaseTimeEntity;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* 템플릿 Entity
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "templates")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TemplateEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "template_id", length = 50)
|
||||
private String templateId;
|
||||
|
||||
@Column(name = "name", length = 200, nullable = false)
|
||||
private String name;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "category", length = 50, nullable = false)
|
||||
private String category;
|
||||
|
||||
@Column(name = "sections", columnDefinition = "TEXT")
|
||||
private String sections;
|
||||
|
||||
@Column(name = "is_public", nullable = false)
|
||||
@Builder.Default
|
||||
private Boolean isPublic = true;
|
||||
|
||||
@Column(name = "created_by", length = 50, nullable = false)
|
||||
private String createdBy;
|
||||
|
||||
public Template toDomain() {
|
||||
return Template.builder()
|
||||
.templateId(this.templateId)
|
||||
.name(this.name)
|
||||
.description(this.description)
|
||||
.category(this.category)
|
||||
.isPublic(this.isPublic)
|
||||
.createdBy(this.createdBy)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static TemplateEntity fromDomain(Template template) {
|
||||
return TemplateEntity.builder()
|
||||
.templateId(template.getTemplateId())
|
||||
.name(template.getName())
|
||||
.description(template.getDescription())
|
||||
.category(template.getCategory())
|
||||
.isPublic(template.getIsPublic())
|
||||
.createdBy(template.getCreatedBy())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.entity;
|
||||
|
||||
import com.unicorn.hgzero.common.entity.BaseTimeEntity;
|
||||
import com.unicorn.hgzero.meeting.biz.domain.Todo;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* Todo Entity
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "todos")
|
||||
@Getter
|
||||
@Builder
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class TodoEntity extends BaseTimeEntity {
|
||||
|
||||
@Id
|
||||
@Column(name = "todo_id", length = 50)
|
||||
private String todoId;
|
||||
|
||||
@Column(name = "minutes_id", length = 50)
|
||||
private String minutesId;
|
||||
|
||||
@Column(name = "meeting_id", length = 50, nullable = false)
|
||||
private String meetingId;
|
||||
|
||||
@Column(name = "title", length = 200, nullable = false)
|
||||
private String title;
|
||||
|
||||
@Column(name = "description", columnDefinition = "TEXT")
|
||||
private String description;
|
||||
|
||||
@Column(name = "assignee_id", length = 50, nullable = false)
|
||||
private String assigneeId;
|
||||
|
||||
@Column(name = "due_date")
|
||||
private LocalDate dueDate;
|
||||
|
||||
@Column(name = "status", length = 20, nullable = false)
|
||||
@Builder.Default
|
||||
private String status = "PENDING";
|
||||
|
||||
@Column(name = "priority", length = 20)
|
||||
@Builder.Default
|
||||
private String priority = "MEDIUM";
|
||||
|
||||
@Column(name = "completed_at")
|
||||
private LocalDateTime completedAt;
|
||||
|
||||
public Todo toDomain() {
|
||||
return Todo.builder()
|
||||
.todoId(this.todoId)
|
||||
.minutesId(this.minutesId)
|
||||
.meetingId(this.meetingId)
|
||||
.title(this.title)
|
||||
.description(this.description)
|
||||
.assigneeId(this.assigneeId)
|
||||
.dueDate(this.dueDate)
|
||||
.status(this.status)
|
||||
.priority(this.priority)
|
||||
.completedAt(this.completedAt)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static TodoEntity fromDomain(Todo todo) {
|
||||
return TodoEntity.builder()
|
||||
.todoId(todo.getTodoId())
|
||||
.minutesId(todo.getMinutesId())
|
||||
.meetingId(todo.getMeetingId())
|
||||
.title(todo.getTitle())
|
||||
.description(todo.getDescription())
|
||||
.assigneeId(todo.getAssigneeId())
|
||||
.dueDate(todo.getDueDate())
|
||||
.status(todo.getStatus())
|
||||
.priority(todo.getPriority())
|
||||
.completedAt(todo.getCompletedAt())
|
||||
.build();
|
||||
}
|
||||
|
||||
public void complete() {
|
||||
this.status = "COMPLETED";
|
||||
this.completedAt = LocalDateTime.now();
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
||||
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MeetingEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의 JPA Repository
|
||||
*/
|
||||
@Repository
|
||||
public interface MeetingJpaRepository extends JpaRepository<MeetingEntity, String> {
|
||||
|
||||
/**
|
||||
* 주최자 ID로 회의 목록 조회
|
||||
*/
|
||||
List<MeetingEntity> findByOrganizerId(String organizerId);
|
||||
|
||||
/**
|
||||
* 상태로 회의 목록 조회
|
||||
*/
|
||||
List<MeetingEntity> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 주최자 ID와 상태로 회의 목록 조회
|
||||
*/
|
||||
List<MeetingEntity> findByOrganizerIdAndStatus(String organizerId, String status);
|
||||
|
||||
/**
|
||||
* 일정 시간 범위로 회의 목록 조회
|
||||
*/
|
||||
List<MeetingEntity> findByScheduledAtBetween(LocalDateTime startTime, LocalDateTime endTime);
|
||||
|
||||
/**
|
||||
* 템플릿 ID로 회의 목록 조회
|
||||
*/
|
||||
List<MeetingEntity> findByTemplateId(String templateId);
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
||||
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* 회의록 JPA Repository
|
||||
*/
|
||||
@Repository
|
||||
public interface MinutesJpaRepository extends JpaRepository<MinutesEntity, String> {
|
||||
|
||||
/**
|
||||
* 회의 ID로 회의록 조회
|
||||
*/
|
||||
List<MinutesEntity> findByMeetingId(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의 ID로 최신 회의록 조회
|
||||
*/
|
||||
Optional<MinutesEntity> findFirstByMeetingIdOrderByVersionDesc(String meetingId);
|
||||
|
||||
/**
|
||||
* 상태로 회의록 목록 조회
|
||||
*/
|
||||
List<MinutesEntity> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 작성자 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<MinutesEntity> findByCreatedBy(String createdBy);
|
||||
|
||||
/**
|
||||
* 확정자 ID로 회의록 목록 조회
|
||||
*/
|
||||
List<MinutesEntity> findByFinalizedBy(String finalizedBy);
|
||||
|
||||
/**
|
||||
* 회의 ID와 버전으로 회의록 조회
|
||||
*/
|
||||
Optional<MinutesEntity> findByMeetingIdAndVersion(String meetingId, Integer version);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
||||
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.MinutesSectionEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 회의록 섹션 JPA Repository
|
||||
*/
|
||||
@Repository
|
||||
public interface MinutesSectionJpaRepository extends JpaRepository<MinutesSectionEntity, String> {
|
||||
|
||||
/**
|
||||
* 회의록 ID로 섹션 목록 조회 (순서대로)
|
||||
*/
|
||||
List<MinutesSectionEntity> findByMinutesIdOrderByOrderAsc(String minutesId);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 타입으로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSectionEntity> findByMinutesIdAndType(String minutesId, String type);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 검증 여부로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSectionEntity> findByMinutesIdAndVerified(String minutesId, Boolean verified);
|
||||
|
||||
/**
|
||||
* 회의록 ID와 잠금 여부로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSectionEntity> findByMinutesIdAndLocked(String minutesId, Boolean locked);
|
||||
|
||||
/**
|
||||
* 잠금한 사용자 ID로 섹션 목록 조회
|
||||
*/
|
||||
List<MinutesSectionEntity> findByLockedBy(String lockedBy);
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
||||
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.TemplateEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 템플릿 JPA Repository
|
||||
*/
|
||||
@Repository
|
||||
public interface TemplateJpaRepository extends JpaRepository<TemplateEntity, String> {
|
||||
|
||||
/**
|
||||
* 카테고리로 템플릿 목록 조회
|
||||
*/
|
||||
List<TemplateEntity> findByCategory(String category);
|
||||
|
||||
/**
|
||||
* 공개 여부로 템플릿 목록 조회
|
||||
*/
|
||||
List<TemplateEntity> findByIsPublic(Boolean isPublic);
|
||||
|
||||
/**
|
||||
* 작성자 ID로 템플릿 목록 조회
|
||||
*/
|
||||
List<TemplateEntity> findByCreatedBy(String createdBy);
|
||||
|
||||
/**
|
||||
* 카테고리와 공개 여부로 템플릿 목록 조회
|
||||
*/
|
||||
List<TemplateEntity> findByCategoryAndIsPublic(String category, Boolean isPublic);
|
||||
|
||||
/**
|
||||
* 이름으로 템플릿 검색 (부분 일치)
|
||||
*/
|
||||
List<TemplateEntity> findByNameContaining(String name);
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.unicorn.hgzero.meeting.infra.gateway.repository;
|
||||
|
||||
import com.unicorn.hgzero.meeting.infra.gateway.entity.TodoEntity;
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Todo JPA Repository
|
||||
*/
|
||||
@Repository
|
||||
public interface TodoJpaRepository extends JpaRepository<TodoEntity, String> {
|
||||
|
||||
/**
|
||||
* 회의 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByMeetingId(String meetingId);
|
||||
|
||||
/**
|
||||
* 회의록 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByMinutesId(String minutesId);
|
||||
|
||||
/**
|
||||
* 담당자 ID로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByAssigneeId(String assigneeId);
|
||||
|
||||
/**
|
||||
* 상태로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByStatus(String status);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 상태로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByAssigneeIdAndStatus(String assigneeId, String status);
|
||||
|
||||
/**
|
||||
* 마감일로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByDueDate(LocalDate dueDate);
|
||||
|
||||
/**
|
||||
* 마감일 이전 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByDueDateBefore(LocalDate date);
|
||||
|
||||
/**
|
||||
* 우선순위로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByPriority(String priority);
|
||||
|
||||
/**
|
||||
* 담당자 ID와 마감일 범위로 Todo 목록 조회
|
||||
*/
|
||||
List<TodoEntity> findByAssigneeIdAndDueDateBetween(String assigneeId, LocalDate startDate, LocalDate endDate);
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
spring:
|
||||
application:
|
||||
name: meeting
|
||||
|
||||
# Database Configuration
|
||||
datasource:
|
||||
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
|
||||
username: ${DB_USERNAME:hgzerouser}
|
||||
password: ${DB_PASSWORD:}
|
||||
driver-class-name: org.postgresql.Driver
|
||||
hikari:
|
||||
maximum-pool-size: 20
|
||||
minimum-idle: 5
|
||||
connection-timeout: 30000
|
||||
idle-timeout: 600000
|
||||
max-lifetime: 1800000
|
||||
leak-detection-threshold: 60000
|
||||
|
||||
# JPA Configuration
|
||||
jpa:
|
||||
show-sql: ${SHOW_SQL:true}
|
||||
properties:
|
||||
hibernate:
|
||||
format_sql: true
|
||||
use_sql_comments: true
|
||||
hibernate:
|
||||
ddl-auto: ${DDL_AUTO:update}
|
||||
|
||||
# Redis Configuration
|
||||
data:
|
||||
redis:
|
||||
host: ${REDIS_HOST:20.249.177.114}
|
||||
port: ${REDIS_PORT:6379}
|
||||
password: ${REDIS_PASSWORD:}
|
||||
timeout: 2000ms
|
||||
lettuce:
|
||||
pool:
|
||||
max-active: 8
|
||||
max-idle: 8
|
||||
min-idle: 0
|
||||
max-wait: -1ms
|
||||
database: ${REDIS_DATABASE:1}
|
||||
|
||||
# Server Configuration
|
||||
server:
|
||||
port: ${SERVER_PORT:8081}
|
||||
|
||||
# JWT Configuration
|
||||
jwt:
|
||||
secret: ${JWT_SECRET:}
|
||||
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
|
||||
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
|
||||
|
||||
# CORS Configuration
|
||||
cors:
|
||||
allowed-origins: ${CORS_ALLOWED_ORIGINS:http://localhost:*}
|
||||
|
||||
# Actuator Configuration
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,metrics,prometheus
|
||||
base-path: /actuator
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
show-components: always
|
||||
health:
|
||||
livenessState:
|
||||
enabled: true
|
||||
readinessState:
|
||||
enabled: true
|
||||
|
||||
# OpenAPI Documentation
|
||||
springdoc:
|
||||
api-docs:
|
||||
path: /v3/api-docs
|
||||
swagger-ui:
|
||||
path: /swagger-ui.html
|
||||
tags-sorter: alpha
|
||||
operations-sorter: alpha
|
||||
show-actuator: false
|
||||
|
||||
# Logging Configuration
|
||||
logging:
|
||||
level:
|
||||
com.unicorn.hgzero.meeting: ${LOG_LEVEL_APP:DEBUG}
|
||||
org.springframework.web: ${LOG_LEVEL_WEB:INFO}
|
||||
org.springframework.security: ${LOG_LEVEL_SECURITY:DEBUG}
|
||||
org.springframework.websocket: ${LOG_LEVEL_WEBSOCKET:DEBUG}
|
||||
org.hibernate.SQL: ${LOG_LEVEL_SQL:DEBUG}
|
||||
org.hibernate.type: ${LOG_LEVEL_SQL_TYPE:TRACE}
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} - %msg%n"
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file:
|
||||
name: ${LOG_FILE_PATH:logs/meeting.log}
|
||||
Reference in New Issue
Block a user