From 21d2b51910f1e1e338beed76d53effcfccb1ef7a Mon Sep 17 00:00:00 2001 From: Jack <jack.su@pollex.com.tw> Date: 星期一, 24 一月 2022 16:47:57 +0800 Subject: [PATCH] Merge branch 'Phase3' of ssh://dev.pollex.com.tw:29418/pcalife/PAM into Phase3 --- pamapi/src/main/java/com/pollex/pam/config/Constants.java | 2 pamapi/src/main/java/com/pollex/pam/web/rest/AppointmentResource.java | 7 PAMapp/shared/services/httpClient.ts | 13 pamapi/src/doc/sql/淨空整個系統資料(除顧問).sql | 12 PAMapp/assets/scss/utilities/_utilities.scss | 5 pamapi/src/main/java/com/pollex/pam/enums/SendEmailMsgMethod.java | 6 PAMapp/components/DateTimePicker.vue | 5 pamapi/src/doc/預約單/客戶取得最新預約的未處理預約單.txt | 2 pamapi/src/main/resources/config/application-sit.yml | 4 PAMapp/pages/index.vue | 231 +++++++++++++---- pamapi/src/doc/sql/20220122_w.sql | 2 pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java | 37 ++ pamapi/src/main/resources/config/application-dev.yml | 18 + PAMapp/components/Ui/UiTimePicker.vue | 33 ++ PAMapp/pages/questionnaire/_agentNo.vue | 22 + PAMapp/components/Ui/UiDatePicker.vue | 20 + PAMapp/middleware/getUrlQuery.ts | 12 PAMapp/pages/myAppointmentList.vue | 1 PAMapp/pages/satisfactionList.vue | 49 +++ pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java | 25 + PAMapp/pages/appointment/_appointmentId/close/index.vue | 1 PAMapp/components/BackActionBar.vue | 3 PAMapp/components/Consultant/ConsultantCard.vue | 25 + /dev/null | 58 ---- pamapi/src/main/resources/config/application-pollex.yml | 18 + pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java | 3 PAMapp/components/Client/ClientCard.vue | 23 + PAMapp/shared/services/reviews.service.ts | 10 PAMapp/store/localStorage.ts | 21 + pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java | 63 +++- PAMapp/shared/models/strict-query.model.ts | 18 pamapi/src/main/resources/config/application-uat.yml | 4 PAMapp/assets/scss/vendors/elementUI/_rate.scss | 1 PAMapp/pages/notification/index.vue | 8 PAMapp/assets/scss/vendors/elementUI/_select.scss | 3 pamapi/src/main/resources/config/application-prod.yml | 4 36 files changed, 540 insertions(+), 229 deletions(-) diff --git a/PAMapp/assets/scss/utilities/_utilities.scss b/PAMapp/assets/scss/utilities/_utilities.scss index 04b3932..ebf13d7 100644 --- a/PAMapp/assets/scss/utilities/_utilities.scss +++ b/PAMapp/assets/scss/utilities/_utilities.scss @@ -5,6 +5,10 @@ margin-bottom: 50px; } +.mt-50 { + margin-top: 50px; +} + .mt-30 { margin-top: 30px; } @@ -21,6 +25,7 @@ margin-bottom: 20px; } + .mt-10 { margin-top: 10px; } diff --git a/PAMapp/assets/scss/vendors/elementUI/_rate.scss b/PAMapp/assets/scss/vendors/elementUI/_rate.scss index 48909a5..571b574 100644 --- a/PAMapp/assets/scss/vendors/elementUI/_rate.scss +++ b/PAMapp/assets/scss/vendors/elementUI/_rate.scss @@ -45,6 +45,7 @@ display: flex; justify-content: center; margin-top: 10px; + @extend .fix-chrome-click--issue; .el-rate__item { .el-rate__icon { font-size: 30px; diff --git a/PAMapp/assets/scss/vendors/elementUI/_select.scss b/PAMapp/assets/scss/vendors/elementUI/_select.scss index 515da4a..815b184 100644 --- a/PAMapp/assets/scss/vendors/elementUI/_select.scss +++ b/PAMapp/assets/scss/vendors/elementUI/_select.scss @@ -31,6 +31,9 @@ line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; + &:before { + content: "\e910"; + } } } diff --git a/PAMapp/components/BackActionBar.vue b/PAMapp/components/BackActionBar.vue index f7981a0..29fc5ee 100644 --- a/PAMapp/components/BackActionBar.vue +++ b/PAMapp/components/BackActionBar.vue @@ -74,9 +74,6 @@ case 'accountSetting': featureLabel = '�犖撣唾�身摰�'; break; - case 'appointmentAgenda': - featureLabel = '�撠�赤����'; - break; case 'consultantAccountSetting': featureLabel = '���董�����'; break; diff --git a/PAMapp/components/Client/ClientCard.vue b/PAMapp/components/Client/ClientCard.vue index 294eb5f..0ff9748 100644 --- a/PAMapp/components/Client/ClientCard.vue +++ b/PAMapp/components/Client/ClientCard.vue @@ -213,6 +213,9 @@ @appointmentStore.Action getAppointmentDetail!: (appointmentId: number) => Promise<Appointment>; + @appointmentStore.Action + updateAppointmentDetail!: (id: number) => Appointment; + @appointmentStore.Getter appointmentProgress!: ContactStatus; @@ -258,7 +261,10 @@ viewAppointmentDetail(): void { this.getAppointmentDetail(this.client.id).then((_) => { - this.readAppointment(); + const unread = !this.client.consultantReadTime; + if (unread) { + this.readAppointment(); + } this.$router.push(`/appointment/${this.client.id}`); }); } @@ -277,7 +283,6 @@ reviewsService.sendSatisfactionToClient(this.client.id).then(res => { this.isShowInviteReviewDialog = true ; }) - } openDetail() { @@ -302,14 +307,12 @@ } private readAppointment(): void { - const unread = !this.client.consultantReadTime; - if (unread) { - appointmentService.recordRead(this.client.id).then((_) => { - const updatedClient = {...this.client}; - updatedClient.consultantReadTime = new Date().toString(); - this.updateMyAppointmentList(updatedClient); - }); - }; + appointmentService.recordRead(this.client.id).then((_) => { + const updatedClient = {...this.client}; + updatedClient.consultantReadTime = new Date().toString(); + this.updateMyAppointmentList(updatedClient); + this.updateAppointmentDetail(this.client.id); + }); } private clearAppointmentIdFromMsg() { diff --git a/PAMapp/components/Consultant/ConsultantCard.vue b/PAMapp/components/Consultant/ConsultantCard.vue index f7198f6..8ea5a64 100644 --- a/PAMapp/components/Consultant/ConsultantCard.vue +++ b/PAMapp/components/Consultant/ConsultantCard.vue @@ -31,7 +31,7 @@ <div class="delete" v-if="showRemoveBtn" - @click="removeAgent" + @click="isRemoveAgentPopup = true" >蝘駁</div> <div v-if="notScoreAppointmentYet" @@ -125,10 +125,18 @@ </div> </PopUpFrame> - <PopUpFrame :isOpen.sync="isConfirmPopup"> - <div class="text--center mdTxt">撌脫����迨蝑���</div> + <PopUpFrame :isOpen.sync="isConfirmPopup"> + <div class="text--center mdTxt">撌脫��{confirmTxt}}</div> <div class="text--center mt-30"> <el-button @click="isConfirmPopup = false" type="primary">蝣箏��</el-button> + </div> + </PopUpFrame> + + <PopUpFrame :isOpen.sync="isRemoveAgentPopup"> + <div class="text--center mdTxt">��蝘駁憿批�� <span class="text--primary">{{agentInfo.name}}</span>嚗�</div> + <div class="text--center mt-30"> + <el-button @click="isRemoveAgentPopup = false">�</el-button> + <el-button @click="removeAgent" type="primary">�</el-button> </div> </PopUpFrame> </div> @@ -181,6 +189,8 @@ isCancelPopup = false; hideReviews = hideReviews; isConfirmPopup = false; + isRemoveAgentPopup = false; + confirmTxt = ''; appointmentDetail: any = { age : '', @@ -339,6 +349,12 @@ removeAgent() { this.removeFromMyConsultantList(this.agentInfo.agentNo).then((removeOk) => { + this.isRemoveAgentPopup = false; + setTimeout(() => { + this.confirmTxt = '蝘駁憿批��'; + this.isConfirmPopup = true; + }, 300); + }); } @@ -370,7 +386,9 @@ this.isVisibleDialog = false; this.isCancelPopup = false; setTimeout(() => { + this.confirmTxt = '���迨蝑���'; this.isConfirmPopup = true; + }, 300); }); } @@ -422,6 +440,7 @@ } .delete { + display: inline-block; color: $PRIMARY_RED; font-size: 14px; font-weight: bold; diff --git a/PAMapp/components/DateTimePicker.vue b/PAMapp/components/DateTimePicker.vue index f2a8763..584df86 100644 --- a/PAMapp/components/DateTimePicker.vue +++ b/PAMapp/components/DateTimePicker.vue @@ -5,12 +5,14 @@ <UiDatePicker @changeDate="changeDateTime($event, 'date')" :isPastDateDisabled="isPastDateDisabled" + :isFutureDateDisabled="isFutureDateDisabled" :defaultValue="defaultValue" ></UiDatePicker> <UiTimePicker @changeTime="changeDateTime($event, 'time')" :defaultValue="defaultValue" :isPastDateDisabled="isPastDateDisabled" + :isFutureDateDisabled="isFutureDateDisabled" :changeDate="changeDate" ></UiTimePicker> </div> @@ -30,6 +32,9 @@ @Prop() isPastDateDisabled!: boolean; + @Prop() + isFutureDateDisabled!: boolean; + @Emit('changeDateTime') changeDateTime(event, type) { if (type === 'date') { diff --git a/PAMapp/components/Interview/interviewNotification.vue b/PAMapp/components/Interview/interviewNotification.vue deleted file mode 100644 index 9bb6253..0000000 --- a/PAMapp/components/Interview/interviewNotification.vue +++ /dev/null @@ -1,58 +0,0 @@ -<template> - <div class="remind-card"> - <div class="remind-card-header"> - <div class="mdTxt text--black">�撠�赤����</div> - <div class="smTxt_bold text--primary sub-title cursor--pointer" @click="toAgendaPage">����</div> - </div> - - <div class="remind-container"> - <div class="remind-date mr-10"> - <div class="mb-3 smTxt bgc-primary-red date-year"> - <div class="mb-3 mt-2">2021</div> - </div> - <div class="p mt-5"> - <div>12/25</div> - </div> - </div> - - <div class="remind-content-txt"> - <div class="mb-3">09:00</div> - <div>蝝赤���</div> - </div> - - </div> - </div> -</template> - -<script lang="ts"> -import { Vue, Component, Prop, Emit, Action, State, namespace } from 'nuxt-property-decorator'; - -@Component -export default class ConsultantAppointmentRemind extends Vue { - - toAgendaPage(){ - this.$router.push('/appointmentAgenda') - } - ////////////////////////////////////////////////////////////////////// -} -</script> -<style lang="scss" scoped> -.remind-card{ - display: flex; - flex-direction:column; - justify-content: center; - border: 1px solid #CCCCCC; - height: 131px; - padding: 10px 20px; - background-color: #fff; - .remind-card-header{ - display: flex; - justify-content: space-between; - align-items: baseline; - .sub-title{ - border-bottom: 1px solid $PRIMARY_RED; - } - } -} - -</style> diff --git a/PAMapp/components/Ui/UiDatePicker.vue b/PAMapp/components/Ui/UiDatePicker.vue index 20132bc..025594a 100644 --- a/PAMapp/components/Ui/UiDatePicker.vue +++ b/PAMapp/components/Ui/UiDatePicker.vue @@ -5,6 +5,7 @@ v-model="dateValue" :clearable="false" type="date" + :editable="false" format="yyyy/MM/dd" placeholder="������" prefix-icon="icon-down down-icon" @@ -27,6 +28,9 @@ @Prop({default: false}) isPastDateDisabled!: boolean; + @Prop({default: false}) + isFutureDateDisabled!: boolean; + @Emit('changeDate') changeDate() { return this.dateValue; @@ -41,16 +45,26 @@ } get pickerOptions() { + const date = new Date(); + const currentDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; + if (this.isPastDateDisabled) { return { disabledDate(time: Date) { - const date = new Date(); - const currentDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`; - const pickedDate = `${time.getFullYear()}/${time.getMonth() + 1}/${time.getDate()}` + const pickedDate = `${time.getFullYear()}/${time.getMonth() + 1}/${time.getDate()}`; return new Date(pickedDate).getTime() < new Date(currentDate).getTime(); } } } + + if (this.isFutureDateDisabled) { + return { + disabledDate(time: Date) { + const pickedDate = `${time.getFullYear()}/${time.getMonth() + 1}/${time.getDate()}`; + return new Date(pickedDate).getTime() > new Date(currentDate).getTime(); + } + } + } } } diff --git a/PAMapp/components/Ui/UiTimePicker.vue b/PAMapp/components/Ui/UiTimePicker.vue index b661e00..291fbcd 100644 --- a/PAMapp/components/Ui/UiTimePicker.vue +++ b/PAMapp/components/Ui/UiTimePicker.vue @@ -4,6 +4,7 @@ popper-class="pam-time-popper" v-model="timeValue" :clearable="false" + :editable="false" :picker-options="pickerOptions" placeholder="������" prefix-icon="icon-down down-icon" @@ -29,6 +30,9 @@ @Prop() isPastDateDisabled!: boolean; + @Prop() + isFutureDateDisabled!: boolean; + /////////////////////////////////////////////////////////////////////// @Emit('changeTime') @@ -49,25 +53,40 @@ get pickerOptions() { let minTime = ''; + let maxTime = ''; const currentDate = new Date(); - if (this.isPastDateDisabled && this.changeDate && this.isPickedToday(currentDate)) { - minTime = this.formatTimeString(currentDate); - this.isPickedDisableTime(currentDate, minTime); + if (this.changeDate && this.isPickedToday(currentDate)) { + + if (this.isPastDateDisabled) { + minTime = this.formatTimeString(currentDate); + this.isPickedDisableTime(currentDate, minTime); + } + + if (this.isFutureDateDisabled) { + maxTime = this.formatTimeString(currentDate); + this.isPickedDisableTime(currentDate, maxTime); + } + } return { start: '09:00', step: '00:15', end: '21:00', - minTime: minTime + minTime: minTime, + maxTime: maxTime } } - private isPickedDisableTime(currentDate: Date, minTime: string) { - const currentTime = this.getTimeValue(currentDate, minTime); + private isPickedDisableTime(currentDate: Date, minMaxTime: string) { + const currentTime = this.getTimeValue(currentDate, minMaxTime); const pickedTime = this.getTimeValue(currentDate, this.timeValue); - if (pickedTime < currentTime) { + if (this.isPastDateDisabled && pickedTime < currentTime) { + this.timeValue = ''; + this.changeTime(); + } + if (this.isFutureDateDisabled && currentTime < pickedTime) { this.timeValue = ''; this.changeTime(); } diff --git a/PAMapp/middleware/getUrlQuery.ts b/PAMapp/middleware/getUrlQuery.ts index 0a6c0ec..5fb2841 100644 --- a/PAMapp/middleware/getUrlQuery.ts +++ b/PAMapp/middleware/getUrlQuery.ts @@ -3,7 +3,15 @@ const getUrlQuery: Middleware = (context) => { const currentRouteName = context.route.name; const satisfactionIdFromMsg = context.route.query.appointmentId; + const queryNotContactAppointmentId = context.route.query.notContactAppointmentId; const isUserLogin = context.store.getters['localStorage/isUserLogin']; + + if (currentRouteName === 'index' && queryNotContactAppointmentId) { + context.store.commit('localStorage/storageNotContactAppointmentIdFromMsg', queryNotContactAppointmentId); + if (!isUserLogin) { + context.redirect('/login'); + } + } if (currentRouteName === 'index' && satisfactionIdFromMsg) { context.store.commit('localStorage/storageSatisfactionIdFromMsg', satisfactionIdFromMsg); @@ -11,6 +19,8 @@ context.redirect('/login'); } } + + } -export default getUrlQuery \ No newline at end of file +export default getUrlQuery diff --git a/PAMapp/pages/appointment/_appointmentId/close/index.vue b/PAMapp/pages/appointment/_appointmentId/close/index.vue index d10476c..a267a72 100644 --- a/PAMapp/pages/appointment/_appointmentId/close/index.vue +++ b/PAMapp/pages/appointment/_appointmentId/close/index.vue @@ -46,6 +46,7 @@ <UiField label="�脖辣����" :labelSize="20" class="required"> <DateTimePicker :defaultValue="appointmentCloseInfo.policyEntryDate" + :isFutureDateDisabled="true" @changeDateTime="appointmentCloseDate = $event"></DateTimePicker> </UiField> </el-row> diff --git a/PAMapp/pages/index.vue b/PAMapp/pages/index.vue index 93084d4..286c1ff 100644 --- a/PAMapp/pages/index.vue +++ b/PAMapp/pages/index.vue @@ -41,10 +41,9 @@ </div> <Ui-Dialog - :isVisible.sync="isVisibleDialog" - :width="width" + :isVisible.sync="isShowAppointmentDialog" + :width="appointmentDialogWidth" class="pam-myDemand-dialog pam-dialog-reserved" - @closeDialog="clearSatisfactionId" > <div v-if="appointmentDetail"> <h5 class="subTitle text--center mb-30">������</h5> @@ -71,16 +70,44 @@ </div> </div> - <div v-if="!appointmentDetail.satisfactionScore" class="reserved-btn"> + <div v-if="notScoreAppointmentYet" class="reserved-btn"> <el-button type="primary" - @click.native="reviewsBtn = true">蝯虫�遛��漲閰��</el-button> + @click.native="isShowReviewDialog = true">蝯虫�遛��漲閰��</el-button> </div> </div> </Ui-Dialog> <PopUpFrame - :isOpen.sync="reviewsBtn" - @closePopUp="clearSatisfactionId" + :isOpen.sync="isShowReAppointmentDialog" + @closePopUp="removeUrlQueryParameter('notContactAppointmentId')" + > + <div class="pam-dialog-review"> + <div class="mt-30 text--middle" v-if="agentInfo"> + 敺甇������<span class="text--bold">{{ consultantName }}</span>憿批�迤敹�葉嚗������蒂���隞“��� + </div> + + <el-row + type="flex" + class="mt-50" + justify="center"> + <el-button + type="primary" + @click="reAppointment">��������隞“���</el-button> + </el-row> + <el-row + type="flex" + class="mt-20" + justify="center"> + <el-button + class="outline_btn" + @click="cancelAppointment">������</el-button> + </el-row> + </div> + </PopUpFrame> + + <PopUpFrame + :isOpen.sync="isShowReviewDialog" + @closePopUp="removeUrlQueryParameter('appointmentId')" > <div class="mdTxt pam-dialog-review"> 靽憿批�遛��漲 @@ -110,11 +137,16 @@ <script lang="ts"> import { Vue, Component, State, Action, Watch, namespace } from 'nuxt-property-decorator'; + import { Appointment, AppointmentClosedInfo } from '~/shared/models/appointment.model'; import { Consultant } from '~/shared/models/consultant.model'; -import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; + import { ContactStatus } from '~/shared/models/enum/contact-status'; + import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; + import { StrictQueryParams } from '~/shared/models/strict-query.model'; import appointmentService from '~/shared/services/appointment.service'; -import reviewsService from '~/shared/services/reviews.service'; + import reviewsService from '~/shared/services/reviews.service'; import UtilsService from '~/shared/services/utils.service'; + import myConsultantService from '~/shared/services/my-consultant.service'; +import { AgentInfo } from '~/shared/models/agent-info.model'; const localStorage = namespace('localStorage'); const roleStorage = namespace('localStorage'); @@ -136,7 +168,8 @@ @Action storeRecommendList!: any; - @Action storeConsultantList!: any; + @Action + storeConsultantList!: any; @localStorage.Mutation storageClearQuickFilter!: () => void; @@ -147,37 +180,55 @@ @localStorage.Getter currentSatisfactionIdFromMsg!: string; + @localStorage.Getter + currentNotContactAppointmentIdFromMsg!: string; + @localStorage.Mutation storageClearSatisfactionIdFromMsg!: () => void; + @localStorage.Mutation + storageClearNotContactAppointmentIdFromMsg!: () => void; + + @localStorage.Mutation + storageStrickQueryItem!: (strictQueryDto: StrictQueryParams) => void; + consultantList: Consultant[] = []; - appointmentDetail: any = { - age : '', - agentNo : '', - appointmentDate : '', - communicateStatus : '', - consultantReadTime: null, - consultantViewTime: null, - contactTime : '', - contactType : '', - customerId : 0, - email : '', - gender : '', - hopeContactTime : "", - id : 0, - job : "", - lastModifiedDate : '', - name : '', - otherRequirement : null, - phone : "", - requirement : '', - satisfactionScore : 0, + appointmentDialogWidth = ''; + inputScore = 0; + isShowAppointmentDialog = false; + isShowReAppointmentDialog = false; + isShowReviewDialog = false; + consultantName = ''; + contactStatus = ContactStatus; + + appointmentDetail: Appointment = { + age : '', + agentNo : '', + appointmentClosedInfo: {} as AppointmentClosedInfo, + appointmentDate : '', + appointmentMemoList: [], + appointmentNoticeLogs: [], + communicateStatus : this.contactStatus.PICKED, + consultantReadTime: '', + consultantViewTime: '', + contactTime : '', + contactType : '', + customerId : 0, + email : '', + gender : '', + hopeContactTime : '', + interviewRecordDTOs: [], + id : 0, + job : '', + lastModifiedDate : '', + name : '', + otherRequirement : '', + phone : '', + requirement : '', + satisfactionScore : 0, }; - isVisibleDialog = false; - width = ''; - reviewsBtn = false; - inputScore = 0; + agentInfo: Consultant = { agentNo : '', name : '', @@ -211,7 +262,7 @@ } destroyed() { - this.clearSatisfactionId(); + this.removeUrlQueryParameter(); } ////////////////////////////////////////////////////////////////////// @@ -223,36 +274,81 @@ .map((item) => ({ ...item, formatDate: new Date(item.updateTime || item.createTime)})) .sort((preItem, nextItem) => +nextItem.formatDate - +preItem.formatDate); - if (this.currentSatisfactionIdFromMsg) { - this.agentInfo = this.myConsultantList.filter(item => { - const satisfactionIdIndex = item.appointments?.findIndex(i => i.id === +this.currentSatisfactionIdFromMsg); - return satisfactionIdIndex !== undefined && satisfactionIdIndex > -1; - })[0]; - if (this.agentInfo) { - this.openAppointmentInfo(); - } + if (this.currentNotContactAppointmentIdFromMsg) { + this.autoOpenAppointmentBy('askReAppointment', +this.currentNotContactAppointmentIdFromMsg); + return; + } + if (this.currentSatisfactionIdFromMsg) { + this.autoOpenAppointmentBy('inviteReviewConsultant',+this.currentSatisfactionIdFromMsg); + this.storageClearSatisfactionIdFromMsg(); + return; } } - private openAppointmentInfo() { - appointmentService.getAppointmentDetail(+this.currentSatisfactionIdFromMsg).then(res => { - this.appointmentDetail = res; - this.width = UtilsService.isMobileDevice() ? '80%' : ''; - this.isVisibleDialog = true; - - if (!this.appointmentDetail.satisfactionScore) { - setTimeout(() => { - this.reviewsBtn = true; - }, 500) - } + private autoOpenAppointmentBy(reason: string, targetAppointmentId: number): void { + const setAgentInfo = new Promise((resolve, reject) => { + this.agentInfo = this.myConsultantList.filter(item => { + const appointmentIndex = item.appointments?.findIndex(i => i.id === targetAppointmentId); + return appointmentIndex !== undefined && appointmentIndex > -1; + })[0]; + if (this.agentInfo) { + myConsultantService.getConsultantDetail(this.agentInfo.agentNo).then((res) => resolve(res)); + } }); + + const setAppointment = new Promise((resolve, reject) => { + appointmentService.getAppointmentDetail(targetAppointmentId).then((res) => resolve(res)); + }); + + Promise.all([setAgentInfo, setAppointment]).then((values) => { + const agentInfo = values[0] as AgentInfo; + const appointmentInfo = values[1] as Appointment; + this.consultantName = agentInfo.name; + this.appointmentDetail = appointmentInfo; + this.appointmentDialogWidth = UtilsService.isMobileDevice() ? '80%' : ''; + this.isShowAppointmentDialog = true; + switch (reason) { + case 'inviteReviewConsultant': + if (this.notScoreAppointmentYet) { + setTimeout(() => { + this.isShowReviewDialog = true; + }, 500); + } + break; + case 'askReAppointment': + setTimeout(() => { + this.isShowReAppointmentDialog = true; + }, 500); + break; + } + }); + } ////////////////////////////////////////////////////////////////////// navigateToRoute(path: string): void { this.$router.push(path); + } + + reAppointment(): void { + appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => { + const requirements = this.appointmentDetail.requirement.split(','); + console.log('requirements', requirements) + this.storeConsultantList(); + this.storageStrickQueryItem({ requirements: requirements }); + this.storageClearNotContactAppointmentIdFromMsg(); + this.$router.push('/recommendConsultant'); + }); + } + + cancelAppointment(): void { + appointmentService.cancelAppointment(this.appointmentDetail.id).then(() => { + this.storeConsultantList(); + this.storageClearNotContactAppointmentIdFromMsg(); + this.$router.push(''); + }); } userReviewsConsultants() { @@ -263,14 +359,22 @@ this.appointmentDetail.satisfactionScore = this.inputScore; reviewsService.userReviewsConsultants(reviewParams).then((res) => { - this.reviewsBtn = false; + this.isShowReviewDialog = false; }); } - clearSatisfactionId() { - console.log('close'); - this.$router.push({query: {}}); - this.storageClearSatisfactionIdFromMsg(); + removeUrlQueryParameter(targetKey?: string): void { + // NOTE: ���摰�� query parameter [Tomas, 2022/1/24 11:36] + // [REF] How to remove a parameter from this.$router.query Nuxt.js? https://reurl.cc/X45aMD + let newRouteQuery = {}; + if (targetKey) { + Object.keys(this.$route.query).forEach((key) => { + if (key !== targetKey) { + newRouteQuery[key] = this.$route.query[key] + } + }) + } + this.$router.push(newRouteQuery); } /////////////////////////////////////////////////////////////////////////////// @@ -288,6 +392,13 @@ return contactList.filter((item: any) => !!item && item !== ",") } + get notScoreAppointmentYet(): boolean { + if (this.appointmentDetail.communicateStatus === 'closed' || this.appointmentDetail.communicateStatus === 'done') { + return !this.appointmentDetail.satisfactionScore; + }; + return false; + } + } </script> diff --git a/PAMapp/pages/myAppointmentList.vue b/PAMapp/pages/myAppointmentList.vue index 5045e5f..c013cd9 100644 --- a/PAMapp/pages/myAppointmentList.vue +++ b/PAMapp/pages/myAppointmentList.vue @@ -1,7 +1,6 @@ <template> <div> <div class="pam-myAppointment-banner"></div> - <InterviewNotification></InterviewNotification> <div class="pam-container"> <div class="pam-cus-tabs mb-10"> <div diff --git a/PAMapp/pages/notification/index.vue b/PAMapp/pages/notification/index.vue index 1dcdba1..f79907c 100644 --- a/PAMapp/pages/notification/index.vue +++ b/PAMapp/pages/notification/index.vue @@ -1,8 +1,9 @@ <template> <div> - <div class="text--right mb-10" @click="showNotificationHint = true"> + <!-- TODO: ��撌脰�/���� ���摰����瘙�Ⅱ隤� ����� --> + <!-- <div class="text--right mb-10" @click="showNotificationHint = true"> <i class="satisfaction-icon icon-edit"></i> - </div> + </div> --> <div v-if="isUserLogin && unReviewLogList.length" class="satisfaction-banner my-10 cursor--pointer" @@ -18,7 +19,8 @@ align="middle" class="notification-card" > - <el-col class="unRead" :span="3" v-if="!item.readDate"></el-col> + <!-- TODO: ��撌脰�/���� ���摰����瘙�Ⅱ隤� ����� --> + <!-- <el-col class="unRead" :span="3" v-if="!item.readDate"></el-col> --> <el-col :span="18"> <p class="text">{{item.content}}</p> </el-col> diff --git a/PAMapp/pages/questionnaire/_agentNo.vue b/PAMapp/pages/questionnaire/_agentNo.vue index cc1ccc5..120c844 100644 --- a/PAMapp/pages/questionnaire/_agentNo.vue +++ b/PAMapp/pages/questionnaire/_agentNo.vue @@ -24,8 +24,12 @@ <div class="ques-header__input-block"> <span>Email嚗�</span> <input class="ques-header__input" + :class="{ 'is-invalid': !emailValid}" placeholder="隢撓�" v-model="myRequest.email"> + </div> + <div class="error mt-5 mb-5" style="margin-left:65px"> + <span v-show="!emailValid">Email�撘�炊</span> </div> </div> </div> @@ -117,19 +121,24 @@ <PopUpFrame :isOpen.sync="sendReserve" @update:isOpen="closeReservePopUp"> <div class="mdTxt mt-30 sendReserve-txt">�������</div> <div class="mdTxt sendReserve-txt mb-30">�����“�������蝯∴��</div> - <div class="pam-app-review mb-10"> + <!-- TODO: �銝脫 api, ���像�皛踵�漲 --> + <!-- <div class="pam-app-review mb-10"> <div class="mdTxt mb-10">撠 <span class="mdTxt text--primary text--bold ">������</span> 撟喳��擃���� </div> <div class="mdTxt">�蝯虫�嗾憿��嚗�</div> </div> - <el-rate v-model="score" class="pam-satisfaction-rate fix-chrome-click--issue"></el-rate> + <el-rate v-model="score" class="pam-satisfaction-rate fix-chrome-click--issue"></el-rate> --> <div class="text--center mdTxt"> - <el-button @click="closeReservePopUp">����</el-button> + <!-- <el-button @click="closeReservePopUp">����</el-button> <el-button type="primary" @click="closeReservePopUp"> � + </el-button> --> + <el-button type="primary" + @click="closeReservePopUp"> + ������ </el-button> </div> </PopUpFrame> @@ -466,6 +475,11 @@ : true; } + get emailValid() { + const rule = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/; + return this.myRequest.email ? rule.test(this.myRequest.email) : true; + } + get userInfo(): RegisterInfo { const initUserInfo = JSON.parse(localStorage.getItem('userInfo')!); return initUserInfo; @@ -473,7 +487,7 @@ get isDisabledSubmitBtn(): boolean { return _.includes(this.myRequest.contactType,ContactType.PHONE) - ? !this.isHopeContactTimeDone() + ? !this.isHopeContactTimeDone() || !this.emailValid : !this.phoneValid; } diff --git a/PAMapp/pages/satisfactionList.vue b/PAMapp/pages/satisfactionList.vue index a6bae69..8d52771 100644 --- a/PAMapp/pages/satisfactionList.vue +++ b/PAMapp/pages/satisfactionList.vue @@ -14,18 +14,32 @@ ��擃���蝯虫�嗾憿��嚗� </div> </div> - <el-rate v-model="item.score" class="pam-satisfaction-rate mt-10 fix-chrome-click--issue"></el-rate> + <el-rate + v-model="item.satisfaction" + class="pam-satisfaction-rate mt-10 fix-chrome-click--issue" + @change="isBtnDisabled = false" + ></el-rate> </div> - <div class="text--center mt-30"> - <el-button type="primary" :disabled="isBtnDisabled">�</el-button> + <div class="text--center mt-30" v-if="mapUnReviewLogList.length"> + <el-button type="primary" :disabled="isBtnDisabled" @click="sent">�</el-button> </div> </div> + + <PopUpFrame :isOpen.sync="showConfirmPopup" + @closePopUp="closePopup"> + <div class="text--center mdTxt">�����</div> + <div class="text--center mt-30"> + <el-button @click="closePopup" type="primary">蝣箏��</el-button> + </div> + </PopUpFrame> </div> </template> <script lang="ts"> import { Vue, Component, Action, State, Watch } from 'nuxt-property-decorator'; import { AppointmentLog } from '~/shared/models/appointment.model'; +import { UserReviewsConsultantsParams } from '~/shared/models/reviews.model'; +import reviewsService from '~/shared/services/reviews.service'; @Component({ layout: 'home' @@ -35,7 +49,12 @@ @State unReviewLogList!: AppointmentLog[]; + @Action + storeMyAppointmentReviewLog!: () => void; + mapUnReviewLogList: AppointmentReviewLog[] = []; + showConfirmPopup = false; + isBtnDisabled = true; /////////////////////////////////////////////////////// @@ -53,12 +72,26 @@ /////////////////////////////////////////////////////// - get isBtnDisabled() { - if (this.mapUnReviewLogList.length) { - return this.mapUnReviewLogList.findIndex(item => item.satisfaction > 0) === -1; - } - return false; + sent() { + const reviewParams: UserReviewsConsultantsParams[] = this.mapUnReviewLogList + .filter(item => item.satisfaction > 0) + .map(item => { + return { + appointmentId: item.appointmentId, + score: item.satisfaction + } + }) + + reviewsService.allUserReviewsConsultants(reviewParams).then((res) => { + this.showConfirmPopup = true; + }); } + + closePopup() { + this.showConfirmPopup = false; + this.storeMyAppointmentReviewLog(); + } + } interface AppointmentReviewLog extends AppointmentLog { diff --git a/PAMapp/shared/models/strict-query.model.ts b/PAMapp/shared/models/strict-query.model.ts index 59892c1..45a5f31 100644 --- a/PAMapp/shared/models/strict-query.model.ts +++ b/PAMapp/shared/models/strict-query.model.ts @@ -1,14 +1,14 @@ export interface StrictQueryParams { - gender : string; - avgScore : number; - status : string; //phase 1 disable - area : string; - requirements : string[]; - otherRequirement: string; - seniority : string; - popularTags : string[]; - otherPopularTags: string; + gender? : string; + avgScore? : number; + status? : string; //phase 1 disable + area? : string; + requirements? : string[]; + otherRequirement?: string; + seniority? : string; + popularTags? : string[]; + otherPopularTags?: string; } export interface AgentOfStrictQuery { diff --git a/PAMapp/shared/services/httpClient.ts b/PAMapp/shared/services/httpClient.ts index e524ddf..bafed6e 100644 --- a/PAMapp/shared/services/httpClient.ts +++ b/PAMapp/shared/services/httpClient.ts @@ -16,8 +16,11 @@ withCredentials: true }); +let apiNumber = 0; + http.interceptors.request.use( (config: AxiosRequestConfig) => { + apiNumber += 1; loadingStart(); addHttpHeader(config); return config; @@ -26,11 +29,17 @@ http.interceptors.response.use( (response: AxiosResponse) => { - loadingFinish(); + apiNumber -= 1; + if (apiNumber === 0) { + loadingFinish(); + } return response; }, (error: AxiosError) => { - loadingFinish(); + apiNumber -= 1; + if (apiNumber === 0) { + loadingFinish(); + } showErrorMessageBox(error) return Promise.reject(error); } diff --git a/PAMapp/shared/services/reviews.service.ts b/PAMapp/shared/services/reviews.service.ts index 44c51d9..4bd81df 100644 --- a/PAMapp/shared/services/reviews.service.ts +++ b/PAMapp/shared/services/reviews.service.ts @@ -5,11 +5,17 @@ class ReviewsService { - //摰X�脰�遛��漲閰�� + //摰X�脰�遛��漲閰��(�蝑�) userReviewsConsultants(data: UserReviewsConsultantsParams) { - return http.post('/satisfaction/score', data ); + return http.post('/satisfaction/score', data); } + // 摰X�脰�遛��漲(憭��) + allUserReviewsConsultants(data: UserReviewsConsultantsParams[]) { + return http.post('/satisfaction/score/all', data); + } + + //������������ async getMyReviewLog(): Promise<AppointmentLog[]> { return http.get('/satisfaction/getMySatisfaction').then(res => res.data); diff --git a/PAMapp/store/localStorage.ts b/PAMapp/store/localStorage.ts index 9ad38e9..a08b69a 100644 --- a/PAMapp/store/localStorage.ts +++ b/PAMapp/store/localStorage.ts @@ -1,6 +1,7 @@ import { Module, Mutation, VuexModule ,Action } from 'vuex-module-decorators'; import { Role } from '~/shared/models/enum/Role'; import { Selected } from '~/shared/models/quick-filter.model'; +import { StrictQueryParams } from '~/shared/models/strict-query.model'; @Module export default class LocalStorage extends VuexModule { id_token = localStorage.getItem('id_token'); @@ -10,6 +11,7 @@ recommendConsultantItem = localStorage.getItem('recommendConsultantItem'); appointmentIdFromMsg = localStorage.getItem('appointmentIdFromMsg'); satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); + notContactAppointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg'); get idToken(): string|null { return this.id_token; @@ -41,6 +43,10 @@ get currentSatisfactionIdFromMsg(): string|null { return this.satisfactionIdFromMsg; + } + + get currentNotContactAppointmentIdFromMsg(): string|null { + return this.notContactAppointmentIdFromMsg; } @Mutation storageIdToken(token: string): void { @@ -78,6 +84,11 @@ this.satisfactionIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); } + @Mutation storageNotContactAppointmentIdFromMsg(id: string) { + localStorage.setItem('notContactAppointmentIdFromMsg', id); + this.notContactAppointmentIdFromMsg = id; + } + @Mutation storageClear(): void { localStorage.removeItem('myRequests'); localStorage.removeItem('userInfo'); @@ -112,6 +123,16 @@ this.appointmentIdFromMsg = localStorage.getItem('satisfactionIdFromMsg'); } + @Mutation storageClearNotContactAppointmentIdFromMsg() { + localStorage.removeItem('notContactAppointmentIdFromMsg'); + this.appointmentIdFromMsg = localStorage.getItem('notContactAppointmentIdFromMsg'); + } + + @Mutation storageStrickQueryItem(queryItem: StrictQueryParams): void { + localStorage.setItem('recommendConsultantItem', JSON.stringify(queryItem)); + this.recommendConsultantItem = localStorage.getItem('recommendConsultantItem'); + } + @Action actionStorageClear(): void { this.context.commit("storageClear"); } diff --git a/pamapi/src/doc/sql/20220122_w.sql b/pamapi/src/doc/sql/20220122_w.sql index 1acd577..39a28b8 100644 --- a/pamapi/src/doc/sql/20220122_w.sql +++ b/pamapi/src/doc/sql/20220122_w.sql @@ -2,5 +2,5 @@ id bigserial NOT NULL, appointment_id bigserial NOT NULL, send_time timestamp NULL, - CONSTRAINT appointment_pending_notify_record_pk PRIMARY KEY (id) + CONSTRAINT appointment_expiring_notify_record_pk PRIMARY KEY (id) ); diff --git "a/pamapi/src/doc/sql/\346\267\250\347\251\272\346\225\264\345\200\213\347\263\273\347\265\261\350\263\207\346\226\231\050\351\231\244\351\241\247\345\225\217\051.sql" "b/pamapi/src/doc/sql/\346\267\250\347\251\272\346\225\264\345\200\213\347\263\273\347\265\261\350\263\207\346\226\231\050\351\231\244\351\241\247\345\225\217\051.sql" new file mode 100644 index 0000000..963ed9f --- /dev/null +++ "b/pamapi/src/doc/sql/\346\267\250\347\251\272\346\225\264\345\200\213\347\263\273\347\265\261\350\263\207\346\226\231\050\351\231\244\351\241\247\345\225\217\051.sql" @@ -0,0 +1,12 @@ +truncate public.appointment; +truncate public.appointment_closed_info; +truncate public.appointment_expiring_notify_record; +truncate public.appointment_memo; +truncate public.appointment_notice_log; +truncate public.customer; +truncate public.interview_record; +truncate public.login_record; +truncate public.otp_tmp; +truncate public.personal_notification; +truncate public.satisfaction; +truncate public.customer_favorite_consultant; 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" index d31a4ea..f4e02f9 100644 --- "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" @@ -4,7 +4,7 @@ 蝪∟��mail��誑閰脩雯���脣擐�� -> http://localhost:3000?notContactAppointmentId={���銝�蝑�����} -response body: ����200銝衣策隞乩�����(���遙雿�暹������嚗����404) +response body: ����200銝衣策隞乩�����(���遙雿�暹������嚗���ull) { "id": 385, "phone": "0911223344", diff --git a/pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java b/pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java index d4b2b36..60b33ca 100644 --- a/pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java +++ b/pamapi/src/main/java/com/pollex/pam/config/ApplicationProperties.java @@ -1,5 +1,6 @@ package com.pollex.pam.config; +import com.pollex.pam.enums.SendEmailMsgMethod; import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -19,7 +20,6 @@ private String eServiceLoginFunc; private String eServiceLoginSys; private String frontEndDomain; - private boolean sendNotifyMsg; private SMS sms; private Email email; private String fileFolderPath; @@ -88,14 +88,6 @@ this.frontEndDomain = frontEndDomain; } - public boolean isSendNotifyMsg() { - return sendNotifyMsg; - } - - public void setSendNotifyMsg(boolean sendNotifyMsg) { - this.sendNotifyMsg = sendNotifyMsg; - } - public SMS getSms() { return sms; } @@ -118,6 +110,7 @@ private String sender; private String smsType; private String subject; + private boolean sendNotifyMsg; public String getUrl() { return url; @@ -158,12 +151,22 @@ public void setSubject(String subject) { this.subject = subject; } + + public boolean isSendNotifyMsg() { + return sendNotifyMsg; + } + + public void setSendNotifyMsg(boolean sendNotifyMsg) { + this.sendNotifyMsg = sendNotifyMsg; + } } public static class Email { private String url; private String functionId; private String senderEmail; + private boolean sendNotifyMsg; + private SendEmailMsgMethod method; public String getUrl() { return url; @@ -188,6 +191,22 @@ public void setSenderEmail(String senderEmail) { this.senderEmail = senderEmail; } + + public boolean isSendNotifyMsg() { + return sendNotifyMsg; + } + + public void setSendNotifyMsg(boolean sendNotifyMsg) { + this.sendNotifyMsg = sendNotifyMsg; + } + + public SendEmailMsgMethod getMethod() { + return method; + } + + public void setMethod(SendEmailMsgMethod method) { + this.method = method; + } } public String getFileFolderPath() { return fileFolderPath; 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 aae9b67..87dd25b 100644 --- a/pamapi/src/main/java/com/pollex/pam/config/Constants.java +++ b/pamapi/src/main/java/com/pollex/pam/config/Constants.java @@ -30,5 +30,7 @@ */ public static final int SEND_EXPIRING_NOTIFY_LIMIT = 1; + public static final String SPRING_PROFILE_POLLEX_DEVELOPMENT = "pollex"; + private Constants() {} } diff --git a/pamapi/src/main/java/com/pollex/pam/enums/SendEmailMsgMethod.java b/pamapi/src/main/java/com/pollex/pam/enums/SendEmailMsgMethod.java new file mode 100644 index 0000000..42a10ce --- /dev/null +++ b/pamapi/src/main/java/com/pollex/pam/enums/SendEmailMsgMethod.java @@ -0,0 +1,6 @@ +package com.pollex.pam.enums; + +public enum SendEmailMsgMethod { + PAM_EMAIL_SERVICE, + POLLEX_GMAIL +} 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 6d5ded6..3ae99c4 100644 --- a/pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java +++ b/pamapi/src/main/java/com/pollex/pam/service/AppointmentService.java @@ -200,7 +200,8 @@ public void sendAppointmentNotify(Appointment appointment) { Assert.notNull(appointment, "appointment entity cannot be null"); - log.debug("is need send appointment notify msg? = {}", applicationProperties.isSendNotifyMsg()); + 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); 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 604479e..57324b5 100644 --- a/pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java +++ b/pamapi/src/main/java/com/pollex/pam/service/ScheduleTaskService.java @@ -73,12 +73,17 @@ consultantWithPendingAppointments.forEach((agentNo, pendingAppointments) -> { int pendingAppointmentsSum = pendingAppointments.size(); Consultant consultant = consultantService.findByAgentNo(agentNo); - String consultantPhoneNumber = consultant.getPhoneNumber(); - String consultantEmail = consultant.getEmail(); + Optional<String> optionalPhone = Optional.ofNullable(consultant.getPhoneNumber()).filter(StringUtils::hasText); + Optional<String> optionalEmail = Optional.ofNullable(consultant.getEmail()).filter(StringUtils::hasText); + String emailContent = getAppointmentPendingNotifyEmailContent(pendingAppointmentsSum); - sendMsgService.sendMsgBySMS(consultantPhoneNumber, String.format("����%s������脰�蝜恬������", pendingAppointmentsSum)); - sendMsgService.sendMsgByEmail(consultantEmail, NOT_CONTACTED_NOTIFY_SUBJECT, emailContent, true); + optionalPhone.ifPresent(phone -> { + sendMsgService.sendMsgBySMS(phone, String.format("����%s������脰�蝜恬������", pendingAppointmentsSum)); + }); + optionalEmail.ifPresent(email -> { + sendMsgService.sendMsgByEmail(email, NOT_CONTACTED_NOTIFY_SUBJECT, emailContent, true); + }); }); log.info("Sending appointment pending notify to consultant finish"); @@ -94,7 +99,7 @@ .filter(appointment -> appointmentService.isAppointmentDateNotInIntervalFromNow(appointment, Constants.APPOINTMENT_EXPIRING_PHONE_INTERVAL, Constants.APPOINTMENT_EXPIRING_EMAIL_INTERVAL) ) - .filter(this::isAppointmentNotifyNotOnLimit) + .filter(this::isAppointmentExpiringNotifyNotOnLimit) .collect(Collectors.toList()); allByCommunicateStatus.forEach(appointment -> { @@ -104,10 +109,10 @@ optionalPhone.ifPresent(phone -> sendMsgService.sendMsgBySMS(phone, String.format("敺甇�����%s憿批�迤敹�葉嚗������蒂���隞“������雯��嚗�%s" - , consultant.getName(), getAppointmentUrl(appointment.getId()))) + , consultant.getName(), getAppointmentExpiringNotifyUrl(appointment.getId()))) ); optionalEmail.ifPresent(email -> - sendMsgService.sendMsgByEmail(email, NOT_CONTACTED_NOTIFY_SUBJECT, getAppointmentExpiringNotifyEmail(consultant.getName(), getAppointmentUrl(appointment.getId())), true) + sendMsgService.sendMsgByEmail(email, NOT_CONTACTED_NOTIFY_SUBJECT, getAppointmentExpiringNotifyEmail(consultant.getName(), getAppointmentExpiringNotifyUrl(appointment.getId())), true) ); AppointmentExpiringNotifyRecord record = new AppointmentExpiringNotifyRecord(); @@ -121,7 +126,7 @@ } // todo ��蝣箄�府����, otis todo=134497 - @Scheduled(cron = "0 0 9 * * *") + @Scheduled(cron = "0 30 8 * * *") public void sendNotFillSatisfactionToPersonalNotification() { Map<Long, List<Satisfaction>> customerNotFillSatisfactions = satisfactionService.getByStatus(SatisfactionStatusEnum.UNFILLED) .stream() @@ -132,14 +137,14 @@ ); } - private boolean isAppointmentNotifyNotOnLimit(AppointmentCustomerView appointment) { + private boolean isAppointmentExpiringNotifyNotOnLimit(AppointmentCustomerView appointment) { int sendNotifyToCustomerRecordSum = appointmentExpiringNotifyRecordRepository.findAllByAppointmentId(appointment.getId()).size(); return sendNotifyToCustomerRecordSum < Constants.SEND_EXPIRING_NOTIFY_LIMIT; } - private String getAppointmentUrl(Long appointmentId) { + private String getAppointmentExpiringNotifyUrl(Long appointmentId) { return applicationProperties.getFrontEndDomain() + "?notContactAppointmentId=" + appointmentId; } diff --git a/pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java b/pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java index 05d8d73..a5be71e 100644 --- a/pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java +++ b/pamapi/src/main/java/com/pollex/pam/service/SendMsgService.java @@ -2,7 +2,10 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.pollex.pam.config.ApplicationProperties; +import com.pollex.pam.config.ApplicationProperties.Email; import com.pollex.pam.config.ApplicationProperties.SMS; +import com.pollex.pam.config.Constants; +import com.pollex.pam.enums.SendEmailMsgMethod; import com.pollex.pam.repository.ConsultantRepository; import com.pollex.pam.service.dto.*; import com.pollex.pam.service.util.HttpRequestUtil; @@ -11,9 +14,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.env.Environment; +import org.springframework.core.env.Profiles; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.thymeleaf.spring5.SpringTemplateEngine; +import tech.jhipster.config.JHipsterConstants; import java.nio.charset.StandardCharsets; import java.time.LocalDateTime; @@ -38,13 +44,19 @@ @Autowired SpringTemplateEngine springTemplateEngine; + @Autowired + Environment environment; + + @Autowired + MailService mailService; + public SendSMSResponse sendMsgBySMS(String toMobile, String content) throws SendSMSFailException { - if(!applicationProperties.isSendNotifyMsg()) { + + SMS smsProperties = applicationProperties.getSms(); + if(!smsProperties.isSendNotifyMsg()) { // return getMockSMSResponse(); - return null; - } - - SMS smsProperties = applicationProperties.getSms(); + return null; + } SendSMSRequest sendSMSRequest = new SendSMSRequest(); sendSMSRequest.setpKey(UUID.randomUUID().toString()); @@ -94,7 +106,7 @@ public String sendMsgByEmail(String toAddress, String subject, String content, boolean htmlFormat, List<String> toCCAddress, List<String> attachments) throws SendEmailFailException { String fromAddress = applicationProperties.getEmail().getSenderEmail(); - + SendMailRequest sendMailRequest = new SendMailRequest(); sendMailRequest.setSendMailAddresses(Collections.singletonList(toAddress)); sendMailRequest.setFrom(fromAddress); @@ -109,25 +121,48 @@ } public String sendMsgByEmail(SendMailRequest sendMailRequest) throws SendEmailFailException{ - if(!applicationProperties.isSendNotifyMsg()) { - return null; - } - try { + final Email emailProperties = applicationProperties.getEmail(); + + if(!emailProperties.isSendNotifyMsg()) { + return null; + } + + if(emailProperties.getMethod() == SendEmailMsgMethod.POLLEX_GMAIL) { + return sendMsgByPollexGmail(sendMailRequest); + } + else if(emailProperties.getMethod() == SendEmailMsgMethod.PAM_EMAIL_SERVICE) { + return sendMsgByPamEmailService(sendMailRequest); + } + + return null; + } + + private String sendMsgByPollexGmail(SendMailRequest sendMailRequest) { + String subject = sendMailRequest.getSubject(); + String content = sendMailRequest.getContent(); + boolean isHtml = sendMailRequest.isHtmlFormat(); + sendMailRequest.getSendMailAddresses().forEach(receiver -> mailService.sendEmail(receiver, subject, content, false, isHtml)); + + return null; + } + + private String sendMsgByPamEmailService(SendMailRequest sendMailRequest) { + final Email emailProperties = applicationProperties.getEmail(); + try { ResponseEntity<String> responseEntity = - HttpRequestUtil.postWithJson( applicationProperties.getEmail().getUrl(), sendMailRequest, String.class); + HttpRequestUtil.postWithJson(emailProperties.getUrl(), sendMailRequest, String.class); log.debug("responseEntity = {}", responseEntity); String rawResponseString = responseEntity.getBody(); SendMailResponse sendMailResponse = new ObjectMapper().readValue(rawResponseString, SendMailResponse.class); log.debug("sendMailResponse = {}", sendMailResponse); - if(sendMailResponse == null || sendMailResponse.getData() == null || !"ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { + if (sendMailResponse == null || sendMailResponse.getData() == null || !"ADDED".equalsIgnoreCase(sendMailResponse.getData().getMessageStatus())) { throw new SendEmailFailException("send email service return error msg! raw response string= " + rawResponseString); } return responseEntity.getBody(); - } - catch (SendEmailFailException e) { + } catch (SendEmailFailException e) { throw e; } catch (Exception e) { log.warn("send email fail by other reason", e); 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 ac19b72..6ac1d5f 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 @@ -86,12 +86,7 @@ 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); - } + return new ResponseEntity<>(customerNewestExpiringAppointment, HttpStatus.OK); } @GetMapping("/consultant/pending/sum") diff --git a/pamapi/src/main/resources/config/application-dev.yml b/pamapi/src/main/resources/config/application-dev.yml index ced80df..e612913 100644 --- a/pamapi/src/main/resources/config/application-dev.yml +++ b/pamapi/src/main/resources/config/application-dev.yml @@ -45,10 +45,16 @@ # Remove 'faker' if you do not want the sample data to be loaded automatically contexts: dev, faker mail: - host: localhost - port: 25 - username: - password: + host: smtp.gmail.com + port: 587 + username: pollex.testing@gmail.com + password: ilismmmhtscppxft + properties: + mail: + smtp: + auth: true + starttls: + enable: true messages: cache-duration: PT1S # 1 second, see the ISO 8601 standard thymeleaf: @@ -120,15 +126,17 @@ e-service-login-func: ValidateUsrLogin e-service-login-sys: epos front-end-domain: http://localhost:3000 - send-notify-msg: false sms: + send-notify-msg: false url: https://localhost:8081/testSMS source-code: ePos sender: POS sms-type: '0017' subject: '慦�像��' email: + send-notify-msg: false url: https://localhost:8081/testEmail function-id: epos sender-email: noreply@pcalife.com.tw + method: 'POLLEX_GMAIL' file-folder-path: C://pam_file diff --git a/pamapi/src/main/resources/config/application-pollex.yml b/pamapi/src/main/resources/config/application-pollex.yml index e1b1955..e6a2ae8 100644 --- a/pamapi/src/main/resources/config/application-pollex.yml +++ b/pamapi/src/main/resources/config/application-pollex.yml @@ -43,10 +43,16 @@ # Remove 'faker' if you do not want the sample data to be loaded automatically contexts: pollex, faker mail: - host: localhost - port: 25 - username: - password: + host: smtp.gmail.com + port: 587 + username: pollex.testing@gmail.com + password: ilismmmhtscppxft + properties: + mail: + smtp: + auth: true + starttls: + enable: true messages: cache-duration: PT1S # 1 second, see the ISO 8601 standard thymeleaf: @@ -118,15 +124,17 @@ 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: + send-notify-msg: false url: https://localhost:8081/testSMS source-code: ePos sender: POS sms-type: '0017' subject: '慦�像��' email: + send-notify-msg: true url: https://localhost:8081/testEmail function-id: epos sender-email: noreply@pcalife.com.tw + method: 'POLLEX_GMAIL' file-folder-path: C://pam_file diff --git a/pamapi/src/main/resources/config/application-prod.yml b/pamapi/src/main/resources/config/application-prod.yml index 71773a9..90ff8b7 100644 --- a/pamapi/src/main/resources/config/application-prod.yml +++ b/pamapi/src/main/resources/config/application-prod.yml @@ -140,15 +140,17 @@ e-service-login-func: ValidateUsrLogin e-service-login-sys: epos front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam - send-notify-msg: true sms: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource source-code: ePos sender: POS sms-type: '0017' subject: '慦�像��' email: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail function-id: epos sender-email: noreply@pcalife.com.tw + method: 'PAM_EMAIL_SERVICE' file-folder-path: /sfs_omo/vtwlifewpsfs01/SensitiveData4AP$/OMO diff --git a/pamapi/src/main/resources/config/application-sit.yml b/pamapi/src/main/resources/config/application-sit.yml index 4f4c25c..402dcef 100644 --- a/pamapi/src/main/resources/config/application-sit.yml +++ b/pamapi/src/main/resources/config/application-sit.yml @@ -118,15 +118,17 @@ e-service-login-func: ValidateUsrLogin e-service-login-sys: epos front-end-domain: https://vtwlifeopensyssit.pru.intranet.asia/pam - send-notify-msg: true sms: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource source-code: ePos sender: POS sms-type: '0017' subject: '慦�像��' email: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail function-id: epos sender-email: noreply@pcalife.com.tw + method: 'PAM_EMAIL_SERVICE' file-folder-path: /sfs_omo/vtwlifewuftp66/sensitivedata4ap$/OMOSIT diff --git a/pamapi/src/main/resources/config/application-uat.yml b/pamapi/src/main/resources/config/application-uat.yml index 15f5390..28d8c16 100644 --- a/pamapi/src/main/resources/config/application-uat.yml +++ b/pamapi/src/main/resources/config/application-uat.yml @@ -118,15 +118,17 @@ e-service-login-func: ValidateUsrLogin e-service-login-sys: epos front-end-domain: https://vtwlifeopensysuat.pru.intranet.asia/pam - send-notify-msg: true sms: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/MesgQueueMgmnt/rest/smsSendMsgResource source-code: ePos sender: POS sms-type: '0017' subject: '慦�像��' email: + send-notify-msg: true url: https://vtwlifeopensysuat.pru.intranet.asia/tsgw/mq/mqSendMail function-id: epos sender-email: noreply@pcalife.com.tw + method: 'PAM_EMAIL_SERVICE' file-folder-path: /sfs_omo/vtwlifewuftp66/sensitivedata4ap$/OMO -- Gitblit v1.8.0