From 8fb0516d2d4791ce41a541407ce3a6fe82541455 Mon Sep 17 00:00:00 2001
From: wayne <wayne8692wayne8692@gmail.com>
Date: 星期六, 22 一月 2022 14:52:54 +0800
Subject: [PATCH] [update] [todo 134622, 134623] 顧問/客戶 未處理預約單平台通知

---
 pamapi/src/doc/預約單/顧問取得所有自己的預約單API.txt                                                        |   89 ++++++++----
 pamapi/src/main/resources/config/application-dev.yml                                          |    2 
 pamapi/src/main/java/com/pollex/pam/config/Constants.java                                     |   19 ++
 pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java                           |   58 ++++++--
 pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java                         |   40 ++++-
 pamapi/src/doc/預約單/標記為已聯絡API.txt                                                              |   67 +++++++--
 pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java         |    4 
 pamapi/src/doc/預約單/顧問取得未處理預約單數量通知.txt                                                         |    5 
 pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java |    8 +
 pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java                          |   67 ++-------
 pamapi/src/doc/預約單/客戶取得最新預約的未處理預約單.txt                                                        |   61 ++++++++
 11 files changed, 293 insertions(+), 127 deletions(-)

diff --git "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\345\256\242\346\210\266\345\217\226\345\276\227\346\234\200\346\226\260\351\240\220\347\264\204\347\232\204\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256.txt" "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\345\256\242\346\210\266\345\217\226\345\276\227\346\234\200\346\226\260\351\240\220\347\264\204\347\232\204\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256.txt"
new file mode 100644
index 0000000..d31a4ea
--- /dev/null
+++ "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\345\256\242\346\210\266\345\217\226\345\276\227\346\234\200\346\226\260\351\240\220\347\264\204\347\232\204\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256.txt"
@@ -0,0 +1,61 @@
+http get :
+
+http://localhost:8080/api/appointment/customer/expiring/newest
+
+蝪∟��mail��誑閰脩雯���脣擐�� -> 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
+    }
+}
diff --git "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\346\250\231\350\250\230\347\202\272\345\267\262\350\201\257\347\265\241API.txt" "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\346\250\231\350\250\230\347\202\272\345\267\262\350\201\257\347\265\241API.txt"
index d4f900c..ad86aac 100644
--- "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\346\250\231\350\250\230\347\202\272\345\267\262\350\201\257\347\265\241API.txt"
+++ "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\346\250\231\350\250\230\347\202\272\345\267\262\350\201\257\347\265\241API.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
+    }
 }
diff --git "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\211\200\346\234\211\350\207\252\345\267\261\347\232\204\351\240\220\347\264\204\345\226\256API.txt" "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\211\200\346\234\211\350\207\252\345\267\261\347\232\204\351\240\220\347\264\204\345\226\256API.txt"
index c863f36..9072a0f 100644
--- "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\211\200\346\234\211\350\207\252\345\267\261\347\232\204\351\240\220\347\264\204\345\226\256API.txt"
+++ "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\211\200\346\234\211\350\207\252\345\267\261\347\232\204\351\240\220\347\264\204\345\226\256API.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
-  } ]
-} ]
\ No newline at end of file
+[
+    {
+        "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
+        }
+    }
+]
diff --git "a/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256\346\225\270\351\207\217\351\200\232\347\237\245.txt" "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256\346\225\270\351\207\217\351\200\232\347\237\245.txt"
new file mode 100644
index 0000000..4582f1e
--- /dev/null
+++ "b/pamapi/src/doc/\351\240\220\347\264\204\345\226\256/\351\241\247\345\225\217\345\217\226\345\276\227\346\234\252\350\231\225\347\220\206\351\240\220\347\264\204\345\226\256\346\225\270\351\207\217\351\200\232\347\237\245.txt"
@@ -0,0 +1,5 @@
+http get :
+http://localhost:8080/api/appointment/consultant/pending/sum
+
+response body:
+2
diff --git a/pamapi/src/main/java/com/pollex/pam/config/Constants.java b/pamapi/src/main/java/com/pollex/pam/config/Constants.java
index ead5fc8..aae9b67 100644
--- a/pamapi/src/main/java/com/pollex/pam/config/Constants.java
+++ b/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";
 
+    /**
+     * �閰勗�mail�撱箇���(T)敺�憭抵�������
+     * �����摰2
+     */
+    public static final int APPOINTMENT_PENDING_PHONE_INTERVAL = 2;
+    public static final int APPOINTMENT_PENDING_EMAIL_INTERVAL = 2;
+
+    /**
+     * �閰勗�mail�撱箇���(T)敺�憭拇�◤閬������嚗憭拇甈⊥����策憿批��
+     * �敺�憭�(T+N+1)嚗停���甈∠策摰X�� 閰脤“���敹�瘜������閬���
+     */
+    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;
+
+    /**
+     * �摰X��活���
+     */
+    public static final int SEND_EXPIRING_NOTIFY_LIMIT = 1;
+
     private Constants() {}
 }
diff --git a/pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java b/pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java
index b3499c6..61b5a04 100644
--- a/pamapi/src/main/java/com/pollex/pam/repository/AppointmentCustomerViewRepository.java
+++ b/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);
 }
diff --git a/pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java b/pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java
index dcac303..6d5ded6 100644
--- a/pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java
+++ b/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;
+    }
 }
diff --git a/pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java b/pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java
index 60bb60a..604479e 100644
--- a/pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java
+++ b/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 {
-
-    /**
-     * �閰勗�mail�撱箇���(T)敺�憭抵�������
-     * �����摰2
-     */
-    private static final int APPOINTMENT_PENDING_PHONE_INTERVAL = 2;
-    private static final int APPOINTMENT_PENDING_EMAIL_INTERVAL = 2;
-
-    /**
-     * �閰勗�mail�撱箇���(T)敺�憭拇�◤閬������嚗憭拇甈⊥����策憿批��
-     * �敺�憭�(T+N+1)嚗停���甈∠策摰X�� 閰脤“���敹�瘜������閬���
-     */
-    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;
-
-    /**
-     * �摰X��活���
-     */
-    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) {
diff --git a/pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java b/pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java
index ca378b3..ac19b72 100644
--- a/pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java
+++ b/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();
 //    }
 }
diff --git a/pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java b/pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java
new file mode 100644
index 0000000..a2a02f0
--- /dev/null
+++ b/pamapi/src/main/java/com/pollex/pam/web/rest/errors/NotFoundExpiringAppointmentException.java
@@ -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 {
+}
diff --git a/pamapi/src/main/resources/config/application-dev.yml b/pamapi/src/main/resources/config/application-dev.yml
index 90c15aa..ced80df 100644
--- a/pamapi/src/main/resources/config/application-dev.yml
+++ b/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:

--
Gitblit v1.8.0