增加定时处理

This commit is contained in:
cb 2025-09-03 15:04:37 +08:00
parent 08e5353b4e
commit 9090b61095
6 changed files with 304 additions and 4 deletions

View File

@ -85,6 +85,10 @@ public class AfterServiceRecord extends BaseEntity
@Excel(name = "重做/补做完成图片") @Excel(name = "重做/补做完成图片")
private String redoCompleteImages; private String redoCompleteImages;
/** 是否自动处理0-否1-是(用于防止重复处理) */
@Excel(name = "是否自动处理0-否1-是")
private Integer isAutoProcessed;
private boolean excludeAfterServiceFinished; private boolean excludeAfterServiceFinished;
private List<AfterServiceImgs> imgsList; private List<AfterServiceImgs> imgsList;

View File

@ -65,4 +65,21 @@ public interface AfterServiceRecordMapper {
* @return 未完成的售后记录 * @return 未完成的售后记录
*/ */
AfterServiceRecord unfinished(Long orderDetailId); AfterServiceRecord unfinished(Long orderDetailId);
/**
* 查询师傅反馈超时的售后记录
* 倒计时1客户发起后师傅24小时不操作任何反馈
*
* @return 超时的售后记录列表
*/
List<AfterServiceRecord> selectWorkerFeedbackTimeoutRecords();
/**
* 查询客户确认超时的售后记录
* 倒计时2师傅重做完成后客户36小时不操作
* 倒计时3师傅拒绝后客户36小时不操作
*
* @return 超时的售后记录列表
*/
List<AfterServiceRecord> selectCustomerConfirmTimeoutRecords();
} }

View File

@ -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<AfterServiceRecord> 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<AfterServiceRecord> 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());
}
}

View File

@ -61,4 +61,29 @@ public interface IAfterServiceRecordService {
* @return 结果 * @return 结果
*/ */
int deleteAfterServiceRecordById(String id); int deleteAfterServiceRecordById(String id);
/**
* 查询师傅反馈超时的售后记录
* 倒计时1客户发起后师傅24小时不操作任何反馈
*
* @return 超时的售后记录列表
*/
List<AfterServiceRecord> selectWorkerFeedbackTimeoutRecords();
/**
* 查询客户确认超时的售后记录
* 倒计时2师傅重做完成后客户36小时不操作
* 倒计时3师傅拒绝后客户36小时不操作
*
* @return 超时的售后记录列表
*/
List<AfterServiceRecord> selectCustomerConfirmTimeoutRecords();
/**
* 执行退款逻辑
* 用于定时器自动处理退款
*
* @param afterServiceRecord 售后记录
*/
void executeRefundLogic(AfterServiceRecord afterServiceRecord);
} }

View File

@ -209,8 +209,41 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService
} }
if (param.getCustomerFinalCheck()!=null ) { 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<String, Object> 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())) { if (one.equals(param.getCustomerFinalCheck())) {
afterServiceRecord.setCustomerFinalCheck(1L); afterServiceRecord.setCustomerFinalCheck(1L);
}else{ }else{
@ -242,7 +275,7 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService
log.warn("子单[{}]已发起分账,不能退款", afterServiceRecord.getOrderDetailId()); log.warn("子单[{}]已发起分账,不能退款", afterServiceRecord.getOrderDetailId());
afterServiceRecord.setOriginalRefund(BigDecimal.ZERO); afterServiceRecord.setOriginalRefund(BigDecimal.ZERO);
afterServiceRecordMapper.updateAfterServiceRecord(afterServiceRecord); afterServiceRecordMapper.updateAfterServiceRecord(afterServiceRecord);
return AjaxResult.error("本单已划款,师傅/服务人员已到帐或即将到帐,双方达成退款的,请对方线下支付后点对方已退款"); return AjaxResult.error("本单已划款,师傅/服务人员已到帐或即将到帐,双方达成退款的,请对方线下支付后点对方已退款");
} else { } else {
agreeRefund(afterServiceRecord); agreeRefund(afterServiceRecord);
} }
@ -401,4 +434,54 @@ public class AfterServiceRecordServiceImpl implements IAfterServiceRecordService
return afterServiceRecordMapper.deleteAfterServiceRecordById(id); return afterServiceRecordMapper.deleteAfterServiceRecordById(id);
} }
/**
* 查询师傅反馈超时的售后记录
* 倒计时1客户发起后师傅24小时不操作任何反馈
*
* @return 超时的售后记录列表
*/
@Override
public List<AfterServiceRecord> selectWorkerFeedbackTimeoutRecords() {
return afterServiceRecordMapper.selectWorkerFeedbackTimeoutRecords();
}
/**
* 查询客户确认超时的售后记录
* 倒计时2师傅重做完成后客户36小时不操作
* 倒计时3师傅拒绝后客户36小时不操作
*
* @return 超时的售后记录列表
*/
@Override
public List<AfterServiceRecord> 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;
}
}
} }

View File

