保誠-保戶業務員媒合平台
wayne
2021-12-22 95be8636892203bd6b0dbfdc5b6a2e2643dc0607
[update] [todo 132008] 新增當客戶預約時,會通知顧問的手機及信箱

修改12個檔案
新增2個檔案
263 ■■■■ 已變更過的檔案
pamapi/src/doc/sql/20211222_w.sql 1 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java 27 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/domain/Consultant.java 16 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java 4 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java 136 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/service/dto/SendMailResponse.java 4 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java 10 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/web/rest/TestSendMsgResource.java 28 ●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/java/com/pollex/pam/web/rest/errors/SendSMSFailException.java 2 ●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/config/application-dev.yml 3 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/config/application-pollex.yml 11 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/config/application-sit.yml 3 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/config/application-uat.yml 3 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/main/resources/templates/mail/appointmentNotifyEmail.html 15 ●●●●● 修補檔 | 檢視 | 原始 | 究查 | 歷程
pamapi/src/doc/sql/20211222_w.sql
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1 @@
ALTER TABLE omo.consultant ADD email varchar NULL;
pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java
@@ -18,6 +18,8 @@
    private String eServiceLoginUrl;
    private String eServiceLoginFunc;
    private String eServiceLoginSys;
    private String frontEndDomain;
    private boolean sendNotifyMsg;
    private SMS sms;
    private Email email;
