保誠-保戶業務員媒合平台
wayne
2022-01-22 8fb0516d2d4791ce41a541407ce3a6fe82541455
[update] [todo 134622, 134623] 顧問/客戶 未處理預約單平台通知

修改8個檔案
新增3個檔案
420 ■■■■■ 已變更過的檔案
pamapi/src/doc/預約單/客戶取得最新預約的未處理預約單.txt 61 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/doc/預約單/標記為已聯絡API.txt 67 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/doc/預約單/顧問取得所有自己的預約單API.txt 89 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/doc/預約單/顧問取得未處理預約單數量通知.txt 5 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/config/Constants.java 19 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java 4 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java 58 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java 67 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java 40 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java 8 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/config/application-dev.yml 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/doc/¹w¬ù³æ/«È¤á¨ú±o³Ì·s¹w¬ùªº¥¼³B²z¹w¬ù³æ.txt
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,61 @@
http get :
http://localhost:8080/api/appointment/customer/expiring/newest
簡訊及email會以該網址進入首頁 -> http://localhost:3000?notContactAppointmentId={最新一筆未處理預約單}
response body: è‹¥æœ‰æœƒå‚³200並給以下資料,若無(未有任何逾期未處理預約單,則會回404)
{
    "id": 385,
    "phone": "0911223344",
    "email": "SDD",
    "contactType": "phone",
    "gender": "female",
    "age": "21-30",
    "job": "內勤",
    "requirement": "健康與保障",
    "communicateStatus": "done",
    "hopeContactTime": "'星期一,星期二,星期三,星期四,星期五,星期六,星期日、9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'",
    "otherRequirement": null,
    "appointmentDate": "2021-12-16T07:11:05.400Z",
    "lastModifiedDate": "2022-01-19T10:57:51.380Z",
    "agentNo": "A568420",
    "customerId": 139,
    "name": "Angula-test",
    "consultantViewTime": "2021-12-27T02:02:18.711Z",
    "consultantReadTime": "2021-12-28T07:16:01.295Z",
    "contactTime": "2021-12-28T07:16:37.004Z",
    "satisfactionScore": null,
    "appointmentMemoList": [],
    "interviewRecordDTOs": [],
    "appointmentNoticeLogs": [
        {
            "id": 4,
            "phone": "0912345678",
            "email": "pollex@gmail.com",
            "appointmentId": 385,
            "content": "notice customer invterview time",
            "createdDate": "2022-01-11T09:33:57.754Z",
            "interviewDate": null
        },
        {
            "id": 6,
            "phone": "0912345678",
            "email": "pollex@gmail.com",
            "appointmentId": 385,
            "content": "notice customer invterview time",
            "createdDate": "2022-01-19T10:38:42.187Z",
            "interviewDate": "2022-11-01T08:00:00.000+00:00"
        }
    ],
    "appointmentClosedInfo": {
        "id": 9,
        "policyholderIdentityId": "A123456789",
        "planCode": "ATMdd",
        "policyEntryDate": "2022-01-12T00:00:00.000+00:00",
        "remark": "test remark",
        "closedReason": "other2",
        "closedOtherReason": "心情不好不想買2",
        "appointmentId": 385
    }
}
pamapi/src/doc/¹w¬ù³æ/¼Ð°O¬°¤wÁpµ¸API.txt
@@ -3,25 +3,56 @@
response body:
{
    "id": 401,
    "phone": "0912345678",
    "email": "wayne@pollex.com.tw",
    "contactType": "EMAIL",
    "gender": "male",
    "age": "under_20",
    "job": "123",
    "requirement": "健康與保障,保單健檢/規劃",
    "communicateStatus": "contacted",
    "id": 385,
    "phone": "0911223344",
    "email": "SDD",
    "contactType": "phone",
    "gender": "female",
    "age": "21-30",
    "job": "內勤",
    "requirement": "健康與保障",
    "communicateStatus": "done",
    "hopeContactTime": "'星期一,星期二,星期三,星期四,星期五,星期六,星期日、9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'",
    "otherRequirement": null,
    "appointmentDate": "2021-12-21T08:13:50.154Z",
    "lastModifiedDate": "2022-01-04T09:40:13.715Z",
    "agentNo": "J149388015",
    "customerId": 155,
    "name": "123",
    "consultantViewTime": "2021-12-24T07:27:48.681Z",
    "consultantReadTime": null,
    "contactTime": "2022-01-04T09:40:13.715Z",
    "appointmentDate": "2021-12-16T07:11:05.400Z",
    "lastModifiedDate": "2022-01-19T10:57:51.380Z",
    "agentNo": "A568420",
    "customerId": 139,
    "name": "Angula-test",
    "consultantViewTime": "2021-12-27T02:02:18.711Z",
    "consultantReadTime": "2021-12-28T07:16:01.295Z",
    "contactTime": "2021-12-28T07:16:37.004Z",
    "satisfactionScore": null,
    "appointmentMemoList": []
    "appointmentMemoList": [],
    "interviewRecordDTOs": [],
    "appointmentNoticeLogs": [
        {
            "id": 4,
            "phone": "0912345678",
            "email": "pollex@gmail.com",
            "appointmentId": 385,
            "content": "notice customer invterview time",
            "createdDate": "2022-01-11T09:33:57.754Z",
            "interviewDate": null
        },
        {
            "id": 6,
            "phone": "0912345678",
            "email": "pollex@gmail.com",
            "appointmentId": 385,
            "content": "notice customer invterview time",
            "createdDate": "2022-01-19T10:38:42.187Z",
            "interviewDate": "2022-11-01T08:00:00.000+00:00"
        }
    ],
    "appointmentClosedInfo": {
        "id": 9,
        "policyholderIdentityId": "A123456789",
        "planCode": "ATMdd",
        "policyEntryDate": "2022-01-12T00:00:00.000+00:00",
        "remark": "test remark",
        "closedReason": "other2",
        "closedOtherReason": "心情不好不想買2",
        "appointmentId": 385
    }
}
pamapi/src/doc/¹w¬ù³æ/ÅU°Ý¨ú±o©Ò¦³¦Û¤vªº¹w¬ù³æAPI.txt
@@ -5,36 +5,59 @@
appointmentMemoList : å‚™è¨»è³‡æ–™
interviewRecordDTOs : ç´„訪紀錄
[ {
  "id" : 385,
  "phone" : "0911223344",
  "email" : "SDD",
  "contactType" : "phone",
  "gender" : "female",
  "age" : "21-30",
  "job" : "內勤",
  "requirement" : "健康與保障",
  "communicateStatus" : "contacted",
  "hopeContactTime" : "'星期一,星期二,星期三,星期四,星期五,星期六,星期日、9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'",
  "otherRequirement" : null,
  "appointmentDate" : "2021-12-16T07:11:05.400Z",
  "lastModifiedDate" : "2021-12-28T07:16:37.004Z",
  "agentNo" : "A568420",
  "customerId" : 139,
  "name" : "Angula-test",
  "consultantViewTime" : "2021-12-27T02:02:18.711Z",
  "consultantReadTime" : "2021-12-28T07:16:01.295Z",
  "contactTime" : "2021-12-28T07:16:37.004Z",
  "satisfactionScore" : null,
  "appointmentMemoList" : [ ],
  "interviewRecordDTOs" : [ {
    "id" : 6,
    "content" : "test record content",
    "createdDate" : "2022-01-07T07:38:05.976Z",
    "lastModifiedDate" : "2022-01-07T07:38:05.976Z",
    "createdBy" : "A568420",
    "lastModifiedBy" : "A568420",
    "interviewDate" : "2021-01-01T08:00:00.000+00:00",
    "appointmentId" : 385
  } ]
} ]
[
    {
        "id": 385,
        "phone": "0911223344",
        "email": "SDD",
        "contactType": "phone",
        "gender": "female",
        "age": "21-30",
        "job": "內勤",
        "requirement": "健康與保障",
        "communicateStatus": "done",
        "hopeContactTime": "'星期一,星期二,星期三,星期四,星期五,星期六,星期日、9:00~12:00,12:00~14:00,14:00~18:00,18:00~21:00'",
        "otherRequirement": null,
        "appointmentDate": "2021-12-16T07:11:05.400Z",
        "lastModifiedDate": "2022-01-19T10:57:51.380Z",
        "agentNo": "A568420",
        "customerId": 139,
        "name": "Angula-test",
        "consultantViewTime": "2021-12-27T02:02:18.711Z",
        "consultantReadTime": "2021-12-28T07:16:01.295Z",
        "contactTime": "2021-12-28T07:16:37.004Z",
        "satisfactionScore": null,
        "appointmentMemoList": [],
        "interviewRecordDTOs": [],
        "appointmentNoticeLogs": [
            {
                "id": 4,
                "phone": "0912345678",
                "email": "pollex@gmail.com",
                "appointmentId": 385,
                "content": "notice customer invterview time",
                "createdDate": "2022-01-11T09:33:57.754Z",
                "interviewDate": null
            },
            {
                "id": 6,
                "phone": "0912345678",
                "email": "pollex@gmail.com",
                "appointmentId": 385,
                "content": "notice customer invterview time",
                "createdDate": "2022-01-19T10:38:42.187Z",
                "interviewDate": "2022-11-01T08:00:00.000+00:00"
            }
        ],
        "appointmentClosedInfo": {
            "id": 9,
            "policyholderIdentityId": "A123456789",
            "planCode": "ATMdd",
            "policyEntryDate": "2022-01-12T00:00:00.000+00:00",
            "remark": "test remark",
            "closedReason": "other2",
            "closedOtherReason": "心情不好不想買2",
            "appointmentId": 385
        }
    }
]
pamapi/src/doc/¹w¬ù³æ/ÅU°Ý¨ú±o¥¼³B²z¹w¬ù³æ¼Æ¶q³qª¾.txt
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,5 @@
http get :
http://localhost:8080/api/appointment/consultant/pending/sum
response body:
2
pamapi/src/main/java/com/pollex/pam/config/Constants.java
@@ -11,5 +11,24 @@
    public static final String SYSTEM = "system";
    public static final String DEFAULT_LANGUAGE = "zh-tw";
    /**
     * é›»è©±åŠemail在建立預約單(T)後的N天視為未處理預約單
     * ç›®å‰N皆暫定為2
     */
    public static final int APPOINTMENT_PENDING_PHONE_INTERVAL = 2;
    public static final int APPOINTMENT_PENDING_EMAIL_INTERVAL = 2;
    /**
     * é›»è©±åŠemail在建立預約單(T)後的N天會被視為未處理預約單,當天批次會發送提醒給顧問
     * è€Œåœ¨å¾Œä¸€å¤©(T+N+1),就會發送批次給客戶告知 è©²é¡§å•å¯èƒ½å¿™ç¢Œç„¡æ³•處理,是否需要取消
     */
    public static final int APPOINTMENT_EXPIRING_PHONE_INTERVAL = APPOINTMENT_PENDING_PHONE_INTERVAL + 1;
    public static final int APPOINTMENT_EXPIRING_EMAIL_INTERVAL = APPOINTMENT_PENDING_EMAIL_INTERVAL + 1;
    /**
     * é€šçŸ¥å®¢æˆ¶çš„æ¬¡æ•¸é™åˆ¶
     */
    public static final int SEND_EXPIRING_NOTIFY_LIMIT = 1;
    private Constants() {}
}
pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java
@@ -2,6 +2,9 @@
import java.util.List;
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.enums.AppointmentStatusEnum;
import com.pollex.pam.enums.ContactStatusEnum;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@@ -11,4 +14,5 @@
public interface AppointmentCustomerViewRepository extends JpaRepository<AppointmentCustomerView, Long>{
    List<AppointmentCustomerView> findByAgentNo(String agentNo);
    List<AppointmentCustomerView> findByAgentNoAndCustomerId(String agentNo, Long customerId);
    List<AppointmentCustomerView> findAllByCommunicateStatusAndStatus(ContactStatusEnum contactStatus, AppointmentStatusEnum status);
}
pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java
@@ -1,6 +1,9 @@
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;
@@ -8,10 +11,9 @@
import com.pollex.pam.appointment.process.AppointmentProcess;
import com.pollex.pam.config.ApplicationProperties;
import com.pollex.pam.service.dto.AppointmentUpdateDTO;
import com.pollex.pam.service.dto.ClosedProcessDTO;
import com.pollex.pam.service.dto.DoneProcessDTO;
import com.pollex.pam.service.dto.InterviewRecordDTO;
import com.pollex.pam.config.Constants;
import com.pollex.pam.service.dto.*;
import com.pollex.pam.web.rest.errors.NotFoundExpiringAppointmentException;
import com.pollex.pam.web.rest.errors.SendEmailFailException;
import com.pollex.pam.web.rest.errors.SendSMSFailException;
import io.jsonwebtoken.lang.Assert;
@@ -24,15 +26,11 @@
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.domain.AppointmentCustomerView;
import com.pollex.pam.domain.InterviewRecord;
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.dto.AppointmentCloseDTO;
import com.pollex.pam.service.dto.AppointmentCreateDTO;
import com.pollex.pam.service.dto.AppointmentCustomerViewDTO;
import com.pollex.pam.service.mapper.AppointmentCustomerViewMapper;
import com.pollex.pam.service.mapper.AppointmentDTOMapper;
import com.pollex.pam.web.rest.errors.AppointmentNotFoundException;
@@ -77,13 +75,13 @@
    @Autowired
    SpringTemplateEngine springTemplateEngine;
    @Autowired
    InterviewRecordService interviewRecordService;
    @Autowired
    AppointmentProcess abstractAppointmentProcess;
    @Autowired
    PersonalNotificationService personalNotificationService;
