package com.pollex.pam.service;
|
|
import java.time.Instant;
|
import java.time.LocalDate;
|
import java.time.ZoneId;
|
import java.util.Comparator;
|
import java.util.List;
|
import java.util.Objects;
|
import java.util.Optional;
|
import java.util.stream.Collectors;
|
|
import com.pollex.pam.appointment.process.AppointmentProcess;
|
import com.pollex.pam.config.ApplicationProperties;
|
import com.pollex.pam.config.Constants;
|
import com.pollex.pam.enums.SatisfactionTypeEnum;
|
import com.pollex.pam.service.dto.*;
|
import com.pollex.pam.web.rest.errors.SendEmailFailException;
|
import com.pollex.pam.web.rest.errors.SendSMSFailException;
|
import io.jsonwebtoken.lang.Assert;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.beans.BeanUtils;
|
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
import com.pollex.pam.domain.Appointment;
|
import com.pollex.pam.domain.AppointmentCustomerView;
|
import com.pollex.pam.enums.ContactStatusEnum;
|
import com.pollex.pam.enums.InterviewRecordStatusEnum;
|
import com.pollex.pam.repository.AppointmentCustomerViewRepository;
|
import com.pollex.pam.repository.AppointmentRepository;
|
import com.pollex.pam.security.SecurityUtils;
|
import com.pollex.pam.service.mapper.AppointmentCustomerViewMapper;
|
import com.pollex.pam.service.mapper.AppointmentDTOMapper;
|
import com.pollex.pam.web.rest.errors.AppointmentNotFoundException;
|
import org.springframework.util.StringUtils;
|
import org.thymeleaf.context.Context;
|
import org.thymeleaf.spring5.SpringTemplateEngine;
|
|
import static com.pollex.pam.enums.AppointmentStatusEnum.AVAILABLE;
|
import static com.pollex.pam.enums.AppointmentStatusEnum.DELETED;
|
|
@Service
|
@Transactional
|
public class AppointmentService {
|
|
private static final Logger log = LoggerFactory.getLogger(AppointmentService.class);
|
|
private static final String NOTIFY_EMAIL_SUBJECT = "保誠媒合平台系統通知:新預約單";
|
|
@Autowired
|
AppointmentRepository appointmentRepository;
|
|
@Autowired
|
AppointmentDTOMapper appointmentDTOMapper;
|
|
@Autowired
|
ConsultantService consultantService;
|
|
@Autowired
|
AppointmentCustomerViewMapper appointmentCustomerViewMapper;
|
|
@Autowired
|
AppointmentCustomerViewRepository appointmentCustomerViewRepository;
|
|
@Autowired
|
SatisfactionService satisfactionService;
|
|
@Autowired
|
SendMsgService sendMsgService;
|
|
@Autowired
|
ApplicationProperties applicationProperties;
|
|
@Autowired
|
SpringTemplateEngine springTemplateEngine;
|
|
@Autowired
|
InterviewRecordService interviewRecordService;
|
|
@Autowired
|
AppointmentProcess abstractAppointmentProcess;
|
|
@Autowired
|
PersonalNotificationService personalNotificationService;
|
|
public Appointment customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) {
|
Appointment appointment = appointmentDTOMapper.toAppointment(appointmentCreateDTO);
|
appointment.setStatus(AVAILABLE);
|
appointment.setCustomerId(SecurityUtils.getCustomerDBId());
|
appointment.setCommunicateStatus(ContactStatusEnum.RESERVED);
|
return appointmentRepository.save(appointment);
|
}
|
|
public Appointment updateAppointment(AppointmentUpdateDTO updateAppointmentDTO) {
|
Appointment appointment = appointmentRepository.findById(updateAppointmentDTO.getId()).get();
|
BeanUtils.copyProperties(updateAppointmentDTO, appointment);
|
appointment.setPhone(updateAppointmentDTO.getPhone());
|
appointment.setEmail(updateAppointmentDTO.getEmail());
|
appointment.setContactType(updateAppointmentDTO.getContactType());
|
appointment.setGender(updateAppointmentDTO.getGender());
|
appointment.setAge(updateAppointmentDTO.getAge());
|
appointment.setJob(updateAppointmentDTO.getJob());
|
appointment.setRequirement(updateAppointmentDTO.getRequirement());
|
appointment.setHopeContactTime(updateAppointmentDTO.getHopeContactTime());
|
appointment.setOtherRequirement(updateAppointmentDTO.getOtherRequirement());
|
appointment.setLastModifiedDate(Instant.now());
|
|
return appointmentRepository.save(appointment);
|
}
|
|
public void markAppointmentDeleted(Long appointmentId) {
|
Appointment appointment = appointmentRepository.findById(appointmentId).get();
|
appointment.setStatus(DELETED);
|
appointment.setLastModifiedDate(Instant.now());
|
appointment.setCommunicateStatus(ContactStatusEnum.CANCEL);
|
appointmentRepository.save(appointment);
|
personalNotificationService.createMarkAppointmentDeletedToConsultant(appointment);
|
|
}
|
|
public List<Appointment> findByAgentNo(String agentNo) {
|
return appointmentRepository.findByAgentNo(agentNo);
|
}
|
|
public Appointment markAsContacted(Long appointmentId) {
|
Appointment appointment = appointmentRepository.findById(appointmentId).get();
|
appointment.setCommunicateStatus(ContactStatusEnum.CONTACTED);
|
appointment.setContactTime(Instant.now());
|
appointment.setLastModifiedDate(Instant.now());
|
return appointmentRepository.save(appointment);
|
}
|
|
public AppointmentCustomerViewDTO getAppointmentDetail(Long appointmentId) {
|
AppointmentCustomerView appointment = appointmentCustomerViewRepository.findById(appointmentId)
|
.orElseThrow(AppointmentNotFoundException::new);
|
|
AppointmentCustomerViewDTO dto = appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointment);
|
setSatisfactionScore(dto, appointmentId);
|
// setInterviewRecordDTO(dto);
|
return dto;
|
}
|
|
public void setInterviewRecordDTO(AppointmentCustomerViewDTO dto) {
|
List<InterviewRecordDTO> interviewRecords = interviewRecordService.findByAppointmentIdAndStatus(dto.getId(), InterviewRecordStatusEnum.AVAILABLE);
|
dto.setInterviewRecordDTOs(interviewRecords);
|
}
|
|
public List<AppointmentCustomerViewDTO> getConsultantAvailableAppointments(String agentNo) {
|
return appointmentCustomerViewRepository.findByAgentNo(agentNo).stream()
|
.filter(appointment -> appointment.getStatus() == AVAILABLE)
|
.map(appointmentCustomerView -> {
|
AppointmentCustomerViewDTO dto = appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointmentCustomerView);
|
setSatisfactionScore(dto, appointmentCustomerView.getId());
|
return dto;
|
})
|
.collect(Collectors.toList());
|
}
|
|
public void setSatisfactionScore(AppointmentCustomerViewDTO dto, Long appointmentId) {
|
satisfactionService.getByAppointmentIdAndType(appointmentId, SatisfactionTypeEnum.APPOINTMENT).ifPresent(satisfaction -> {
|
dto.setSatisfactionScore(satisfaction.getScore());
|
});
|
}
|
|
public List<AppointmentCustomerView> findAvailableByAgentNoAndCustomerId(String agentNo, Long customerId) {
|
return appointmentCustomerViewRepository.findByAgentNoAndCustomerId(agentNo, customerId)
|
.stream()
|
.filter(appointmentCustomerView -> appointmentCustomerView.getStatus() == AVAILABLE)
|
.collect(Collectors.toList());
|
}
|
|
public Optional<Appointment> findLatestAppointmentByAgentNoAndCustomerId(String agentNo, Long customerId) {
|
return appointmentRepository.findTopByAgentNoAndCustomerIdOrderByAppointmentDateDesc(agentNo, customerId);
|
}
|
|
public void recordConsultantReadTime(Long appointmentId) {
|
Appointment appointment = appointmentRepository.findById(appointmentId).get();
|
|
if(appointment.getConsultantReadTime() == null) {
|
appointment.setConsultantReadTime(Instant.now());
|
appointmentRepository.save(appointment);
|
}
|
else {
|
log.debug("this appointment was read, read time = {}", appointment.getConsultantReadTime());
|
}
|
}
|
|
public void recordAllAppointmentsView(String agentNo) {
|
List<Appointment> consultantNotViewAppointments = findByAgentNo(agentNo)
|
.stream()
|
.filter(appointment -> Objects.isNull(appointment.getConsultantViewTime()))
|
.collect(Collectors.toList());
|
|
consultantNotViewAppointments.forEach(appointment -> appointment.setConsultantViewTime(Instant.now()));
|
appointmentRepository.saveAll(consultantNotViewAppointments);
|
}
|
|
public void sendAppointmentNotify(Appointment appointment) {
|
Assert.notNull(appointment, "appointment entity cannot be null");
|
|
log.debug("is need send appointment notify msg? sms = {}, email = {}",
|
applicationProperties.getSms().isSendNotifyMsg(), applicationProperties.getEmail().isSendNotifyMsg());
|
|
log.debug("sending appointment notify, appointmentId = {}", appointment.getId());
|
sendAppointmentNotifyBySMS(appointment);
|
sendAppointmentNotifyByHtmlEmail(appointment);
|
}
|
|
private void sendAppointmentNotifyBySMS(Appointment appointment) {
|
String msg = getAppointmentNotifyWording(appointment);
|
String consultantMobile = consultantService.findByAgentNo(appointment.getAgentNo()).getPhoneNumber();
|
|
try {
|
if(!StringUtils.hasText(consultantMobile)) {
|
throw new SendSMSFailException("the consultant does not have mobile!");
|
}
|
|
sendMsgService.sendMsgBySMS(consultantMobile, msg);
|
} catch (SendSMSFailException e) {
|
log.warn("send appointment notify by sms was fail, appointment Id = {}", appointment.getId(), e);
|
}
|
}
|
|
private String getAppointmentNotifyWording(Appointment appointment) {
|
String normalContent;
|
if(StringUtils.hasText(appointment.getPhone())) {
|
normalContent = "親愛的顧問您好,您有一筆來自保誠人壽媒合平台的預約,預約客戶手機號碼為" + appointment.getPhone();
|
}
|
else {
|
normalContent = "親愛的顧問您好,您有一筆來自保誠人壽媒合平台的預約";
|
}
|
|
String urlContent = ",請點擊網址:" + getAppointmentDetailUrl(appointment.getId()) + "查看,並安排聯絡訪談,謝謝!";
|
return normalContent + urlContent;
|
}
|
|
private void sendAppointmentNotifyByHtmlEmail(Appointment appointment) {
|
String consultantEmail = consultantService.findByAgentNo(appointment.getAgentNo()).getEmail();
|
String customerMobile = appointment.getPhone();
|
String normalContent;
|
|
if(StringUtils.hasText(customerMobile)) {
|
normalContent = "親愛的顧問您好:您有一筆來自保誠人壽媒合平台的預約,預約客戶手機號碼為" + customerMobile + ",";
|
}
|
else {
|
normalContent = "親愛的顧問您好:您有一筆來自保誠人壽媒合平台的預約,";
|
}
|
|
Context context = new Context();
|
context.setVariable("content", normalContent);
|
context.setVariable("urlHint", getAppointmentDetailUrl(appointment.getId()));
|
String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context);
|
|
try {
|
if(!StringUtils.hasText(consultantEmail)) {
|
throw new SendEmailFailException("the consultant does not have email!");
|
}
|
|
sendMsgService.sendMsgByEmail(consultantEmail, NOTIFY_EMAIL_SUBJECT, content, true);
|
} catch (SendEmailFailException e) {
|
log.warn("send appointment notify by email was fail, appointment Id = {}", appointment.getId(), e);
|
}
|
}
|
|
public String getAppointmentDetailUrl(Long appointmentId) {
|
return applicationProperties.getFrontEndDomain() + "/myAppointmentList/contactedList?appointmentId=" + appointmentId;
|
}
|
|
public Appointment findById(Long id) {
|
return appointmentRepository.findById(id)
|
.orElseThrow(AppointmentNotFoundException::new);
|
}
|
|
public void closeAppointment(AppointmentCloseDTO closeDTO) {
|
if(closeDTO.getContactStatus() == ContactStatusEnum.DONE) {
|
DoneProcessDTO dto = new DoneProcessDTO();
|
BeanUtils.copyProperties(closeDTO, dto);
|
abstractAppointmentProcess.process(dto);
|
}else if(closeDTO.getContactStatus() == ContactStatusEnum.CLOSED){
|
ClosedProcessDTO dto = new ClosedProcessDTO();
|
BeanUtils.copyProperties(closeDTO, dto);
|
abstractAppointmentProcess.process(dto);
|
}
|
}
|
|
public Long getConsultantPendingAppointmentSum(String agentNo) {
|
return appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AVAILABLE)
|
.stream()
|
.filter(appointment -> agentNo.equals(appointment.getAgentNo()))
|
.filter(appointment -> isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_PENDING_PHONE_INTERVAL, Constants.APPOINTMENT_PENDING_EMAIL_INTERVAL))
|
.count();
|
}
|
|
public AppointmentCustomerViewDTO getCustomerNewestExpiringAppointment(Long customerId) {
|
return appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AVAILABLE)
|
.stream()
|
.filter(appointment -> customerId.equals(appointment.getCustomerId()))
|
.filter(appointment -> isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_EXPIRING_PHONE_INTERVAL, Constants.APPOINTMENT_EXPIRING_EMAIL_INTERVAL))
|
.max(Comparator.comparing(AppointmentCustomerView::getAppointmentDate))
|
.map(appointmentCustomerView -> appointmentCustomerViewMapper.toAppointmentCustomerViewDTO(appointmentCustomerView))
|
.orElse(null);
|
}
|
|
public boolean isAppointmentDateNotInIntervalFromNow(AppointmentCustomerView appointment, int phoneInterval, int emailInterval) {
|
final boolean isHavePhone = StringUtils.hasText(appointment.getPhone());
|
final boolean isHaveEmail = StringUtils.hasText(appointment.getEmail());
|
|
LocalDate appointmentDate = appointment.getAppointmentDate().atZone(ZoneId.systemDefault()).toLocalDate();
|
LocalDate nowDate = Instant.now().atZone(ZoneId.systemDefault()).toLocalDate();
|
long intervalDays = nowDate.toEpochDay() - appointmentDate.toEpochDay();
|
|
final boolean isAppointmentExpiringByPhone = isHavePhone && intervalDays >= phoneInterval;
|
final boolean isAppointmentExpiringByEmail = isHaveEmail && intervalDays >= emailInterval;
|
|
return isAppointmentExpiringByPhone || isAppointmentExpiringByEmail;
|
}
|
}
|