@@ -75,6 +77,22 @@
    public void seteServiceLoginSys(String eServiceLoginSys) {
        this.eServiceLoginSys = eServiceLoginSys;
    }
    public String getFrontEndDomain() {
        return frontEndDomain;
    }
    public void setFrontEndDomain(String frontEndDomain) {
        this.frontEndDomain = frontEndDomain;
    }
    public boolean isSendNotifyMsg() {
        return sendNotifyMsg;
    }
    public void setSendNotifyMsg(boolean sendNotifyMsg) {
        this.sendNotifyMsg = sendNotifyMsg;
    }
    public SMS getSms() {
@@ -135,6 +153,7 @@
    public static class Email {
        private String url;
        private String functionId;
        private String senderEmail;
        public String getUrl() {
            return url;
@@ -151,5 +170,13 @@
        public void setFunctionId(String functionId) {
            this.functionId = functionId;
        }
        public String getSenderEmail() {
            return senderEmail;
        }
        public void setSenderEmail(String senderEmail) {
            this.senderEmail = senderEmail;
        }
    }
}
pamapi/src/main/java/com/pollex/pam/domain/Consultant.java
@@ -71,6 +71,9 @@
    @Column(name = "communication_style")
    private String communicationStyle;
    @Column(name = "email")
    private String email;
    public Long getId() {
        return id;
    }
@@ -230,6 +233,14 @@
        this.communicationStyle = communicationStyle;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    @Override
    public String toString() {
        return "Consultant{" +
@@ -245,13 +256,14 @@
            ", gender=" + gender +
            ", phoneNumber='" + phoneNumber + '\'' +
            ", companyAddress='" + companyAddress + '\'' +
            ", seniorityYear='" + seniorityYear + '\'' +
            ", seniorityMonth='" + seniorityMonth + '\'' +
            ", seniorityYear=" + seniorityYear +
            ", seniorityMonth=" + seniorityMonth +
            ", concept='" + concept + '\'' +
            ", experience='" + experience + '\'' +
            ", award='" + award + '\'' +
            ", recommend=" + recommend +
            ", communicationStyle='" + communicationStyle + '\'' +
            ", email='" + email + '\'' +
            '}';
    }
}
pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java
@@ -53,12 +53,12 @@
    @Autowired
    SatisfactionService satisfactionService;
    public void customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) {
    public Appointment customerCreateAppointment(AppointmentCreateDTO appointmentCreateDTO) {
        Appointment appointment = appointmentDTOMapper.toAppointment(appointmentCreateDTO);
        appointment.setStatus(AVAILABLE);
        appointment.setCustomerId(SecurityUtils.getCustomerDBId());
        appointment.setCommunicateStatus(ContactStatusEnum.RESERVED);
        appointmentRepository.save(appointment);
        return appointmentRepository.save(appointment);
    }
    public void updateAppointment(AppointmentUpdateDTO updateAppointmentDTO) {
pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java
@@ -1,16 +1,23 @@
package com.pollex.pam.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.pollex.pam.config.ApplicationProperties;
import com.pollex.pam.config.ApplicationProperties.SMS;
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.repository.ConsultantRepository;
import com.pollex.pam.service.dto.*;
import com.pollex.pam.service.util.HttpRequestUtil;
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.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.thymeleaf.context.Context;
import org.thymeleaf.spring5.SpringTemplateEngine;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
@@ -27,10 +34,95 @@
    private static final Logger log = LoggerFactory.getLogger(SendMsgService.class);
    private final Encoder encoder = Base64.getEncoder();
    private static final String EMAIL_SUBJECT = "保誠媒合平台系統通知:新預約單";
    @Autowired
    ApplicationProperties applicationProperties;
    public void sendMsgBySMS(String toMobile, String content) throws SendSMSFailException{
    @Autowired
    ConsultantRepository consultantRepository;
    @Autowired
    SpringTemplateEngine springTemplateEngine;
    public void sendAppointmentNotify(Appointment appointment) {
        Assert.notNull(appointment);
        log.debug("is need sending notify msg = {}", applicationProperties.isSendNotifyMsg());
        if(applicationProperties.isSendNotifyMsg()) {
            log.debug("sending appointment notify, appointmentId = {}", appointment.getId());
            sendAppointmentNotifyBySMS(appointment);
            sendAppointmentNotifyByEmail(appointment);
            // todo éœ€ç¢ºèªä¿èª æ˜¯å¦æœ‰éœ€æ±‚使用html mail
            // sendAppointmentNotifyByHtmlEmail(appointment);
        }
    }
    private void sendAppointmentNotifyBySMS(Appointment appointment) throws SendSMSFailException {
        String msg = getAppointmentNotifyWording(appointment);
        String consultantMobile = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getPhoneNumber();
        try {
            sendMsgBySMS(consultantMobile, msg);
        } catch (SendSMSFailException e) {
            log.debug("send sms failed, appointment Id = {}", appointment.getId(), e);
        }
    }
    private void sendAppointmentNotifyByEmail(Appointment appointment) {
        // todo éœ€å¾—知保誠系統寄件信箱 (並改於設定檔中)
        String senderEmail = applicationProperties.getEmail().getSenderEmail();
        String consultantEmail = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getEmail();
        String content = getAppointmentNotifyWording(appointment);
        try {
            sendMsgByEmail(senderEmail, consultantEmail, EMAIL_SUBJECT, content, false);
        } catch (SendEmailFailException e) {
            log.debug("send email failed, appointment Id = {}", appointment.getId(), e);
        }
    }
    private void sendAppointmentNotifyByHtmlEmail(Appointment appointment) {
        // todo éœ€å¾—知保誠系統寄件信箱 (並改於設定檔中)
        String senderEmail = applicationProperties.getEmail().getSenderEmail();
        String consultantEmail = consultantRepository.findOneByAgentNo(appointment.getAgentNo()).get().getEmail();
        String customerMobile = appointment.getPhone();
        String normalContent;
        if(StringUtils.hasText(customerMobile)) {
            normalContent = "親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單,該客戶手機號碼為" + customerMobile + "\n";
        }
        else {
            normalContent = "親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單\n";
        }
        Context context = new Context();
        context.setVariable("content", normalContent);
        context.setVariable("urlHint", getAppointmentDetailUrl(appointment.getId()));
        String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context);
        try {
            sendMsgByEmail(senderEmail, consultantEmail, EMAIL_SUBJECT, content, true);
        } catch (SendEmailFailException e) {
            log.debug("send email failed, appointment Id = {}", appointment.getId(), e);
        }
    }
    private String getAppointmentNotifyWording(Appointment appointment) {
        String normalContent;
        if(StringUtils.hasText(appointment.getPhone())) {
            normalContent = "親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單,該客戶手機號碼為" + appointment.getPhone() + "\n";
        }
        else {
            normalContent = "親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單\n";
        }
        String urlContent = "點擊網址:" + getAppointmentDetailUrl(appointment.getId()) + " é–‹å•Ÿåª’合平台查看。";
        return normalContent + urlContent;
    }
    public SendSMSResponse sendMsgBySMS(String toMobile, String content) throws SendSMSFailException {
        SMS smsProperties = applicationProperties.getSms();
        SendSMSRequest sendSMSRequest = new SendSMSRequest();
@@ -54,6 +146,7 @@
            log.debug("response status code = {}", responseEntity.getStatusCode());
            log.debug("smsResponse = {}", responseEntity.getBody());
            return responseEntity.getBody();
            // todo å¯èƒ½éœ€è¦å†è£œä¸Šå¾ŒçºŒéŒ¯èª¤è™•理,但要先測通
        }
        catch (Exception e) {
@@ -62,11 +155,20 @@
        }
    }
    public void sendMsgByEmail(String from, String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{
        sendMsgByEmail(from, to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList());
    public String sendMsgByHtmlTestTemplateEmail(String from, String to) {
        Context context = new Context();
        context.setVariable("content", "親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單\n");
        context.setVariable("urlHint", getAppointmentDetailUrl(0L));
        String content = springTemplateEngine.process("mail/appointmentNotifyEmail", context);
        return sendMsgByEmail(from, to, EMAIL_SUBJECT, content, true);
    }
    public void sendMsgByEmail(
    public String sendMsgByEmail(String from, String to, String subject, String content, boolean htmlFormat) throws SendEmailFailException{
        return sendMsgByEmail(from, to, subject, content, htmlFormat, Collections.emptyList(), Collections.emptyList());
    }
    public String sendMsgByEmail(
        String fromAddress, String toAddress, String subject, String content, boolean htmlFormat, List<String> toCCAddress,
        List<String> attachments) throws SendEmailFailException
    {
@@ -80,21 +182,23 @@
        sendMailRequest.setHtmlFormat(htmlFormat);
        sendMailRequest.setFunctionId(applicationProperties.getEmail().getFunctionId());
        sendMsgByEmail(sendMailRequest);
        return sendMsgByEmail(sendMailRequest);
    }
    public void sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{
    private String sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{
        try {
            ResponseEntity<SendMailResponse> responseEntity =
                HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, SendMailResponse.class);
            ResponseEntity<String> responseEntity =
                HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, String.class);
            log.debug("responseEntity = {}", responseEntity);
            SendMailResponse sendMailResponse = responseEntity.getBody();
            log.debug("response status code = {}", responseEntity.getStatusCode());
            log.debug("emailResponse = {}", responseEntity.getBody());
            SendMailResponse sendMailResponse = new ObjectMapper().readValue(responseEntity.getBody(), SendMailResponse.class);
            log.debug("sendMailResponse = {}", sendMailResponse);
            if(sendMailResponse == null || sendMailResponse.getData() == null || "ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) {
                throw new SendEmailFailException();
            }
//            if(sendMailResponse == null || sendMailResponse.getData() == null || "ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) {
//                throw new SendEmailFailException();
//            }
            return responseEntity.getBody();
        }
        catch (SendEmailFailException e) {
            throw e;
@@ -104,4 +208,8 @@
            throw new SendEmailFailException();
        }
    }
    private String getAppointmentDetailUrl(Long appointmentId) {
        return applicationProperties.getFrontEndDomain() + "/myAppointmentList/contactedList?appointmentId=" + appointmentId;
    }
}
pamapi/src/main/java/com/pollex/pam/service/dto/SendMailResponse.java
@@ -1,5 +1,8 @@
package com.pollex.pam.service.dto;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class SendMailResponse {
    private Data data;
@@ -11,6 +14,7 @@
        this.data = data;
    }
    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class Data {
        private String messageStatus;
pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java
@@ -1,5 +1,7 @@
package com.pollex.pam.web.rest;
import com.pollex.pam.domain.Appointment;
import com.pollex.pam.service.SendMsgService;
import com.pollex.pam.service.dto.AppointmentUpdateDTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
@@ -20,6 +22,9 @@
    @Autowired
    SatisfactionService satisfactionService;
    @Autowired
    SendMsgService sendMsgService;
    @PutMapping("")
    public ResponseEntity<Void> updateAppointment(@RequestBody AppointmentUpdateDTO appointment) {
        appointmentService.updateAppointment(appointment);
@@ -34,8 +39,9 @@
    @PostMapping("/customer/create")
    public void clientCreateAppointment(@RequestBody AppointmentCreateDTO appointmentCreateDTO) {
        appointmentService.customerCreateAppointment(appointmentCreateDTO);
    }
        Appointment appointment = appointmentService.customerCreateAppointment(appointmentCreateDTO);
        sendMsgService.sendAppointmentNotify(appointment);
    }
    @PostMapping("/markAsContacted/{appointmentId}")
    public void markAsContacted(@PathVariable Long appointmentId) {
pamapi/src/main/java/com/pollex/pam/web/rest/TestSendMsgResource.java
@@ -1,6 +1,8 @@
package com.pollex.pam.web.rest;
import com.pollex.pam.repository.AppointmentRepository;
import com.pollex.pam.service.SendMsgService;
import com.pollex.pam.service.dto.SendSMSResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@@ -13,21 +15,35 @@
    @Autowired
    SendMsgService sendMsgService;
    @Autowired
    AppointmentRepository appointmentRepository;
    @GetMapping("/bySMS")
    public ResponseEntity<Void> bySMS(@RequestParam String toMobile, @RequestParam String content) {
        sendMsgService.sendMsgBySMS(toMobile, content);
        return ResponseEntity.noContent().build();
    public ResponseEntity<SendSMSResponse> bySMS(@RequestParam String toMobile, @RequestParam String content) {
        return ResponseEntity.ok(sendMsgService.sendMsgBySMS(toMobile, content));
    }
    @GetMapping("/byEmail")
    public ResponseEntity<Void> byEmail(
    public ResponseEntity<String> byEmail(
        @RequestParam String from,
        @RequestParam String to,
        @RequestParam String subject,
        @RequestParam String content,
        @RequestParam boolean htmlFormat
    ) {
        sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat);
        return ResponseEntity.noContent().build();
        return ResponseEntity.ok(sendMsgService.sendMsgByEmail(from, to, subject, content, htmlFormat));
    }
    @GetMapping("/byHtmlEmail")
    public ResponseEntity<String> byHtmlEmail(
        @RequestParam String from,
        @RequestParam String to
    ) {
        return ResponseEntity.ok(sendMsgService.sendMsgByHtmlTestTemplateEmail(from, to));
    }
    @GetMapping("/appointment/{appointmentId}")
    public void sendAppointmentNotify(@PathVariable Long appointmentId) {
        sendMsgService.sendAppointmentNotify(appointmentRepository.findById(appointmentId).get());
    }
}
pamapi/src/main/java/com/pollex/pam/web/rest/errors/SendSMSFailException.java
@@ -3,7 +3,7 @@
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "send email failed")
@ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR, reason = "send sms failed")
public class SendSMSFailException extends RuntimeException{
    public SendSMSFailException(){}
    public SendSMSFailException(String message) {
pamapi/src/main/resources/config/application-dev.yml
@@ -119,6 +119,8 @@
  e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate
  e-service-login-func: ValidateUsrLogin
  e-service-login-sys: epos
  front-end-domain: http://localhost:3000
  send-notify-msg: false
  sms:
    url: https://localhost:8081/testSMS
    source-code: ePos
@@ -127,3 +129,4 @@
  email:
    url: https://localhost:8081/testEmail
    function-id: epos
    sender-email: test@pollex.com.tw
pamapi/src/main/resources/config/application-pollex.yml
@@ -117,3 +117,14 @@
  e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate
  e-service-login-func: ValidateUsrLogin
  e-service-login-sys: epos
  front-end-domain: http://dev.pollex.com.tw:5566/pam
  send-notify-msg: false
  sms:
    url: https://localhost:8081/testSMS
    source-code: ePos
    sender: POS
    sms-type: '0017'
  email:
    url: https://localhost:8081/testEmail
    function-id: epos
    sender-email: test@pollex.com.tw
pamapi/src/main/resources/config/application-sit.yml
@@ -117,6 +117,8 @@
  e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate
  e-service-login-func: ValidateUsrLogin
  e-service-login-sys: epos
  front-end-domain: https://vtwlifeopensyssit.pru.intranet.asia/pam
  send-notify-msg: true
  sms:
    url: https://vtwlifewinbo66.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource
    source-code: ePos
@@ -125,3 +127,4 @@
  email:
    url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail
    function-id: epos
    sender-email: test@pollex.com.tw
pamapi/src/main/resources/config/application-uat.yml
@@ -117,6 +117,8 @@
  e-service-login-url: https://eserviceuat.pcalife.com.tw/sso/chatbotValidate
  e-service-login-func: ValidateUsrLogin
  e-service-login-sys: epos
  front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam
  send-notify-msg: true
  sms:
    url: https://vtwlifewinbo66.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource
    source-code: ePos
@@ -125,3 +127,4 @@
  email:
    url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail
    function-id: epos
    sender-email: test@pollex.com.tw
pamapi/src/main/resources/templates/mail/appointmentNotifyEmail.html
¤ñ¹ï·sÀÉ®×
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org" lang="zh">
  <head>
    <title>新的預約單通知</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  </head>
  <body>
    <p th:text="${content}">親愛的顧問您好,您有一筆來自保誠媒合平台的新預約單</p>
    <p>
      é»žæ“Šç¶²å€ï¼š
      <a th:href="${urlHint}" th:text="${urlHint}">Url Position</a>
      é–‹å•Ÿåª’合平台查看
    </p>
  </body>
</html>