mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 12:36:23 +00:00
feat: redis 환경설정 및 회의예약 API 개발
This commit is contained in:
parent
75feb8493a
commit
65dabd730d
File diff suppressed because it is too large
Load Diff
1
meeting/logs/meeting-start.log
Normal file
1
meeting/logs/meeting-start.log
Normal file
@ -0,0 +1 @@
|
|||||||
|
nohup: ./gradlew: No such file or directory
|
||||||
@ -6,6 +6,8 @@ import com.unicorn.hgzero.meeting.biz.domain.Meeting;
|
|||||||
import com.unicorn.hgzero.meeting.biz.usecase.in.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.MeetingReader;
|
||||||
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
|
import com.unicorn.hgzero.meeting.biz.usecase.out.MeetingWriter;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.cache.CacheService;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.event.publisher.EventPublisher;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -31,6 +33,8 @@ public class MeetingService implements
|
|||||||
|
|
||||||
private final MeetingReader meetingReader;
|
private final MeetingReader meetingReader;
|
||||||
private final MeetingWriter meetingWriter;
|
private final MeetingWriter meetingWriter;
|
||||||
|
private final CacheService cacheService;
|
||||||
|
private final EventPublisher eventPublisher;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 회의 생성
|
* 회의 생성
|
||||||
@ -40,10 +44,31 @@ public class MeetingService implements
|
|||||||
public Meeting createMeeting(CreateMeetingCommand command) {
|
public Meeting createMeeting(CreateMeetingCommand command) {
|
||||||
log.info("Creating meeting: {}", command.title());
|
log.info("Creating meeting: {}", command.title());
|
||||||
|
|
||||||
// 회의 ID 생성
|
// 1. 회의 시간 유효성 검사
|
||||||
|
if (command.scheduledAt().isAfter(command.endTime()) ||
|
||||||
|
command.scheduledAt().isEqual(command.endTime())) {
|
||||||
|
log.warn("Invalid meeting time: start={}, end={}",
|
||||||
|
command.scheduledAt(), command.endTime());
|
||||||
|
throw new BusinessException(ErrorCode.INVALID_MEETING_TIME);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 중복 회의 체크
|
||||||
|
long conflictCount = meetingReader.countConflictingMeetings(
|
||||||
|
command.organizerId(),
|
||||||
|
command.scheduledAt(),
|
||||||
|
command.endTime()
|
||||||
|
);
|
||||||
|
|
||||||
|
if (conflictCount > 0) {
|
||||||
|
log.warn("Meeting time conflict detected: organizerId={}, count={}",
|
||||||
|
command.organizerId(), conflictCount);
|
||||||
|
throw new BusinessException(ErrorCode.MEETING_TIME_CONFLICT);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 회의 ID 생성
|
||||||
String meetingId = UUID.randomUUID().toString();
|
String meetingId = UUID.randomUUID().toString();
|
||||||
|
|
||||||
// 회의 도메인 객체 생성
|
// 4. 회의 도메인 객체 생성
|
||||||
Meeting meeting = Meeting.builder()
|
Meeting meeting = Meeting.builder()
|
||||||
.meetingId(meetingId)
|
.meetingId(meetingId)
|
||||||
.title(command.title())
|
.title(command.title())
|
||||||
@ -57,9 +82,36 @@ public class MeetingService implements
|
|||||||
.templateId(command.templateId())
|
.templateId(command.templateId())
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// 회의 저장
|
// 5. 회의 저장
|
||||||
Meeting savedMeeting = meetingWriter.save(meeting);
|
Meeting savedMeeting = meetingWriter.save(meeting);
|
||||||
|
|
||||||
|
// 6. 캐시 저장 (TTL: 10분)
|
||||||
|
try {
|
||||||
|
cacheService.cacheMeeting(meetingId, savedMeeting, 600);
|
||||||
|
log.debug("Meeting cached: meetingId={}", meetingId);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.warn("Failed to cache meeting: meetingId={}", meetingId, e);
|
||||||
|
// 캐시 실패는 비즈니스 로직에 영향을 주지 않으므로 계속 진행
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 참석자 초대 이벤트 발행 (비동기)
|
||||||
|
try {
|
||||||
|
eventPublisher.publishMeetingCreated(
|
||||||
|
meetingId,
|
||||||
|
command.title(),
|
||||||
|
command.scheduledAt(),
|
||||||
|
command.location(),
|
||||||
|
command.participants(),
|
||||||
|
command.organizerId(),
|
||||||
|
command.organizerId() // organizerName은 나중에 User 서비스 연동 시 개선
|
||||||
|
);
|
||||||
|
log.debug("Meeting invitation events published: meetingId={}, participants={}",
|
||||||
|
meetingId, command.participants().size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Failed to publish meeting invitation events: meetingId={}", meetingId, e);
|
||||||
|
// 이벤트 발행 실패는 비즈니스 로직에 영향을 주지 않으므로 계속 진행
|
||||||
|
}
|
||||||
|
|
||||||
log.info("Meeting created successfully: {}", savedMeeting.getMeetingId());
|
log.info("Meeting created successfully: {}", savedMeeting.getMeetingId());
|
||||||
return savedMeeting;
|
return savedMeeting;
|
||||||
}
|
}
|
||||||
@ -72,21 +124,31 @@ public class MeetingService implements
|
|||||||
public Meeting startMeeting(String meetingId) {
|
public Meeting startMeeting(String meetingId) {
|
||||||
log.info("Starting meeting: {}", meetingId);
|
log.info("Starting meeting: {}", meetingId);
|
||||||
|
|
||||||
|
// Redis 캐시 조회 기능 필요
|
||||||
|
|
||||||
// 회의 조회
|
// 회의 조회
|
||||||
Meeting meeting = meetingReader.findById(meetingId)
|
Meeting meeting = meetingReader.findById(meetingId)
|
||||||
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
.orElseThrow(() -> new BusinessException(ErrorCode.ENTITY_NOT_FOUND));
|
||||||
|
|
||||||
|
// 권한 검증 (생성자 or 참석자)
|
||||||
|
|
||||||
// 회의 상태 검증
|
// 회의 상태 검증
|
||||||
if (!"SCHEDULED".equals(meeting.getStatus())) {
|
if (!"SCHEDULED".equals(meeting.getStatus())) {
|
||||||
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
throw new BusinessException(ErrorCode.INVALID_INPUT_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 세션 생성 기능 필요
|
||||||
|
|
||||||
// 회의 시작
|
// 회의 시작
|
||||||
meeting.start();
|
meeting.start();
|
||||||
|
|
||||||
// 저장
|
// 저장
|
||||||
Meeting updatedMeeting = meetingWriter.save(meeting);
|
Meeting updatedMeeting = meetingWriter.save(meeting);
|
||||||
|
|
||||||
|
// 회의록 초안 생성 필요
|
||||||
|
|
||||||
|
// 이벤트 발행 필요
|
||||||
|
|
||||||
log.info("Meeting started successfully: {}", meetingId);
|
log.info("Meeting started successfully: {}", meetingId);
|
||||||
return updatedMeeting;
|
return updatedMeeting;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -40,4 +40,14 @@ public interface MeetingReader {
|
|||||||
* 템플릿 ID로 회의 목록 조회
|
* 템플릿 ID로 회의 목록 조회
|
||||||
*/
|
*/
|
||||||
List<Meeting> findByTemplateId(String templateId);
|
List<Meeting> findByTemplateId(String templateId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주최자의 특정 시간대 중복 회의 개수 조회
|
||||||
|
*
|
||||||
|
* @param organizerId 주최자 ID
|
||||||
|
* @param startTime 회의 시작 시간
|
||||||
|
* @param endTime 회의 종료 시간
|
||||||
|
* @return 중복 회의 개수
|
||||||
|
*/
|
||||||
|
long countConflictingMeetings(String organizerId, LocalDateTime startTime, LocalDateTime endTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,9 @@ public class CacheConfig {
|
|||||||
@Value("${spring.data.redis.port:6379}")
|
@Value("${spring.data.redis.port:6379}")
|
||||||
private int redisPort;
|
private int redisPort;
|
||||||
|
|
||||||
|
@Value("${spring.data.redis.password:}")
|
||||||
|
private String redisPassword;
|
||||||
|
|
||||||
@Value("${spring.data.redis.database:1}")
|
@Value("${spring.data.redis.database:1}")
|
||||||
private int database;
|
private int database;
|
||||||
|
|
||||||
@ -33,7 +36,15 @@ public class CacheConfig {
|
|||||||
public RedisConnectionFactory redisConnectionFactory() {
|
public RedisConnectionFactory redisConnectionFactory() {
|
||||||
var factory = new LettuceConnectionFactory(redisHost, redisPort);
|
var factory = new LettuceConnectionFactory(redisHost, redisPort);
|
||||||
factory.setDatabase(database);
|
factory.setDatabase(database);
|
||||||
log.info("Redis 연결 설정 - host: {}, port: {}, database: {}", redisHost, redisPort, database);
|
|
||||||
|
// 비밀번호가 설정된 경우에만 적용
|
||||||
|
if (redisPassword != null && !redisPassword.isEmpty()) {
|
||||||
|
factory.setPassword(redisPassword);
|
||||||
|
log.info("Redis 연결 설정 - host: {}, port: {}, database: {}, password: ****", redisHost, redisPort, database);
|
||||||
|
} else {
|
||||||
|
log.info("Redis 연결 설정 - host: {}, port: {}, database: {}", redisHost, redisPort, database);
|
||||||
|
}
|
||||||
|
|
||||||
return factory;
|
return factory;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.azure.messaging.eventhubs.EventHubProducerClient;
|
|||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
@ -14,6 +15,7 @@ import org.springframework.context.annotation.Configuration;
|
|||||||
*/
|
*/
|
||||||
@Configuration
|
@Configuration
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@org.springframework.boot.autoconfigure.condition.ConditionalOnExpression("'${eventhub.connection-string:}'.length() > 0")
|
||||||
public class EventHubConfig {
|
public class EventHubConfig {
|
||||||
|
|
||||||
@Value("${eventhub.connection-string}")
|
@Value("${eventhub.connection-string}")
|
||||||
|
|||||||
@ -142,6 +142,8 @@ public class MeetingController {
|
|||||||
@RequestHeader("X-User-Email") String userEmail) {
|
@RequestHeader("X-User-Email") String userEmail) {
|
||||||
|
|
||||||
log.info("회의 시작 요청 - meetingId: {}, userId: {}", meetingId, userId);
|
log.info("회의 시작 요청 - meetingId: {}, userId: {}", meetingId, userId);
|
||||||
|
|
||||||
|
// meeting id 유효성 검증 필요
|
||||||
|
|
||||||
var sessionData = startMeetingUseCase.startMeeting(meetingId);
|
var sessionData = startMeetingUseCase.startMeeting(meetingId);
|
||||||
var response = SessionResponse.from(sessionData);
|
var response = SessionResponse.from(sessionData);
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import java.util.List;
|
|||||||
public class CreateMeetingRequest {
|
public class CreateMeetingRequest {
|
||||||
|
|
||||||
@NotBlank(message = "회의 제목은 필수입니다")
|
@NotBlank(message = "회의 제목은 필수입니다")
|
||||||
|
@jakarta.validation.constraints.Size(max = 100, message = "회의 제목은 100자를 초과할 수 없습니다")
|
||||||
@Schema(description = "회의 제목", example = "Q1 전략 회의", required = true)
|
@Schema(description = "회의 제목", example = "Q1 전략 회의", required = true)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import org.springframework.stereotype.Component;
|
|||||||
*/
|
*/
|
||||||
@Component
|
@Component
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@org.springframework.boot.autoconfigure.condition.ConditionalOnBean(name = "eventProducer")
|
||||||
public class EventHubPublisher implements EventPublisher {
|
public class EventHubPublisher implements EventPublisher {
|
||||||
|
|
||||||
private final EventHubProducerClient eventProducer;
|
private final EventHubProducerClient eventProducer;
|
||||||
@ -120,6 +121,34 @@ public class EventHubPublisher implements EventPublisher {
|
|||||||
EventHubConstants.EVENT_TYPE_MINUTES_FINALIZED);
|
EventHubConstants.EVENT_TYPE_MINUTES_FINALIZED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMeetingCreated(String meetingId, String title, LocalDateTime startTime,
|
||||||
|
String location, java.util.List<String> participants,
|
||||||
|
String organizerId, String organizerName) {
|
||||||
|
// 각 참석자에게 개별 알림 이벤트 발행
|
||||||
|
for (String participantEmail : participants) {
|
||||||
|
NotificationRequestEvent event = NotificationRequestEvent.builder()
|
||||||
|
.notificationType("MEETING_INVITATION")
|
||||||
|
.recipientEmail(participantEmail)
|
||||||
|
.recipientId(participantEmail)
|
||||||
|
.recipientName(participantEmail)
|
||||||
|
.title("회의 초대")
|
||||||
|
.message(String.format("'%s' 회의에 초대되었습니다. 일시: %s, 장소: %s",
|
||||||
|
title, startTime, location))
|
||||||
|
.relatedEntityId(meetingId)
|
||||||
|
.relatedEntityType("MEETING")
|
||||||
|
.requestedBy(organizerId)
|
||||||
|
.requestedByName(organizerName)
|
||||||
|
.eventTime(LocalDateTime.now())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
publishNotificationRequest(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("회의 생성 알림 발행 완료 - meetingId: {}, participants count: {}",
|
||||||
|
meetingId, participants.size());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 이벤트 발행 공통 메서드
|
* 이벤트 발행 공통 메서드
|
||||||
*
|
*
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
|
|||||||
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
|
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 이벤트 발행 인터페이스
|
* 이벤트 발행 인터페이스
|
||||||
@ -59,4 +60,19 @@ public interface EventPublisher {
|
|||||||
* 회의록 확정 이벤트 발행 (편의 메서드)
|
* 회의록 확정 이벤트 발행 (편의 메서드)
|
||||||
*/
|
*/
|
||||||
void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName);
|
void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 회의 생성 알림 발행 (편의 메서드)
|
||||||
|
* 참석자에게 회의 초대 이메일 발송
|
||||||
|
*
|
||||||
|
* @param meetingId 회의 ID
|
||||||
|
* @param title 회의 제목
|
||||||
|
* @param startTime 회의 시작 시간
|
||||||
|
* @param location 회의 장소
|
||||||
|
* @param participants 참석자 이메일 목록
|
||||||
|
* @param organizerId 주최자 ID
|
||||||
|
* @param organizerName 주최자 이름
|
||||||
|
*/
|
||||||
|
void publishMeetingCreated(String meetingId, String title, LocalDateTime startTime,
|
||||||
|
String location, List<String> participants, String organizerId, String organizerName);
|
||||||
}
|
}
|
||||||
@ -0,0 +1,69 @@
|
|||||||
|
package com.unicorn.hgzero.meeting.infra.event.publisher;
|
||||||
|
|
||||||
|
import com.unicorn.hgzero.meeting.infra.event.dto.MeetingStartedEvent;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.event.dto.MeetingEndedEvent;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.event.dto.TodoAssignedEvent;
|
||||||
|
import com.unicorn.hgzero.meeting.infra.event.dto.NotificationRequestEvent;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||||
|
import org.springframework.context.annotation.Primary;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* No-Op EventPublisher 구현체
|
||||||
|
* EventHub가 설정되지 않은 경우 사용되는 더미 구현체
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
@Primary
|
||||||
|
@ConditionalOnMissingBean(name = "eventProducer")
|
||||||
|
@Slf4j
|
||||||
|
public class NoOpEventPublisher implements EventPublisher {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMeetingStarted(MeetingStartedEvent event) {
|
||||||
|
log.debug("[NoOp] Meeting started event: {}", event.getMeetingId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMeetingEnded(MeetingEndedEvent event) {
|
||||||
|
log.debug("[NoOp] Meeting ended event: {}", event.getMeetingId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishTodoAssigned(TodoAssignedEvent event) {
|
||||||
|
log.debug("[NoOp] Todo assigned event: {}", event.getTodoId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishNotificationRequest(NotificationRequestEvent event) {
|
||||||
|
log.debug("[NoOp] Notification request event: {}", event.getRecipientId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishTodoAssigned(String todoId, String title, String assigneeId, String assigneeName,
|
||||||
|
String assignedBy, String assignedByName, LocalDate dueDate) {
|
||||||
|
log.debug("[NoOp] Todo assigned: todoId={}, title={}", todoId, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishTodoCompleted(String todoId, String title, String assigneeId, String assigneeName,
|
||||||
|
String completedBy, String completedByName) {
|
||||||
|
log.debug("[NoOp] Todo completed: todoId={}, title={}", todoId, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMinutesFinalized(String minutesId, String title, String finalizedBy, String finalizedByName) {
|
||||||
|
log.debug("[NoOp] Minutes finalized: minutesId={}, title={}", minutesId, title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void publishMeetingCreated(String meetingId, String title, LocalDateTime startTime,
|
||||||
|
String location, List<String> participants, String organizerId, String organizerName) {
|
||||||
|
log.debug("[NoOp] Meeting created: meetingId={}, title={}, participants={}",
|
||||||
|
meetingId, title, participants.size());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -77,4 +77,9 @@ public class MeetingGateway implements MeetingReader, MeetingWriter {
|
|||||||
public void delete(String meetingId) {
|
public void delete(String meetingId) {
|
||||||
meetingJpaRepository.deleteById(meetingId);
|
meetingJpaRepository.deleteById(meetingId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long countConflictingMeetings(String organizerId, LocalDateTime startTime, LocalDateTime endTime) {
|
||||||
|
return meetingJpaRepository.countConflictingMeetings(organizerId, startTime, endTime);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,4 +37,26 @@ public interface MeetingJpaRepository extends JpaRepository<MeetingEntity, Strin
|
|||||||
* 템플릿 ID로 회의 목록 조회
|
* 템플릿 ID로 회의 목록 조회
|
||||||
*/
|
*/
|
||||||
List<MeetingEntity> findByTemplateId(String templateId);
|
List<MeetingEntity> findByTemplateId(String templateId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 주최자의 특정 시간대 중복 회의 개수 조회
|
||||||
|
* 회의 상태가 SCHEDULED 또는 IN_PROGRESS이고,
|
||||||
|
* 시간이 겹치는 회의가 있는지 확인
|
||||||
|
*
|
||||||
|
* @param organizerId 주최자 ID
|
||||||
|
* @param startTime 회의 시작 시간
|
||||||
|
* @param endTime 회의 종료 시간
|
||||||
|
* @return 중복 회의 개수
|
||||||
|
*/
|
||||||
|
@org.springframework.data.jpa.repository.Query(
|
||||||
|
"SELECT COUNT(m) FROM MeetingEntity m WHERE " +
|
||||||
|
"m.organizerId = :organizerId AND " +
|
||||||
|
"m.status IN ('SCHEDULED', 'IN_PROGRESS') AND " +
|
||||||
|
"((m.scheduledAt < :endTime AND m.endTime > :startTime))"
|
||||||
|
)
|
||||||
|
long countConflictingMeetings(
|
||||||
|
@org.springframework.data.repository.query.Param("organizerId") String organizerId,
|
||||||
|
@org.springframework.data.repository.query.Param("startTime") LocalDateTime startTime,
|
||||||
|
@org.springframework.data.repository.query.Param("endTime") LocalDateTime endTime
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,7 +8,7 @@ spring:
|
|||||||
datasource:
|
datasource:
|
||||||
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
|
url: jdbc:${DB_KIND:postgresql}://${DB_HOST:4.230.48.72}:${DB_PORT:5432}/${DB_NAME:meetingdb}
|
||||||
username: ${DB_USERNAME:hgzerouser}
|
username: ${DB_USERNAME:hgzerouser}
|
||||||
password: ${DB_PASSWORD:}
|
password: ${DB_PASSWORD:Hi5Jessica!}
|
||||||
driver-class-name: org.postgresql.Driver
|
driver-class-name: org.postgresql.Driver
|
||||||
hikari:
|
hikari:
|
||||||
maximum-pool-size: 20
|
maximum-pool-size: 20
|
||||||
@ -35,7 +35,7 @@ spring:
|
|||||||
redis:
|
redis:
|
||||||
host: ${REDIS_HOST:20.249.177.114}
|
host: ${REDIS_HOST:20.249.177.114}
|
||||||
port: ${REDIS_PORT:6379}
|
port: ${REDIS_PORT:6379}
|
||||||
password: ${REDIS_PASSWORD:}
|
password: ${REDIS_PASSWORD:Hi5Jessica!}
|
||||||
timeout: 2000ms
|
timeout: 2000ms
|
||||||
lettuce:
|
lettuce:
|
||||||
pool:
|
pool:
|
||||||
@ -51,7 +51,7 @@ server:
|
|||||||
|
|
||||||
# JWT Configuration
|
# JWT Configuration
|
||||||
jwt:
|
jwt:
|
||||||
secret: ${JWT_SECRET:}
|
secret: ${JWT_SECRET:hgzero-jwt-secret-key-for-dev-environment-only-do-not-use-in-production-minimum-256-bits}
|
||||||
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
|
access-token-validity: ${JWT_ACCESS_TOKEN_VALIDITY:3600}
|
||||||
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
|
refresh-token-validity: ${JWT_REFRESH_TOKEN_VALIDITY:604800}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user