订单超时与扣款记录

This commit is contained in:
Hawking 2023-05-05 10:12:23 +08:00
parent cd21ae9d17
commit 2aaab088a6
9 changed files with 202 additions and 132 deletions

View File

@ -146,4 +146,14 @@ public class OrderDetail extends BaseEntity {
private boolean workFinishTimeExisted;
private List<Integer> orderStatusList;
/**
* 是否超时 1= 0=
*/
private Integer timeout;
/**
* 超时扣款次数
*/
private Integer timeoutFineTimes;
}

View File

@ -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

View File

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

View File

@ -382,7 +382,9 @@ public class OrderDetailServiceImpl implements OrderDetailService {
@Override
public List<OrderDetail> selectByStatus(List<Integer> 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);
}
}

View File

@ -30,6 +30,8 @@
<result property="updateBy" column="update_by"/>
<result property="updateTime" column="update_time"/>
<result property="remark" column="remark"/>
<result property="timeout" column="timeout_"/>
<result property="timeoutFineTimes" column="timeout_fine_times"/>
</resultMap>
<sql id="selectOrderDetail">
@ -56,7 +58,9 @@
create_by,
create_time,
update_time,
remark
remark,
timeout_,
timeout_fine_times
FROM order_detail
</sql>
@ -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>
<update id="updateTimeout">
UPDATE order_detail
SET timeout_ = #{timeout},
timeout_fine_times = #{timeoutFineTimes}
WHERE id = #{id}
</update>
<update id="updateByOrderMasterId">
UPDATE order_detail
<set>

View File

@ -20,6 +20,7 @@ public class OrderTimeoutRecord {
* 子订单ID
*/
private Long orderDetailId;
private Long workerId;
private List<Long> 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;
}
}

View File

@ -7,18 +7,20 @@
<resultMap id="OrderFineRecordResult" type="com.ghy.payment.domain.OrderTimeoutRecord">
<id property="id" column="id"/>
<result property="orderDetailId" column="order_detail_id"/>
<result property="workerId" column="worker_id"/>
<result property="fineStatus" column="fine_status"/>
<result property="orderStatus" column="order_status"/>
<result property="payMoney" column="pay_money"/>
<result property="createTime" column="create_time"/>
</resultMap>
<sql id="selectOrderFineRecord">
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
</sql>
<select id="selectByDetailIdAndStatus" resultMap="OrderFineRecordResult">
<include refid="selectOrderFineRecord"></include>
<include refid="selectOrderFineRecord"/>
WHERE order_detail_id = #{orderDetailId} AND order_status = #{orderStatus}
</select>
@ -29,6 +31,9 @@
<if test="orderDetailId != null">
AND order_detail_id = #{orderDetailId}
</if>
<if test="workerId != null">
AND worker_id = #{workerId}
</if>
<if test="orderDetailIds != null and orderDetailIds != ''">
AND order_detail_id IN
<foreach collection="orderDetailIds" item="orderDetailId" open="(" separator="," close=")">
@ -41,10 +46,9 @@
</where>
</select>
<insert id="insert" parameterType="com.ghy.payment.domain.OrderTimeoutRecord" useGeneratedKeys="true"
keyProperty="id">
INSERT INTO order_timeout_record (order_detail_id, fine_status, order_status)
VALUES (#{orderDetailId}, #{fineStatus}, #{orderStatus})
<insert id="insert" parameterType="com.ghy.payment.domain.OrderTimeoutRecord" useGeneratedKeys="true" keyProperty="id">
INSERT INTO order_timeout_record (order_detail_id, worker_id, fine_status, order_status, pay_money)
VALUES (#{orderDetailId}, #{workerId}, #{fineStatus}, #{orderStatus}, #{payMoney})
</insert>
<update id="update">

View File

@ -3,7 +3,7 @@ package com.ghy.quartz.service;
public interface OrderService {
// 更新超时环境订单
void overTimeOrder(String orderStatus) throws Exception;
void overTimeOrder(String orderStatus);
// 自动完成和分账
void autoFinishOrder();

View File

@ -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,28 +53,128 @@ 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<OrderDetail> 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;
}
}
/**
@ -82,13 +183,13 @@ public class OrderServiceImpl implements OrderService {
* @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<OrderTimeoutRecord> 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<OrderDetail> 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());
// }
// }
}
}