@@ -119,7 +117,7 @@
        appointment.setCommunicateStatus(ContactStatusEnum.CANCEL);
        appointmentRepository.save(appointment);
        personalNotificationService.createMarkAppointmentDeletedToConsultant(appointment);
    }
    public List<Appointment> findByAgentNo(String agentNo) {
@@ -203,7 +201,7 @@
        Assert.notNull(appointment, "appointment entity cannot be null");
        log.debug("is need send appointment notify msg? = {}", applicationProperties.isSendNotifyMsg());
        log.debug("sending appointment notify, appointmentId = {}", appointment.getId());
        sendAppointmentNotifyBySMS(appointment);
        sendAppointmentNotifyByHtmlEmail(appointment);
@@ -268,7 +266,7 @@
    public String getAppointmentDetailUrl(Long appointmentId) {
        return applicationProperties.getFrontEndDomain() + "/myAppointmentList/contactedList?appointmentId=" + appointmentId;
    }
    public Appointment findById(Long id) {
        return appointmentRepository.findById(id)
                .orElseThrow(AppointmentNotFoundException::new);
@@ -285,4 +283,36 @@
            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;
    }
}
pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java
@@ -1,15 +1,13 @@
package com.pollex.pam.service;
import com.pollex.pam.config.ApplicationProperties;
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.domain.AppointmentExpiringNotifyRecord;
import com.pollex.pam.domain.Consultant;
import com.pollex.pam.domain.Satisfaction;
import com.pollex.pam.config.Constants;
import com.pollex.pam.domain.*;
import com.pollex.pam.enums.AppointmentStatusEnum;
import com.pollex.pam.enums.ContactStatusEnum;
import com.pollex.pam.enums.SatisfactionStatusEnum;
import com.pollex.pam.repository.AppointmentCustomerViewRepository;
import com.pollex.pam.repository.AppointmentExpiringNotifyRecordRepository;
import com.pollex.pam.repository.AppointmentRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -21,8 +19,6 @@
import org.thymeleaf.spring5.SpringTemplateEngine;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -31,25 +27,6 @@
@Service
@Transactional
public class ScheduleTaskService {
    /**
     * é›»è©±åŠemail在建立預約單(T)後的N天視為未處理預約單
     * ç›®å‰N皆暫定為2
     */
    private static final int APPOINTMENT_PENDING_PHONE_INTERVAL = 2;
    private static final int APPOINTMENT_PENDING_EMAIL_INTERVAL = 2;
    /**
     * é›»è©±åŠemail在建立預約單(T)後的N天會被視為未處理預約單,當天批次會發送提醒給顧問
     * è€Œåœ¨å¾Œä¸€å¤©(T+N+1),就會發送批次給客戶告知 è©²é¡§å•å¯èƒ½å¿™ç¢Œç„¡æ³•處理,是否需要取消
     */
    private static final int APPOINTMENT_EXPIRING_PHONE_INTERVAL = APPOINTMENT_PENDING_PHONE_INTERVAL + 1;
    private static final int APPOINTMENT_EXPIRING_EMAIL_INTERVAL = APPOINTMENT_PENDING_EMAIL_INTERVAL + 1;
    /**
     * é€šçŸ¥å®¢æˆ¶çš„æ¬¡æ•¸é™åˆ¶
     */
    private static final int SEND_EXPIRING_NOTIFY_LIMIT = 1;
    private static final String NOT_CONTACTED_NOTIFY_SUBJECT = "預約單未進行聯繫通知";
    private static final Logger log = LoggerFactory.getLogger(ScheduleTaskService.class);
@@ -61,7 +38,7 @@
    AppointmentService appointmentService;
    @Autowired
    AppointmentRepository appointmentRepository;
    AppointmentCustomerViewRepository appointmentCustomerViewRepository;
    @Autowired
    SendMsgService sendMsgService;
@@ -85,11 +62,13 @@
    public void sendAppointmentPendingNotifyToConsultant() {
        log.info("Starting send appointment pending notify to consultant");
        Map<String, List<Appointment>> consultantWithPendingAppointments =
            appointmentRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE)
        Map<String, List<AppointmentCustomerView>> consultantWithPendingAppointments =
            appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE)
                .stream()
                .filter(appointment -> isAppointmentInInterval(appointment, APPOINTMENT_PENDING_PHONE_INTERVAL, APPOINTMENT_PENDING_EMAIL_INTERVAL))
                .collect(Collectors.groupingBy(Appointment::getAgentNo));
                .filter(appointment ->
                    appointmentService.isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_PENDING_PHONE_INTERVAL, Constants.APPOINTMENT_PENDING_EMAIL_INTERVAL)
                )
                .collect(Collectors.groupingBy(AppointmentCustomerView::getAgentNo));
        consultantWithPendingAppointments.forEach((agentNo, pendingAppointments) -> {
            int pendingAppointmentsSum = pendingAppointments.size();
@@ -109,10 +88,12 @@
    public void sendAppointmentExpiringNotifyToCustomer() {
        log.info("Starting send appointment expiring notify to customer");
        List<Appointment> allByCommunicateStatus =
            appointmentRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE)
        List<AppointmentCustomerView> allByCommunicateStatus =
            appointmentCustomerViewRepository.findAllByCommunicateStatusAndStatus(ContactStatusEnum.RESERVED, AppointmentStatusEnum.AVAILABLE)
                .stream()
                .filter(appointment -> isAppointmentInInterval(appointment, APPOINTMENT_EXPIRING_PHONE_INTERVAL, APPOINTMENT_EXPIRING_EMAIL_INTERVAL))
                .filter(appointment ->
                    appointmentService.isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_EXPIRING_PHONE_INTERVAL, Constants.APPOINTMENT_EXPIRING_EMAIL_INTERVAL)
                )
                .filter(this::isAppointmentNotifyNotOnLimit)
                .collect(Collectors.toList());
@@ -151,25 +132,11 @@
        );
    }
    private boolean isAppointmentInInterval(Appointment 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;
    }
    private boolean isAppointmentNotifyNotOnLimit(Appointment appointment) {
    private boolean isAppointmentNotifyNotOnLimit(AppointmentCustomerView appointment) {
        int sendNotifyToCustomerRecordSum =
            appointmentExpiringNotifyRecordRepository.findAllByAppointmentId(appointment.getId()).size();
        return sendNotifyToCustomerRecordSum < SEND_EXPIRING_NOTIFY_LIMIT;
        return sendNotifyToCustomerRecordSum < Constants.SEND_EXPIRING_NOTIFY_LIMIT;
    }
    private String getAppointmentUrl(Long appointmentId) {
pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java
@@ -2,16 +2,13 @@
import com.pollex.pam.appointment.process.AppointmentProcess;
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.enums.ContactStatusEnum;
import com.pollex.pam.security.SecurityUtils;
import com.pollex.pam.service.SendMsgService;
import com.pollex.pam.service.dto.AppointmentUpdateDTO;
import com.pollex.pam.service.dto.ClosedProcessDTO;
import com.pollex.pam.service.dto.DoneProcessDTO;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import com.pollex.pam.service.AppointmentService;
@@ -20,6 +17,8 @@
import com.pollex.pam.service.dto.AppointmentCloseDTO;
import com.pollex.pam.service.dto.AppointmentCreateDTO;
import com.pollex.pam.service.dto.AppointmentCustomerViewDTO;
import java.util.Objects;
@RestController
@RequestMapping("/api/appointment")
@@ -33,10 +32,10 @@
    @Autowired
    SendMsgService sendMsgService;
    @Autowired
    AppointmentProcess abstractAppointmentProcess;
    @Autowired
    PersonalNotificationService personalNotificationService;
@@ -75,16 +74,35 @@
        appointmentService.recordConsultantReadTime(appointmentId);
        return ResponseEntity.noContent().build();
    }
    @PostMapping("/close")
    public ResponseEntity<Void> closeAppointment(@RequestBody AppointmentCloseDTO closeDTO) {
        appointmentService.closeAppointment(closeDTO);
        return ResponseEntity.noContent().build();
    }
    @GetMapping("/customer/expiring/newest")
    public ResponseEntity<AppointmentCustomerViewDTO> getNewestExpiringAppointment() {
        Long customerId = SecurityUtils.getCustomerDBId();
        AppointmentCustomerViewDTO customerNewestExpiringAppointment = appointmentService.getCustomerNewestExpiringAppointment(customerId);
        if(Objects.nonNull(customerNewestExpiringAppointment)) {
            return new ResponseEntity<>(customerNewestExpiringAppointment, HttpStatus.OK);
        }
        else {
            return new ResponseEntity<>(HttpStatus.NOT_FOUND);
        }
    }
    @GetMapping("/consultant/pending/sum")
    public ResponseEntity<Long> getConsultantPendingAppointmentSum() {
        String agentNo = SecurityUtils.getAgentNo();
        return new ResponseEntity<>(appointmentService.getConsultantPendingAppointmentSum(agentNo), HttpStatus.OK);
    }
//    @PostMapping("/close/info/edit")
//    public ResponseEntity<Void> editAppointmentClosedInfo(@RequestBody AppointmentCloseDTO closeDTO) {
//
//
//        if(closeDTO.getContactStatus() == ContactStatusEnum.DONE) {
//            DoneProcessDTO dto = new DoneProcessDTO();
//            BeanUtils.copyProperties(closeDTO, dto);
@@ -96,7 +114,7 @@
//        }else {
//            return ResponseEntity.notFound().build();
//        }
//
//
//        return ResponseEntity.noContent().build();
//    }
}
pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,8 @@
package com.pollex.pam.web.rest.errors;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "not found any expiring appointment")
public class NotFoundExpiringAppointmentException extends RuntimeException {
}
pamapi/src/main/resources/config/application-dev.yml
@@ -33,7 +33,7 @@
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:postgresql://dev.pollex.com.tw:5433/pam_p2
    #url: jdbc:postgresql://localhost:5432/omo?currentSchema=omo
    #url: jdbc:postgresql://localhost:5432/omo?currentSchema=public
    username: pamadmin
    password: pamadmin
    hikari: