diff --git a/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java b/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java index a5047859..f07bea21 100644 --- a/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java +++ b/ghy-order/src/main/java/com/ghy/order/domain/OrderDetail.java @@ -146,4 +146,14 @@ public class OrderDetail extends BaseEntity { private boolean workFinishTimeExisted; private List orderStatusList; + + /** + * 是否超时 1=是 0=否 + */ + private Integer timeout; + + /** + * 超时扣款次数 + */ + private Integer timeoutFineTimes; } diff --git a/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java b/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java index cc352836..86f88ff0 100644 --- a/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java +++ b/ghy-order/src/main/java/com/ghy/order/mapper/OrderDetailMapper.java @@ -76,6 +76,16 @@ public interface OrderDetailMapper { */ int updateStatus(@Param("id") Long orderDetailId, @Param("orderStatus") int orderStatus); + /** + * 更新子订单超时状态 + * + * @param id 子订单ID + * @param timeout 是否超时 + * @param timeoutFineTimes 超时罚款次数 + * @return 1 + */ + int updateTimeout(@Param("id") Long id, @Param("timeout") int timeout, @Param("timeoutFineTimes") int timeoutFineTimes); + /** * 通过主订单ID批量更新子订单 * 可更新字段: orderStatus diff --git a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java index b37406b2..33d88550 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java +++ b/ghy-order/src/main/java/com/ghy/order/service/OrderDetailService.java @@ -163,4 +163,14 @@ public interface OrderDetailService { * @return */ int updateStatus(Long id, int status); + + /** + * 更新子订单超时状态 + * + * @param id 子订单ID + * @param timeout 是否超时 + * @param timeoutFineTimes 超时罚款次数 + * @return 1 + */ + int updateTimeout(Long id, int timeout, int timeoutFineTimes); } diff --git a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java index b862779e..6115a3ac 100644 --- a/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java +++ b/ghy-order/src/main/java/com/ghy/order/service/impl/OrderDetailServiceImpl.java @@ -382,7 +382,9 @@ public class OrderDetailServiceImpl implements OrderDetailService { @Override public List selectByStatus(List status) { - Assert.isTrue(!CollectionUtils.isEmpty(status), "订单状态为空"); + if (CollectionUtils.isEmpty(status)) { + return Collections.emptyList(); + } return orderDetailMapper.selectByStatus(status); } @@ -689,4 +691,9 @@ public class OrderDetailServiceImpl implements OrderDetailService { } throw new IllegalArgumentException("Unknown OrderStatus: " + status); } + + @Override + public int updateTimeout(Long id, int timeout, int timeoutFineTimes) { + return orderDetailMapper.updateTimeout(id, timeout, timeoutFineTimes); + } } diff --git a/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml b/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml index 9bd3de70..7fcbe498 100644 --- a/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml +++ b/ghy-order/src/main/resources/mapper/order/OrderDetailMapper.xml @@ -30,6 +30,8 @@ + + @@ -56,7 +58,9 @@ create_by, create_time, update_time, - remark + remark, + timeout_, + timeout_fine_times FROM order_detail @@ -85,7 +89,9 @@ od.create_time, od.update_time, od.remark, - od.draw_cash_status + od.draw_cash_status, + od.timeout_, + od.timeout_fine_times FROM order_detail od LEFT JOIN order_master om ON om.id = od.order_master_id LEFT JOIN customer_address ca ON ca.customer_address_id = om.address_id @@ -289,6 +295,13 @@ WHERE id = #{id} + + UPDATE order_detail + SET timeout_ = #{timeout}, + timeout_fine_times = #{timeoutFineTimes} + WHERE id = #{id} + + UPDATE order_detail diff --git a/ghy-payment/src/main/java/com/ghy/payment/domain/OrderTimeoutRecord.java b/ghy-payment/src/main/java/com/ghy/payment/domain/OrderTimeoutRecord.java index 57dacdfd..6d7cef46 100644 --- a/ghy-payment/src/main/java/com/ghy/payment/domain/OrderTimeoutRecord.java +++ b/ghy-payment/src/main/java/com/ghy/payment/domain/OrderTimeoutRecord.java @@ -20,6 +20,7 @@ public class OrderTimeoutRecord { * 子订单ID */ private Long orderDetailId; + private Long workerId; private List orderDetailIds; @@ -30,6 +31,7 @@ public class OrderTimeoutRecord { /** * 扣款状态 0未扣款 1已扣款 + * 这个字段用来判断超时罚金是否已经被冲账 */ private Integer fineStatus; @@ -38,13 +40,17 @@ public class OrderTimeoutRecord { */ private Date createTime; + /** + * 扣款金额 + */ private BigDecimal payMoney; public OrderTimeoutRecord() { } - public OrderTimeoutRecord(Long orderDetailId, Integer orderStatus) { + public OrderTimeoutRecord(Long orderDetailId, Long workerId, Integer orderStatus) { this.orderDetailId = orderDetailId; + this.workerId = workerId; this.orderStatus = orderStatus; } } diff --git a/ghy-payment/src/main/resources/mapper/financial/OrderFineRecordMapper.xml b/ghy-payment/src/main/resources/mapper/financial/OrderFineRecordMapper.xml index 9b87894e..cf2940c2 100644 --- a/ghy-payment/src/main/resources/mapper/financial/OrderFineRecordMapper.xml +++ b/ghy-payment/src/main/resources/mapper/financial/OrderFineRecordMapper.xml @@ -7,18 +7,20 @@ - + + + - SELECT id, order_detail_id, fine_status, order_status, create_time + SELECT id, order_detail_id, worker_id, fine_status, order_status, pay_money, create_time FROM order_timeout_record @@ -29,6 +31,9 @@ AND order_detail_id = #{orderDetailId} + + AND worker_id = #{workerId} + AND order_detail_id IN @@ -41,10 +46,9 @@ - - INSERT INTO order_timeout_record (order_detail_id, fine_status, order_status) - VALUES (#{orderDetailId}, #{fineStatus}, #{orderStatus}) + + INSERT INTO order_timeout_record (order_detail_id, worker_id, fine_status, order_status, pay_money) + VALUES (#{orderDetailId}, #{workerId}, #{fineStatus}, #{orderStatus}, #{payMoney}) diff --git a/ghy-quartz/src/main/java/com/ghy/quartz/service/OrderService.java b/ghy-quartz/src/main/java/com/ghy/quartz/service/OrderService.java index e1401f81..50cbed6e 100644 --- a/ghy-quartz/src/main/java/com/ghy/quartz/service/OrderService.java +++ b/ghy-quartz/src/main/java/com/ghy/quartz/service/OrderService.java @@ -3,7 +3,7 @@ package com.ghy.quartz.service; public interface OrderService { // 更新超时环境订单 - void overTimeOrder(String orderStatus) throws Exception; + void overTimeOrder(String orderStatus); // 自动完成和分账 void autoFinishOrder(); diff --git a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java index 3ddba3db..24877beb 100644 --- a/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java +++ b/ghy-quartz/src/main/java/com/ghy/quartz/service/impl/OrderServiceImpl.java @@ -1,6 +1,5 @@ package com.ghy.quartz.service.impl; -import com.ghy.common.enums.FinancialDetailType; import com.ghy.common.enums.OrderStatus; import com.ghy.order.domain.OrderDetail; import com.ghy.order.domain.OrderMaster; @@ -13,8 +12,8 @@ import com.ghy.payment.service.FinancialDetailService; import com.ghy.quartz.service.OrderService; import com.ghy.system.domain.SysDeptConfig; import com.ghy.system.service.ISysDeptConfigService; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.time.DateUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Service; @@ -24,13 +23,15 @@ import org.springframework.util.CollectionUtils; import javax.annotation.Resource; import java.math.BigDecimal; import java.text.SimpleDateFormat; +import java.time.LocalTime; import java.util.*; import java.util.stream.Collectors; +@Slf4j @Service public class OrderServiceImpl implements OrderService { - private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class); + private static final Integer ONE = 1; /** * 需要超时扣款的订单状态 @@ -52,43 +53,143 @@ public class OrderServiceImpl implements OrderService { private ISysDeptConfigService sysDeptConfigService; @Override - public void overTimeOrder(String orderStatus) throws Exception{ + public void overTimeOrder(String orderStatus) { + LocalTime nowT = LocalTime.now(); + // 晚上19点后 - 早上8点前不需要执行定时器 + if (nowT.getHour() < 8 || nowT.getHour() > 18) { + return; + } // 查询符合超时的单 List orders = orderDetailService.selectByStatus(timeoutOrderStatus); + log.info("扫描到{}条未完成的订单", orders.size()); for (OrderDetail order : orders) { + executor.execute(() -> checkTimeout(order)); + } + } + + /** + * 判断是否超时 + * + * @param order 订单信息 + */ + @Transactional(rollbackFor = Exception.class) + void checkTimeout(OrderDetail order) { + Date now = new Date(); + // 是否超时 + boolean timeout = ONE.equals(order.getTimeout()); + // 超时扣款次数 + Integer times = order.getTimeoutFineTimes(); + if (!timeout) { + // 未超时的单 if (order.getOrderStatus().equals(OrderStatus.SERVER.code())) { - // 服务中状态要按预计上门时间计算超时 4h超时并且扣款 - long overTimeSec = this.getOverTimeSec(order.getExpectTimeStart(), 4 * 60 * 60 * 1000); - if (overTimeSec < System.currentTimeMillis()) { - executor.execute(() -> orderTimeout(order, true)); + // 服务中状态要按预计上门时间计算4h超时 + Date overTime = getOverTime(order.getExpectTimeStart(), 4 * 60 * 60 * 1000); + if (overTime.before(now)) { + log.info("订单[{}]服务中状态超时4小时 扣款", order.getId()); + OrderTimeoutRecord record = new OrderTimeoutRecord(order.getId(), order.getWorkerId(), order.getOrderStatus()); + record.setPayMoney(getFineMoney(order)); + record.setFineStatus(0); + orderFineRecordMapper.insert(record); + orderDetailService.updateTimeout(order.getId(), 1, 1); } } else if (timeoutOrderStatus.contains(order.getOrderStatus())) { - // 其它状态用update_time判断超时 30min超时 4h扣款 - long halfOverTimeSec = this.getOverTimeSec(order.getUpdateTime(), 30 * 60 * 1000); - long fourOverTimeSec = this.getOverTimeSec(order.getUpdateTime(), 4 * 60 * 60 * 1000); - if (halfOverTimeSec < System.currentTimeMillis()) { - executor.execute(() -> orderTimeout(order, true)); - } else if (fourOverTimeSec < System.currentTimeMillis()) { - executor.execute(() -> orderTimeout(order, false)); + // 其它状态用update_time判断30min超时 + Date overTime = getOverTime(order.getUpdateTime(), 30 * 60 * 1000); + if (overTime.before(now)) { + log.info("订单[{}]超时30分钟", order.getId()); + orderDetailService.updateTimeout(order.getId(), 1, 0); } } + return; } + if (times > 1) { + // 已经扣过2次款了 不再扣款 + log.info("订单[{}]已经扣过2次超时款 不再扣款", order.getId()); + } else if (times == 0) { + // 没扣过款的 + if (order.getOrderStatus().equals(OrderStatus.SERVER.code())) { + // 服务中的订单第一次扣款在上面已经扣过了 + return; + } + Date overTime = getOverTime(order.getUpdateTime(), 4 * 60 * 60 * 1000); + if (overTime.before(now)) { + log.info("订单[{}]超时4h 扣款", order.getId()); + OrderTimeoutRecord record = new OrderTimeoutRecord(order.getId(), order.getWorkerId(), order.getOrderStatus()); + record.setPayMoney(getFineMoney(order)); + record.setFineStatus(0); + orderFineRecordMapper.insert(record); + orderDetailService.updateTimeout(order.getId(), 1, 1); + } + } else if (times == 1) { + // 扣过一次款的 + // 服务中状态取预计上门时间 其它状态取update_time + Date orderTime = order.getOrderStatus().equals(OrderStatus.SERVER.code()) ? order.getExpectTimeStart() : order.getUpdateTime(); + Date overTime = getOverTime(orderTime, 6 * 60 * 60 * 1000); + if (overTime.before(now)) { + log.info("订单[{}]超时6h 再次扣款", order.getId()); + OrderTimeoutRecord record = new OrderTimeoutRecord(order.getId(), order.getWorkerId(), order.getOrderStatus()); + record.setPayMoney(getFineMoney(order)); + record.setFineStatus(0); + orderFineRecordMapper.insert(record); + orderDetailService.updateTimeout(order.getId(), 1, 2); + } + } + } + + /** + * 计算订单罚金(固定罚金+比例罚金) + */ + private BigDecimal getFineMoney(OrderDetail order) { + FinancialDetail orderFinancial = financialDetailService.selectByOrderDetailId(order.getId()); + SysDeptConfig deptConfig = sysDeptConfigService.selectByDeptId(order.getDeptId()); + BigDecimal fineMoney = deptConfig.getGoingOutTime(); + // 如果扣款额为null或<0 + if (fineMoney == null || BigDecimal.ZERO.compareTo(fineMoney) > -1) { + fineMoney = BigDecimal.ZERO; + } + BigDecimal timeoutRate = deptConfig.getGoingOutTimeRate(); + // 如果扣款比例 为null 或 <0 或 >1.00 + if (timeoutRate == null || BigDecimal.ZERO.compareTo(timeoutRate) > 0 || BigDecimal.ONE.compareTo(timeoutRate) < 0) { + return fineMoney; + } + BigDecimal rateMoney = orderFinancial.getPayMoney().multiply(timeoutRate); + return fineMoney.add(rateMoney); + } + + /** + * 计算超时时间 + * + * @param orderTime 开始时间 + * @param time 超时时间 + * @return 超时时间 + */ + private Date getOverTime(Date orderTime, int time) { + Date date = DateUtils.addMilliseconds(orderTime, time); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(date); + int hour = calendar.get(Calendar.HOUR_OF_DAY); + if (hour < 8 || hour > 18) { + // 超过19点的单,需要加上13个小时 + return DateUtils.addHours(date, 13); + } else { + return date; + } } /** * @param orderTime 单据时间/可能为上门开始时间 - * @param time 超时时间,0.5h/4h/8h等 + * @param time 超时时间,0.5h/4h/8h等 * @return 最低限制时间 * @throws Exception */ - private long getOverTimeSec(Date orderTime, long time) throws Exception{ + private long getOverTimeSec(Date orderTime, long time) throws Exception { long overTime = orderTime.getTime(); // 隔夜需要加上13个小时 long isNight = 13 * 60 * 60 * 1000; //设置日期格式 SimpleDateFormat df = new SimpleDateFormat("HH:mm"); - Date nowTime =df.parse(df.format(new Date())); + Date nowTime = df.parse(df.format(new Date())); // 早上8点-晚上19点 Date amBeginTime = df.parse("08:00"); Date pmEndTime = df.parse("19:00"); @@ -102,103 +203,30 @@ public class OrderServiceImpl implements OrderService { Calendar endTime = Calendar.getInstance(); endTime.setTime(pmEndTime); // 符合时间区间 - if(date.after(beginTime) && date.before(endTime)){ + if (date.after(beginTime) && date.before(endTime)) { // 当日单 - if(checkIsTodayOrder(orderTime)){ + if (checkIsTodayOrder(orderTime)) { overTime = overTime + time; - }else { + } else { overTime = overTime + isNight + time; } } return overTime; } - private Boolean checkIsTodayOrder(Date orderTime) throws Exception{ + private Boolean checkIsTodayOrder(Date orderTime) throws Exception { // 订单日期 SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); Date orderDate = df.parse(df.format(orderTime)); // 当前日期 Date now = df.parse(df.format(new Date())); - if(orderDate.before(now)){ + if (orderDate.before(now)) { return false; - }else { + } else { return true; } } - /** - * 处理订单超时与扣款 - * - * @param order 子订单信息 - * @param fine 是否需要扣款 - */ - @Transactional(rollbackFor = Exception.class) - public void orderTimeout(OrderDetail order, boolean fine) { - OrderTimeoutRecord record = new OrderTimeoutRecord(order.getId(), order.getOrderStatus()); - List records = orderFineRecordMapper.selectList(record); - if (CollectionUtils.isEmpty(records)) { - // 如果没有扣款记录 则保存一条扣款记录 - record.setFineStatus(0); - orderFineRecordMapper.insert(record); - } else { - record = records.get(0); - } - - if (!fine) { - // 不需要扣款 - return; - } - if (record.getFineStatus() == 1) { - // 如果已经扣款 就不需要往下走了 - return; - } - - // 把超时记录的扣款状态改为1 - record.setFineStatus(1); - orderFineRecordMapper.update(record); - - FinancialDetail orderFinancial = financialDetailService.selectByOrderDetailId(order.getId()); - SysDeptConfig deptConfig = sysDeptConfigService.selectByDeptId(order.getDeptId()); - BigDecimal timeoutMoney = deptConfig.getGoingOutTime(); - // 如果扣款额为null或<0 - if (timeoutMoney == null || BigDecimal.ZERO.compareTo(timeoutMoney) > 0) { - timeoutMoney = BigDecimal.ZERO; - } - BigDecimal timeoutRate = deptConfig.getGoingOutTimeRate(); - // 如果扣款比例 为null 或 <0 或 >1.00 - if (timeoutRate == null || BigDecimal.ZERO.compareTo(timeoutRate) > 0 || BigDecimal.ONE.compareTo(timeoutRate) < 0) { - timeoutRate = BigDecimal.ZERO; - } - timeoutMoney = orderFinancial.getPayMoney().multiply(timeoutRate).add(timeoutMoney); - if (BigDecimal.ZERO.compareTo(timeoutMoney) == 0) { - // 如果罚金=0时 无需扣款 - logger.info("订单[ID={}]超时无需扣款 GoingOutTime={}, GoingOutTimeRate={}", order.getId(), - deptConfig.getGoingOutTime(), deptConfig.getGoingOutTimeRate()); - return; - } - if (timeoutMoney.compareTo(orderFinancial.getPayMoney()) > 0) { - // 如果罚金大于本单金额的情况 - logger.warn("订单[ID={}]超时罚金[{}]大于订单金额[{}]!!! GoingOutTime={}, GoingOutTimeRate={}", order.getId(), - timeoutMoney, orderFinancial.getPayMoney(), deptConfig.getGoingOutTime(), deptConfig.getGoingOutTimeRate()); - timeoutMoney = orderFinancial.getPayMoney(); - } - - //下面是扣款逻辑 - // 从子订单对应的财务单里扣除2元 - orderFinancial.setPayMoney(orderFinancial.getPayMoney().subtract(timeoutMoney)); - financialDetailService.updateFinancialDetail(orderFinancial); - - // 生成对应的扣款明细 - FinancialDetail fineFinancial = new FinancialDetail(); - fineFinancial.setDeptId(orderFinancial.getDeptId()); - fineFinancial.setCode(financialDetailService.createCode()); - fineFinancial.setFinancialMasterId(orderFinancial.getFinancialMasterId()); - fineFinancial.setFinancialMasterCode(orderFinancial.getFinancialMasterCode()); - fineFinancial.setPayMoney(timeoutMoney); - fineFinancial.setFinancialDetailType(FinancialDetailType.FINE_FEE.getCode()); - financialDetailService.insertFinancialDetail(fineFinancial); - } - @Override public void autoFinishOrder() { @@ -211,12 +239,12 @@ public class OrderServiceImpl implements OrderService { for (OrderDetail orderDetail : orderDetails) { // 筛选符合自动确认的订单 if (day14ago > orderDetail.getWorkFinishTime().getTime()) { - logger.info("子订单自动完成[id={}, code={}]", orderDetail.getId(), orderDetail.getCode()); + log.info("子订单自动完成[id={}, code={}]", orderDetail.getId(), orderDetail.getCode()); try { // 子订单完单流程(分账与提现) orderDetailService.finish(orderDetail.getId()); } catch (Exception e) { - logger.error("子订单自动完成[id={}, code={}]出错", orderDetail.getId(), orderDetail.getCode(), e); + log.error("子订单自动完成[id={}, code={}]出错", orderDetail.getId(), orderDetail.getCode(), e); } } } @@ -245,35 +273,17 @@ public class OrderServiceImpl implements OrderService { } // 如果已经不存在"已完成"和"已取消"以外的子订单 就把主订单也改为完成 if (allFinish) { - logger.info("主订单自动完成[id={} code={}]", om.getId(), om.getCode()); + log.info("主订单自动完成[id={} code={}]", om.getId(), om.getCode()); orderMasterService.finish(om.getId()); } } catch (Exception e) { - logger.error(e.getMessage(), e); + log.error(e.getMessage(), e); } } } @Override public void autoChangeDrawCashStatus() { -// LocalDate now = LocalDate.now(); -// DayOfWeek dayOfWeek = now.getDayOfWeek(); -// if (dayOfWeek.getValue() > 5) { -// return; -// } -// OrderDetail orderDetail = new OrderDetail(); -// orderDetail.setDrawCashStatus(1); -// List orderDetails = orderDetailService.selectOrderDetailList(orderDetail); -// long before24h = System.currentTimeMillis() - (24 * 60 * 60 * 1000); -// for (OrderDetail od : orderDetails) { -// if (od.getDrawCashDate().getTime() < before24h) { -// OrderDetail update = new OrderDetail(); -// update.setId(od.getId()); -// update.setOrderStatus(2); -// orderDetailService.updateOrderDetail(update); -// logger.info("订单[{}]自动提现成功", od.getCode()); -// } -// } } }