diff --git a/ghy-order/src/main/java/com/ghy/order/domain/AfterServiceRecord.java b/ghy-order/src/main/java/com/ghy/order/domain/AfterServiceRecord.java index ca5a9485..6453d669 100644 --- a/ghy-order/src/main/java/com/ghy/order/domain/AfterServiceRecord.java +++ b/ghy-order/src/main/java/com/ghy/order/domain/AfterServiceRecord.java @@ -85,6 +85,10 @@ public class AfterServiceRecord extends BaseEntity @Excel(name = "重做/补做完成图片") private String redoCompleteImages; + /** 是否自动处理:0-否,1-是(用于防止重复处理) */ + @Excel(name = "是否自动处理:0-否,1-是") + private Integer isAutoProcessed; + private boolean excludeAfterServiceFinished; private List imgsList; diff --git a/ghy-order/src/main/java/com/ghy/order/mapper/AfterServiceRecordMapper.java b/ghy-order/src/main/java/com/ghy/order/mapper/AfterServiceRecordMapper.java index 33fae398..c66beed6 100644 --- a/ghy-order/src/main/java/com/ghy/order/mapper/AfterServiceRecordMapper.java +++ b/ghy-order/src/main/java/com/ghy/order/mapper/AfterServiceRecordMapper.java @@ -65,4 +65,21 @@ public interface AfterServiceRecordMapper { * @return 未完成的售后记录 */ AfterServiceRecord unfinished(Long orderDetailId); + + /** + * 查询师傅反馈超时的售后记录 + * 倒计时1:客户发起后,师傅24小时不操作任何反馈 + * + * @return 超时的售后记录列表 + */ + List selectWorkerFeedbackTimeoutRecords(); + + /** + * 查询客户确认超时的售后记录 + * 倒计时2:师傅重做完成后客户36小时不操作 + * 倒计时3:师傅拒绝后客户36小时不操作 + * + * @return 超时的售后记录列表 + */ + List selectCustomerConfirmTimeoutRecords(); } diff --git a/ghy-order/src/main/java/com/ghy/order/quartz/AfterServiceTimeoutTask.java b/ghy-order/src/main/java/com/ghy/order/quartz/AfterServiceTimeoutTask.java new file mode 100644 index 00000000..47b9da02 --- /dev/null +++ b/ghy-order/src/main/java/com/ghy/order/quartz/AfterServiceTimeoutTask.java @@ -0,0 +1,136 @@ +package com.ghy.order.quartz; + +import com.ghy.common.utils.DateUtils; +import com.ghy.order.domain.AfterServiceRecord; +import com.ghy.order.service.IAfterServiceRecordService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.util.Date; +import java.util.List; + +/** + * @Date: 2024-12-19 + * @Author: 系统 + * @Version: v1.0 + * @Description: 售后倒计时定时任务 + */ +@Slf4j +@Component +@RequiredArgsConstructor(onConstructor = @__(@Autowired)) +public class AfterServiceTimeoutTask { + + private final IAfterServiceRecordService afterServiceRecordService; + + /** + * 售后倒计时定时任务 - 每5分钟执行一次 + * 处理3种倒计时场景: + * 1. 师傅24小时不操作自动同意完单 + * 2. 师傅重做完成后客户36小时不操作自动同意完单 + * 3. 师傅拒绝后客户36小时不操作自动取消售后 + */ + @Scheduled(cron = "0 0/5 * * * ?") + public void afterServiceTimeoutProcess() { + log.info("{}开始售后倒计时定时任务", DateUtils.timeFormat(new Date())); + + try { + // 处理师傅24小时不操作的情况 + processWorkerFeedbackTimeout(); + + // 处理客户36小时不操作的情况 + processCustomerConfirmTimeout(); + + log.info("{}售后倒计时定时任务执行完成", DateUtils.timeFormat(new Date())); + } catch (Exception e) { + log.error("售后倒计时定时任务执行异常", e); + } + } + + /** + * 处理师傅24小时不操作的情况 + * 倒计时1:客户发起后,师傅24小时不操作任何反馈,直接按同意完单 + */ + private void processWorkerFeedbackTimeout() { + log.info("开始处理师傅反馈超时情况"); + + // 查询需要处理的售后记录 + List timeoutRecords = afterServiceRecordService.selectWorkerFeedbackTimeoutRecords(); + + for (AfterServiceRecord record : timeoutRecords) { + try { + log.info("处理师傅反馈超时售后记录:{}", record.getId()); + + // 师傅24小时不操作,直接设置为师傅同意并客户同意,完成售后 + record.setWorkerFeedbackResult(1L); + record.setCustomerFinalCheck(1L); + record.setRefundApplyTime(new Date()); + record.setIsAutoProcessed(1); // 自动处理 + + // 更新记录 + afterServiceRecordService.updateAfterServiceRecord(record); + + // 师傅24小时不操作,需要执行退款逻辑 + try { + afterServiceRecordService.executeRefundLogic(record); + log.info("师傅24小时不操作,自动执行退款逻辑完成,售后记录ID:{}", record.getId()); + } catch (Exception e) { + log.error("师傅24小时不操作,自动执行退款逻辑异常,售后记录ID:{}", record.getId(), e); + } + + log.info("师傅反馈超时自动处理完成,售后记录ID:{}", record.getId()); + } catch (Exception e) { + log.error("处理师傅反馈超时异常,售后记录ID:{}", record.getId(), e); + } + } + + log.info("师傅反馈超时处理完成,共处理{}条记录", timeoutRecords.size()); + } + + /** + * 处理客户36小时不操作的情况 + * 倒计时2:师傅重做完成后客户36小时不操作自动同意完单 + * 倒计时3:师傅拒绝后客户36小时不操作自动取消售后 + */ + private void processCustomerConfirmTimeout() { + log.info("开始处理客户确认超时情况"); + + // 查询需要处理的售后记录 + List timeoutRecords = afterServiceRecordService.selectCustomerConfirmTimeoutRecords(); + + for (AfterServiceRecord record : timeoutRecords) { + try { + log.info("处理客户确认超时售后记录:{}", record.getId()); + + // 根据师傅反馈结果决定处理方式 + if (record.getWorkerFeedbackResult() != null && record.getWorkerFeedbackResult().equals(3L)) { + // 倒计时2:师傅重做完成后客户36小时不操作自动同意完单 + record.setCustomerFinalCheck(1L); + // 师傅重做完成,客户同意,订单完成,不需要退款,不设置refundApplyTime + record.setIsAutoProcessed(1); // 自动处理 + + log.info("师傅重做完成后客户超时自动同意,订单完成,售后记录ID:{}", record.getId()); + } else if (record.getWorkerFeedbackResult() != null && + (record.getWorkerFeedbackResult().equals(0L) || record.getWorkerFeedbackResult().equals(1L))) { + // 倒计时3:师傅拒绝或同意后客户36小时不操作自动取消售后 + record.setCustomerFinalCheck(NULL); + // 售后单取消完成,不需要退款,不设置refundApplyTime + record.setIsAutoProcessed(1); // 自动处理 + + log.info("师傅拒绝/同意后客户超时自动取消,售后单取消完成,售后记录ID:{}", record.getId()); + } + + // 更新记录 + afterServiceRecordService.updateAfterServiceRecord(record); + + log.info("客户确认超时自动处理完成,售后记录ID:{}", record.getId()); + } catch (Exception e) { + log.error("处理客户确认超时异常,售后记录ID:{}", record.getId(), e); + } + } + + log.info("客户确认超时处理完成,共处理{}条记录", timeoutRecords.size()); + } +} diff --git a/ghy-order/src/main/java/com/ghy/order/service/IAfterServiceRecordService.java b/ghy-order/src/main/java/com/ghy/order/service/IAfterServiceRecordService.java index 8aa93a55..916e08d2 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/IAfterServiceRecordService.java +++ b/ghy-order/src/main/java/com/ghy/order/service/IAfterServiceRecordService.java @@ -61,4 +61,29 @@ public interface IAfterServiceRecordService { * @return 结果 */ int deleteAfterServiceRecordById(String id); + + /** + * 查询师傅反馈超时的售后记录 + * 倒计时1:客户发起后,师傅24小时不操作任何反馈 + * + * @return 超时的售后记录列表 + */ + List selectWorkerFeedbackTimeoutRecords(); + + /** + * 查询客户确认超时的售后记录 + * 倒计时2:师傅重做完成后客户36小时不操作 + * 倒计时3:师傅拒绝后客户36小时不操作 + * + * @return 超时的售后记录列表 + */ + List selectCustomerConfirmTimeoutRecords(); + + /** + * 执行退款逻辑 + * 用于定时器自动处理退款 + * + * @param afterServiceRecord 售后记录 + */ + void executeRefundLogic(AfterServiceRecord afterServiceRecord); } diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/AfterServiceRecordServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/AfterServiceRecordServiceImpl.java index e4469ce3..9570b0fe 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/impl/AfterServiceRecordServiceImpl.java +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/AfterServiceRecordServiceImpl.java @@ -209,8 +209,41 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService } if (param.getCustomerFinalCheck()!=null ) { - // 师傅同意 且 客户同意退款 - //afterServiceRecord.setCustomerFinalCheck(1L); + // 检查师傅是否已经重做完成 + if (afterServiceRecord.getWorkerFeedbackResult() != null && afterServiceRecord.getWorkerFeedbackResult().equals(3L)) { + // 师傅已重做完成 + if (one.equals(param.getCustomerFinalCheck())) { + // 客户同意重做结果,订单完成结束,不执行退款 + afterServiceRecord.setCustomerFinalCheck(1L); + afterServiceRecord.setRefundApplyTime(new Date()); + log.info("师傅重做完成后,客户同意重做结果,订单完成结束,不执行退款"); + + // // 售后客户回复通知 + // try { + // Worker worker = workerService.selectById(orderDetail.getWorkerId()); + // OrderMaster orderMaster = orderMasterService.selectById(orderDetail.getOrderMasterId()); + // // 推送公众号通知数据。 + // Map paramsNew = new HashMap<>(); + // paramsNew.put("thing9", "您有1条售后/投诉单,客户已回复"); + // paramsNew.put("thing11", "请进入【我的订单--售后中】查看处理"); + // CustomerAddress address = customerAddressService.selectByCustomerAddressId(orderMaster.getAddressId()); + // paramsNew.put("thing10", address.getName()); + // paramsNew.put("time13", com.ghy.common.utils.DateUtils.parseDateToStr("yyyy年MM月dd日 HH:mm", new Date())); + // WechatMsgUtils.sendWeChatMsg(WechatMsgUtils.getToken(), worker.getWxOpenId(), WxMsgEnum.AFTER_SALES_ORDER, paramsNew); + // } catch (Exception e) { + // log.error(e.getMessage(), e); + // } + + afterServiceRecordMapper.updateAfterServiceRecord(afterServiceRecord); + return AjaxResult.success("订单完成结束"); + } else { + // 客户不同意重做结果,按之前的逻辑走(可能需要退款) + log.info("师傅重做完成后,客户不同意重做结果,按原逻辑处理"); + // 继续执行下面的退款逻辑 + } + } + + // 师傅未重做完成,按原逻辑处理退款 if (one.equals(param.getCustomerFinalCheck())) { afterServiceRecord.setCustomerFinalCheck(1L); }else{ @@ -242,7 +275,7 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService log.warn("子单[{}]已发起分账,不能退款", afterServiceRecord.getOrderDetailId()); afterServiceRecord.setOriginalRefund(BigDecimal.ZERO); afterServiceRecordMapper.updateAfterServiceRecord(afterServiceRecord); - return AjaxResult.error("本单已划款,师傅/服务人员已到帐或即将到帐,双方达成退款的,请对方线下支付后点“对方已退款”"); + return AjaxResult.error("本单已划款,师傅/服务人员已到帐或即将到帐,双方达成退款的,请对方线下支付后点对方已退款"); } else { agreeRefund(afterServiceRecord); } @@ -401,4 +434,54 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService return afterServiceRecordMapper.deleteAfterServiceRecordById(id); } + /** + * 查询师傅反馈超时的售后记录 + * 倒计时1:客户发起后,师傅24小时不操作任何反馈 + * + * @return 超时的售后记录列表 + */ + @Override + public List selectWorkerFeedbackTimeoutRecords() { + return afterServiceRecordMapper.selectWorkerFeedbackTimeoutRecords(); + } + + /** + * 查询客户确认超时的售后记录 + * 倒计时2:师傅重做完成后客户36小时不操作 + * 倒计时3:师傅拒绝后客户36小时不操作 + * + * @return 超时的售后记录列表 + */ + @Override + public List selectCustomerConfirmTimeoutRecords() { + return afterServiceRecordMapper.selectCustomerConfirmTimeoutRecords(); + } + + /** + * 执行退款逻辑 + * 用于定时器自动处理退款 + * + * @param afterServiceRecord 售后记录 + */ + @Override + public void executeRefundLogic(AfterServiceRecord afterServiceRecord) { + try { + // 检查是否已分账,如果已分账则不能退款 + OrderDetail orderDetail = orderDetailService.selectById(afterServiceRecord.getOrderDetailId()); + if (orderDetail != null && orderDetail.getDrawCashTime() != null) { + log.warn("子单[{}]已发起分账,不能自动退款", afterServiceRecord.getOrderDetailId()); + afterServiceRecord.setOriginalRefund(BigDecimal.ZERO); + afterServiceRecordMapper.updateAfterServiceRecord(afterServiceRecord); + return; + } + + // 执行退款逻辑 + agreeRefund(afterServiceRecord); + log.info("定时器自动执行退款逻辑完成,售后记录ID:{}", afterServiceRecord.getId()); + } catch (Exception e) { + log.error("定时器自动执行退款逻辑异常,售后记录ID:{}", afterServiceRecord.getId(), e); + throw e; + } + } + } diff --git a/ghy-order/src/main/resources/mapper/order/AfterServiceRecordMapper.xml b/ghy-order/src/main/resources/mapper/order/AfterServiceRecordMapper.xml index afd0074b..707f4734 100644 --- a/ghy-order/src/main/resources/mapper/order/AfterServiceRecordMapper.xml +++ b/ghy-order/src/main/resources/mapper/order/AfterServiceRecordMapper.xml @@ -27,13 +27,14 @@ + select id, customer_reason_type, customer_reason, order_detail_id, oper_type, worker_feedback_result, worker_feedback_reason_type, worker_feedback_reason, refund, agreed_refund, original_refund, customer_final_check, create_by, create_time, update_by, update_time, remark, refund_apply_time, customer_agree_redo, - redo_complete_time, redo_complete_remark, redo_complete_images + redo_complete_time, redo_complete_remark, redo_complete_images, is_auto_processed from after_service_record @@ -83,6 +84,7 @@ redo_complete_time, redo_complete_remark, redo_complete_images, + is_auto_processed, create_by, update_by, remark, @@ -103,6 +105,7 @@ #{redoCompleteTime}, #{redoCompleteRemark}, #{redoCompleteImages}, + #{isAutoProcessed}, #{createBy}, #{updateBy}, #{remark}, @@ -129,6 +132,7 @@ redo_complete_time = #{redoCompleteTime}, redo_complete_remark = #{redoCompleteRemark}, redo_complete_images = #{redoCompleteImages}, + is_auto_processed = #{isAutoProcessed}, create_by = #{createBy}, create_time = #{createTime}, update_by = #{updateBy}, @@ -149,4 +153,35 @@ + + + + + + + + +