@ -27,13 +27,14 @@
<result property="redoCompleteTime" column="redo_complete_time" /> <result property="redoCompleteTime" column="redo_complete_time" />
<result property="redoCompleteRemark" column="redo_complete_remark" /> <result property="redoCompleteRemark" column="redo_complete_remark" />
<result property="redoCompleteImages" column="redo_complete_images" /> <result property="redoCompleteImages" column="redo_complete_images" />
<result property="isAutoProcessed" column="is_auto_processed" />
</resultMap> </resultMap>
<sql id="selectAfterServiceRecordVo"> <sql id="selectAfterServiceRecordVo">
select id, customer_reason_type, customer_reason, order_detail_id, oper_type, worker_feedback_result, 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, 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, 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 from after_service_record
</sql> </sql>
@ -83,6 +84,7 @@
<if test="redoCompleteTime != null">redo_complete_time,</if> <if test="redoCompleteTime != null">redo_complete_time,</if>
<if test="redoCompleteRemark != null">redo_complete_remark,</if> <if test="redoCompleteRemark != null">redo_complete_remark,</if>
<if test="redoCompleteImages != null">redo_complete_images,</if> <if test="redoCompleteImages != null">redo_complete_images,</if>
<if test="isAutoProcessed != null">is_auto_processed,</if>
<if test="createBy != null">create_by,</if> <if test="createBy != null">create_by,</if>
<if test="updateBy != null">update_by,</if> <if test="updateBy != null">update_by,</if>
<if test="remark != null">remark,</if> <if test="remark != null">remark,</if>
@ -103,6 +105,7 @@
<if test="redoCompleteTime != null">#{redoCompleteTime},</if> <if test="redoCompleteTime != null">#{redoCompleteTime},</if>
<if test="redoCompleteRemark != null">#{redoCompleteRemark},</if> <if test="redoCompleteRemark != null">#{redoCompleteRemark},</if>
<if test="redoCompleteImages != null">#{redoCompleteImages},</if> <if test="redoCompleteImages != null">#{redoCompleteImages},</if>
<if test="isAutoProcessed != null">#{isAutoProcessed},</if>
<if test="createBy != null">#{createBy},</if> <if test="createBy != null">#{createBy},</if>
<if test="updateBy != null">#{updateBy},</if> <if test="updateBy != null">#{updateBy},</if>
<if test="remark != null">#{remark},</if> <if test="remark != null">#{remark},</if>
@ -129,6 +132,7 @@
<if test="redoCompleteTime != null">redo_complete_time = #{redoCompleteTime},</if> <if test="redoCompleteTime != null">redo_complete_time = #{redoCompleteTime},</if>
<if test="redoCompleteRemark != null">redo_complete_remark = #{redoCompleteRemark},</if> <if test="redoCompleteRemark != null">redo_complete_remark = #{redoCompleteRemark},</if>
<if test="redoCompleteImages != null">redo_complete_images = #{redoCompleteImages},</if> <if test="redoCompleteImages != null">redo_complete_images = #{redoCompleteImages},</if>
<if test="isAutoProcessed != null">is_auto_processed = #{isAutoProcessed},</if>
<if test="createBy != null">create_by = #{createBy},</if> <if test="createBy != null">create_by = #{createBy},</if>
<if test="createTime != null">create_time = #{createTime},</if> <if test="createTime != null">create_time = #{createTime},</if>
<if test="updateBy != null">update_by = #{updateBy},</if> <if test="updateBy != null">update_by = #{updateBy},</if>
@ -149,4 +153,35 @@
</foreach> </foreach>
</delete> </delete>
<!-- 查询师傅反馈超时的售后记录 -->
<!-- 倒计时1客户发起后师傅24小时不操作任何反馈 -->
<select id="selectWorkerFeedbackTimeoutRecords" resultMap="AfterServiceRecordResult">
<include refid="selectAfterServiceRecordVo"/>
WHERE worker_feedback_result IS NULL
AND customer_final_check IS NULL
AND (is_auto_processed IS NULL OR is_auto_processed = 0)
AND create_time &lt;= DATE_SUB(NOW(), INTERVAL 24 HOUR)
AND create_time &gt; DATE_SUB(NOW(), INTERVAL 24*60+5 MINUTE)
</select>
<!-- 查询客户确认超时的售后记录 -->
<!-- 倒计时2师傅重做完成后客户36小时不操作 -->
<!-- 倒计时3师傅拒绝后客户36小时不操作 -->
<select id="selectCustomerConfirmTimeoutRecords" resultMap="AfterServiceRecordResult">
<include refid="selectAfterServiceRecordVo"/>
WHERE customer_final_check IS NULL
AND (is_auto_processed IS NULL OR is_auto_processed = 0)
AND (
-- 倒计时2师傅重做完成后客户36小时不操作
(worker_feedback_result = 3
AND redo_complete_time &lt;= DATE_SUB(NOW(), INTERVAL 36 HOUR)
AND redo_complete_time &gt; DATE_SUB(NOW(), INTERVAL 36*60+5 MINUTE))
OR
-- 倒计时3师傅拒绝或同意后客户36小时不操作
(worker_feedback_result IN (0, 1)
AND update_time &lt;= DATE_SUB(NOW(), INTERVAL 36 HOUR)
AND update_time &gt; DATE_SUB(NOW(), INTERVAL 36*60+5 MINUTE))
)
</select>
</mapper> </mapper>