TriggerCheckServiceImpl.java
package com.deveagles.be15_deveagles_be.features.workflows.execution.infrastructure.service;
import com.deveagles.be15_deveagles_be.features.customers.query.dto.response.CustomerDetailResponse;
import com.deveagles.be15_deveagles_be.features.customers.query.service.CustomerQueryService;
import com.deveagles.be15_deveagles_be.features.workflows.command.domain.aggregate.Workflow;
import com.deveagles.be15_deveagles_be.features.workflows.command.domain.repository.WorkflowRepository;
import com.deveagles.be15_deveagles_be.features.workflows.command.domain.vo.TriggerConfig;
import com.deveagles.be15_deveagles_be.features.workflows.execution.application.service.TriggerCheckService;
import com.deveagles.be15_deveagles_be.features.workflows.execution.application.service.WorkflowExecutionService;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Optional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class TriggerCheckServiceImpl implements TriggerCheckService {
private final WorkflowRepository workflowRepository;
private final WorkflowExecutionService workflowExecutionService;
private final ObjectMapper objectMapper;
private final CustomerQueryService customerQueryService;
@EventListener
@Override
public void onCustomerVisit(CustomerVisitEvent event) {
log.debug("고객 방문 이벤트 처리: 고객 ID={}, 매장 ID={}", event.getCustomerId(), event.getShopId());
try {
checkVisitCycleTriggers(event);
checkBirthdayTriggers(event);
} catch (Exception e) {
log.error("고객 방문 트리거 처리 중 오류: 고객 ID={}, 오류={}", event.getCustomerId(), e.getMessage(), e);
}
}
@EventListener
@Override
public void onCustomerRegistration(CustomerRegistrationEvent event) {
log.debug("고객 등록 이벤트 처리: 고객 ID={}, 매장 ID={}", event.getCustomerId(), event.getShopId());
try {
List<Workflow> newCustomerWorkflows =
workflowRepository.findByTriggerTypeAndShopId("new-customer-followup", event.getShopId());
for (Workflow workflow : newCustomerWorkflows) {
if (workflow.canExecute()) {
log.info(
"신규 고객 팔로업 워크플로우 실행: 워크플로우 ID={}, 고객 ID={}", workflow.getId(), event.getCustomerId());
workflowExecutionService.executeTriggeredWorkflow(workflow, event.getCustomerId());
}
}
} catch (Exception e) {
log.error("고객 등록 트리거 처리 중 오류: 고객 ID={}, 오류={}", event.getCustomerId(), e.getMessage(), e);
}
}
@EventListener
@Override
public void onPaymentCompleted(PaymentCompletedEvent event) {
log.debug(
"결제 완료 이벤트 처리: 고객 ID={}, 매장 ID={}, 금액={}",
event.getCustomerId(),
event.getShopId(),
event.getAmount());
try {
checkAmountMilestoneTriggers(event);
} catch (Exception e) {
log.error("결제 완료 트리거 처리 중 오류: 고객 ID={}, 오류={}", event.getCustomerId(), e.getMessage(), e);
}
}
private void checkVisitCycleTriggers(CustomerVisitEvent event) {
List<Workflow> visitCycleWorkflows =
workflowRepository.findByTriggerTypeAndShopId("visit-cycle", event.getShopId());
for (Workflow workflow : visitCycleWorkflows) {
if (!workflow.canExecute()) continue;
try {
TriggerConfig triggerConfig = parseTriggerConfig(workflow.getTriggerConfig());
// 고객 정보 조회하여 실제 방문 패턴 확인
Optional<CustomerDetailResponse> customerOpt =
customerQueryService.getCustomerDetail(event.getCustomerId(), event.getShopId());
if (customerOpt.isEmpty()) {
log.warn("고객 정보를 찾을 수 없습니다. customerId: {}", event.getCustomerId());
continue;
}
CustomerDetailResponse customer = customerOpt.get();
boolean shouldTrigger = checkVisitCycle(customer, triggerConfig.getVisitCycleDays());
if (shouldTrigger) {
log.info(
"방문 주기 트리거 실행: 워크플로우 ID={}, 고객 ID={}, 방문 횟수={}회",
workflow.getId(),
event.getCustomerId(),
customer.getVisitCount());
workflowExecutionService.executeTriggeredWorkflow(workflow, event.getCustomerId());
}
} catch (Exception e) {
log.error("방문 주기 트리거 체크 중 오류: 워크플로우 ID={}, 오류={}", workflow.getId(), e.getMessage());
}
}
}
private void checkBirthdayTriggers(CustomerVisitEvent event) {
List<Workflow> birthdayWorkflows =
workflowRepository.findByTriggerTypeAndShopId("birthday", event.getShopId());
for (Workflow workflow : birthdayWorkflows) {
if (!workflow.canExecute()) continue;
try {
TriggerConfig triggerConfig = parseTriggerConfig(workflow.getTriggerConfig());
// 고객 정보 조회하여 생일 확인
Optional<CustomerDetailResponse> customerOpt =
customerQueryService.getCustomerDetail(event.getCustomerId(), event.getShopId());
if (customerOpt.isEmpty() || customerOpt.get().getBirthdate() == null) {
continue;
}
CustomerDetailResponse customer = customerOpt.get();
boolean shouldTrigger =
checkBirthdayApproaching(
customer.getBirthdate(), triggerConfig.getBirthdayDaysBefore());
if (shouldTrigger) {
log.info(
"생일 트리거 실행: 워크플로우 ID={}, 고객 ID={}, 생일={}",
workflow.getId(),
event.getCustomerId(),
customer.getBirthdate());
workflowExecutionService.executeTriggeredWorkflow(workflow, event.getCustomerId());
}
} catch (Exception e) {
log.error("생일 트리거 체크 중 오류: 워크플로우 ID={}, 오류={}", workflow.getId(), e.getMessage());
}
}
}
private void checkAmountMilestoneTriggers(PaymentCompletedEvent event) {
List<Workflow> amountWorkflows =
workflowRepository.findByTriggerTypeAndShopId("amount-milestone", event.getShopId());
for (Workflow workflow : amountWorkflows) {
if (!workflow.canExecute()) continue;
try {
TriggerConfig triggerConfig = parseTriggerConfig(workflow.getTriggerConfig());
// 고객 정보 조회하여 누적 매출 확인
Optional<CustomerDetailResponse> customerOpt =
customerQueryService.getCustomerDetail(event.getCustomerId(), event.getShopId());
if (customerOpt.isEmpty()) {
continue;
}
CustomerDetailResponse customer = customerOpt.get();
boolean shouldTrigger =
checkAmountMilestone(customer.getTotalRevenue(), triggerConfig.getAmountMilestone());
if (shouldTrigger) {
log.info(
"누적 금액 마일스톤 트리거 실행: 워크플로우 ID={}, 고객 ID={}, 누적매출={}원",
workflow.getId(),
event.getCustomerId(),
customer.getTotalRevenue());
workflowExecutionService.executeTriggeredWorkflow(workflow, event.getCustomerId());
}
} catch (Exception e) {
log.error("누적 금액 마일스톤 트리거 체크 중 오류: 워크플로우 ID={}, 오류={}", workflow.getId(), e.getMessage());
}
}
}
private boolean checkVisitCycle(CustomerDetailResponse customer, Integer cycleDays) {
if (cycleDays == null || customer.getRecentVisitDate() == null) {
return false;
}
// 최근 방문일로부터 설정된 주기가 지났는지 확인
long daysSinceLastVisit =
ChronoUnit.DAYS.between(customer.getRecentVisitDate(), LocalDate.now());
// 설정된 주기와 일치하거나 조금 지났을 때 트리거 (±1일 허용)
return Math.abs(daysSinceLastVisit - cycleDays) <= 1;
}
private boolean checkBirthdayApproaching(LocalDate birthdate, Integer daysBefore) {
if (daysBefore == null || birthdate == null) {
return false;
}
LocalDate today = LocalDate.now();
LocalDate thisYearBirthday = birthdate.withYear(today.getYear());
// 올해 생일이 이미 지났으면 내년 생일로 계산
if (thisYearBirthday.isBefore(today)) {
thisYearBirthday = thisYearBirthday.plusYears(1);
}
long daysUntilBirthday = ChronoUnit.DAYS.between(today, thisYearBirthday);
return daysUntilBirthday == daysBefore;
}
private boolean checkAmountMilestone(Integer totalRevenue, Long amountMilestone) {
if (amountMilestone == null || totalRevenue == null) {
return false;
}
// 누적 매출이 마일스톤에 도달했는지 확인
return totalRevenue >= amountMilestone;
}
private TriggerConfig parseTriggerConfig(String triggerConfigJson) {
try {
if (triggerConfigJson == null || triggerConfigJson.trim().isEmpty()) {
return TriggerConfig.builder().build();
}
return objectMapper.readValue(triggerConfigJson, TriggerConfig.class);
} catch (Exception e) {
log.error("트리거 설정 파싱 오류: {}, JSON={}", e.getMessage(), triggerConfigJson);
return TriggerConfig.builder().build();
}
}
}