临时提交,还未测试押金抵扣

This commit is contained in:
磷叶 2025-05-19 18:09:38 +08:00
parent 82c547d778
commit 794781248b
18 changed files with 359 additions and 66 deletions

View File

@ -205,4 +205,11 @@ public class DeviceUtil {
device.setOnlineStatus(DeviceOnlineStatus.ONLINE.getStatus()); device.setOnlineStatus(DeviceOnlineStatus.ONLINE.getStatus());
device.setLastOnlineTime(at); device.setLastOnlineTime(at);
} }
/**
* 车辆是否离线
*/
public static boolean isOffline(DeviceVO device) {
return device != null && device.getOnlineStatus() != null && DeviceOnlineStatus.OFFLINE.getStatus().equals(device.getOnlineStatus());
}
} }

View File

@ -206,4 +206,24 @@ public class Order extends BaseEntity {
@ApiModelProperty("实收车损费") @ApiModelProperty("实收车损费")
private BigDecimal actualDeductionFee; private BigDecimal actualDeductionFee;
@Excel(name = "套餐是否自动押金抵扣")
@ApiModelProperty("套餐是否自动押金抵扣")
private Boolean suitDepositDeduction;
@Excel(name = "订单版本号")
@ApiModelProperty("订单版本号")
private Integer version;
@Excel(name = "支付方式")
@ApiModelProperty("支付方式1押金抵扣 2用户支付")
private String payType;
@Excel(name = "押金抵扣金额")
@ApiModelProperty("押金抵扣金额")
private BigDecimal depositDeductionAmount;
@Excel(name = "骑行支付ID")
@ApiModelProperty("骑行支付ID")
private Long ridePayId;
} }

View File

@ -58,4 +58,7 @@ public class OrderQuery extends OrderVO {
@ApiModelProperty("结束日期范围") @ApiModelProperty("结束日期范围")
@DateTimeFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd")
private List<LocalDate> endDateRange; private List<LocalDate> endDateRange;
@ApiModelProperty("是否为空支付类型")
private Boolean isNullPayType;
} }

View File

@ -75,6 +75,10 @@ public class OrderVO extends Order implements IotDevice {
@ApiModelProperty("实收金额") @ApiModelProperty("实收金额")
private BigDecimal actualAmount; private BigDecimal actualAmount;
// 剩余可抵扣押金
@ApiModelProperty("剩余可抵扣押金")
private BigDecimal depositDeductRemain;
@Override @Override
public String mac() { public String mac() {
return this.deviceMac; return this.deviceMac;

View File

@ -0,0 +1,24 @@
package com.ruoyi.bst.order.domain.bo;
import java.math.BigDecimal;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.enums.OrderPayType;
import lombok.Data;
@Data
public class OrderRidePaySuccessBO {
// 订单数据
private OrderVO order;
// 支付方式
private OrderPayType payType;
// 支付/抵扣金额
private BigDecimal payAmount;
// 是否需要审核
private Boolean needVerify;
}

View File

@ -0,0 +1,32 @@
package com.ruoyi.bst.order.domain.dto;
import java.math.BigDecimal;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* 押金抵扣DTO
*/
@Data
public class OrderDeductDTO {
@ApiModelProperty("订单ID")
@NotNull(message = "订单ID不能为空")
private Long id;
@ApiModelProperty("抵扣金额")
@NotNull(message = "抵扣金额不能为空")
@Min(value = 0, message = "抵扣金额不能小于0")
private BigDecimal amount;
@ApiModelProperty("是否需要审核")
private Boolean needVerify;
@ApiModelProperty("当可抵扣金额不足时,是否减少抵扣金额")
private Boolean reduceAmountWhenNotEnough;
}

View File

@ -0,0 +1,16 @@
package com.ruoyi.bst.order.domain.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum OrderPayType {
DEPOSIT_DEDUCTION("1", "押金抵扣"),
USER_PAY("2", "用户支付");
private String code;
private String name;
}

View File

@ -11,11 +11,12 @@ import lombok.Getter;
@AllArgsConstructor @AllArgsConstructor
public enum OrderStatus { public enum OrderStatus {
WAIT_PAY("WAIT_PAY", "待支付"), WAIT_PAY("WAIT_PAY", "押金待支付"),
PROCESSING("PROCESSING", "进行中"), PROCESSING("PROCESSING", "进行中"),
FINISHED("FINISHED", "已结束"), FINISHED("FINISHED", "已结束"),
CANCELED("CANCELED", "已取消"), CANCELED("CANCELED", "已取消"),
WAIT_VERIFY("WAIT_VERIFY", "待审核"), WAIT_VERIFY("WAIT_VERIFY", "待审核"),
RIDE_WAIT_PAY("RIDE_WAIT_PAY", "骑行费待支付"),
REJECTED("REJECTED", "已驳回"), REJECTED("REJECTED", "已驳回"),
REFUNDED("REFUNDED", "已退款"); REFUNDED("REFUNDED", "已退款");
@ -72,4 +73,14 @@ public enum OrderStatus {
public static List<String> finishedList() { public static List<String> finishedList() {
return CollectionUtils.map(OrderStatus::getCode, FINISHED, WAIT_VERIFY, REJECTED, REFUNDED); return CollectionUtils.map(OrderStatus::getCode, FINISHED, WAIT_VERIFY, REJECTED, REFUNDED);
} }
// 允许押金抵扣的订单状态
public static List<String> canDeduct() {
return CollectionUtils.map(OrderStatus::getCode, RIDE_WAIT_PAY);
}
// 允许骑行支付成功的订单状态
public static List<String> canRidePaySuccess() {
return CollectionUtils.map(OrderStatus::getCode, RIDE_WAIT_PAY);
}
} }

View File

@ -219,4 +219,11 @@ public interface OrderMapper {
*/ */
BigDecimal selectSumOfActualTotalAmount(@Param("query") OrderQuery query); BigDecimal selectSumOfActualTotalAmount(@Param("query") OrderQuery query);
/**
* 押金抵扣
* @param id
* @param amount
*/
int deductDeposit(@Param("id") Long id, @Param("amount") BigDecimal amount);
} }

View File

@ -60,7 +60,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bo.actual_dispatch_fee, bo.actual_dispatch_fee,
bo.actual_manage_fee, bo.actual_manage_fee,
bo.actual_deduction_fee, bo.actual_deduction_fee,
bo.suit_deposit_deduction,
bo.version,
bo.pay_type,
bo.deposit_deduction_amount,
bo.ride_pay_id,
<include refid="actualAmount"/> as actual_amount, <include refid="actualAmount"/> as actual_amount,
<include refid="depositCanDeductRemain"/> as deposit_deduct_remain,
ba.name as area_name, ba.name as area_name,
su.nick_name as user_name, su.nick_name as user_name,
su.user_name as user_phone, su.user_name as user_phone,
@ -140,6 +146,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.payChannelId != null "> and bp.channel_id = #{query.payChannelId}</if> <if test="query.payChannelId != null "> and bp.channel_id = #{query.payChannelId}</if>
<if test="query.payExpireTimeEnd != null "> and bo.pay_expire_time &lt;= #{query.payExpireTimeEnd}</if> <if test="query.payExpireTimeEnd != null "> and bo.pay_expire_time &lt;= #{query.payExpireTimeEnd}</if>
<if test="query.payExpireTimeStart != null "> and bo.pay_expire_time &gt;= #{query.payExpireTimeStart}</if> <if test="query.payExpireTimeStart != null "> and bo.pay_expire_time &gt;= #{query.payExpireTimeStart}</if>
<if test="query.suitDepositDeduction != null "> and bo.suit_deposit_deduction = #{query.suitDepositDeduction}</if>
<if test="query.version != null "> and bo.version = #{query.version}</if>
<if test="query.payType != null and query.payType != ''"> and bo.pay_type = #{query.payType}</if>
<if test="query.isNullPayType != null"> and bo.pay_type is <if test="!query.isNullPayType">not</if> null</if>
<if test="query.bonusUserId != null "> <if test="query.bonusUserId != null ">
and bo.id in ( and bo.id in (
select distinct bb.bst_id from bst_bonus bb select distinct bb.bst_id from bst_bonus bb
@ -249,6 +259,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="actualDispatchFee != null">actual_dispatch_fee,</if> <if test="actualDispatchFee != null">actual_dispatch_fee,</if>
<if test="actualManageFee != null">actual_manage_fee,</if> <if test="actualManageFee != null">actual_manage_fee,</if>
<if test="actualDeductionFee != null">actual_deduction_fee,</if> <if test="actualDeductionFee != null">actual_deduction_fee,</if>
<if test="suitDepositDeduction != null">suit_deposit_deduction,</if>
<if test="version != null">version,</if>
<if test="payType != null and payType != ''">pay_type,</if>
<if test="depositDeductionAmount != null">deposit_deduction_amount,</if>
<if test="ridePayId != null">ride_pay_id,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="no != null and no != ''">#{no},</if> <if test="no != null and no != ''">#{no},</if>
@ -295,6 +310,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="actualDispatchFee != null">#{actualDispatchFee},</if> <if test="actualDispatchFee != null">#{actualDispatchFee},</if>
<if test="actualManageFee != null">#{actualManageFee},</if> <if test="actualManageFee != null">#{actualManageFee},</if>
<if test="actualDeductionFee != null">#{actualDeductionFee},</if> <if test="actualDeductionFee != null">#{actualDeductionFee},</if>
<if test="suitDepositDeduction != null">#{suitDepositDeduction},</if>
<if test="version != null">#{version},</if>
<if test="payType != null and payType != ''">#{payType},</if>
<if test="depositDeductionAmount != null">#{depositDeductionAmount},</if>
<if test="ridePayId != null">#{ridePayId},</if>
</trim> </trim>
</insert> </insert>
@ -351,6 +371,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.actualDispatchFee != null">actual_dispatch_fee = #{data.actualDispatchFee},</if> <if test="data.actualDispatchFee != null">actual_dispatch_fee = #{data.actualDispatchFee},</if>
<if test="data.actualManageFee != null">actual_manage_fee = #{data.actualManageFee},</if> <if test="data.actualManageFee != null">actual_manage_fee = #{data.actualManageFee},</if>
<if test="data.actualDeductionFee != null">actual_deduction_fee = #{data.actualDeductionFee},</if> <if test="data.actualDeductionFee != null">actual_deduction_fee = #{data.actualDeductionFee},</if>
<if test="data.suitDepositDeduction != null">suit_deposit_deduction = #{data.suitDepositDeduction},</if>
<if test="data.version != null">version = #{data.version},</if>
<if test="data.payType != null and data.payType != ''">pay_type = #{data.payType},</if>
<if test="data.depositDeductionAmount != null">deposit_deduction_amount = #{data.depositDeductionAmount},</if>
<if test="data.ridePayId != null">ride_pay_id = #{data.ridePayId},</if>
</sql> </sql>
<delete id="deleteOrderById" parameterType="Long"> <delete id="deleteOrderById" parameterType="Long">
@ -483,16 +508,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</select> </select>
<!-- addDeductionFee -->
<update id="addDeductionFee">
update bst_order bo
set bo.deduction_fee = bo.deduction_fee + #{deductionFee},
bo.actual_deduction_fee = bo.actual_deduction_fee + #{deductionFee},
bo.total_fee = bo.total_fee + #{deductionFee}
where bo.id = #{id} and bo.total_fee + #{deductionFee} &lt;= bo.deposit_fee
</update>
<!-- selectIdByQuery --> <!-- selectIdByQuery -->
<select id="selectIdByQuery" resultType="Long"> <select id="selectIdByQuery" resultType="Long">
@ -563,4 +578,28 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</where> </where>
</select> </select>
<!-- deductDeposit -->
<update id="deductDeposit">
update bst_order bo
left join bst_pay bp on bp.id = bo.pay_id
set bo.deposit_deduction_amount = bo.deposit_deduction_amount + #{amount}
where bo.id = #{id}
and <include refid="depositCanDeductRemain"/> &gt;= #{amount}
</update>
<!-- 剩余可抵扣押金 = 已支付押金 - 退款中 - 已退款 - 已抵扣 - 车损费 -->
<sql id="depositCanDeductRemain">
bp.amount - IFNULL(bp.refunding, 0) - IFNULL(bp.refunded, 0) - IFNULL(bo.deposit_deduction_amount, 0) - IFNULL(bo.actual_deduction_fee, 0)
</sql>
<!-- addDeductionFee -->
<update id="addDeductionFee">
update bst_order bo
set bo.deduction_fee = bo.deduction_fee + #{deductionFee},
bo.actual_deduction_fee = bo.actual_deduction_fee + #{deductionFee},
bo.total_fee = bo.total_fee + #{deductionFee}
where bo.id = #{id}
and <include refid="depositCanDeductRemain"/> &gt;= #{deductionFee}
</update>
</mapper> </mapper>

View File

@ -11,6 +11,7 @@ import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCloseDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderCloseDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO; import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.dto.OrderDeductDTO;
import com.ruoyi.bst.order.domain.dto.OrderEndDTO; import com.ruoyi.bst.order.domain.dto.OrderEndDTO;
import com.ruoyi.bst.order.domain.dto.OrderOpenDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderOpenDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderRefundDTO; import com.ruoyi.bst.order.domain.dto.OrderRefundDTO;
@ -209,4 +210,13 @@ public interface OrderService {
* @param distance 距离 * @param distance 距离
*/ */
public int updateDisatance(Long id, BigDecimal distance); public int updateDisatance(Long id, BigDecimal distance);
/**
* 押金抵扣
*
* @param dto 参数
* @return 结果
*/
public int deduct(OrderDeductDTO dto);
} }

View File

@ -219,6 +219,7 @@ public class OrderConverterImpl implements OrderConverter{
Order order = new Order(); Order order = new Order();
// 基础信息 // 基础信息
order.setPayExpireTime(LocalDateTime.now().plusMinutes(3)); order.setPayExpireTime(LocalDateTime.now().plusMinutes(3));
order.setVersion(2);
// 用户信息 // 用户信息
UserVO user = bo.getUser(); UserVO user = bo.getUser();
@ -247,6 +248,7 @@ public class OrderConverterImpl implements OrderConverter{
order.setSuitStartRule(suit.getStartRule()); order.setSuitStartRule(suit.getStartRule());
order.setSuitIntervalRule(suit.getIntervalRule()); order.setSuitIntervalRule(suit.getIntervalRule());
order.setSuitSeconds(suit.getSeconds()); order.setSuitSeconds(suit.getSeconds());
order.setSuitDepositDeduction(suit.getDepositDeduction());
} }
// 价格信息 // 价格信息
@ -337,9 +339,6 @@ public class OrderConverterImpl implements OrderConverter{
data.setReturnType(order.getReturnType()); data.setReturnType(order.getReturnType());
data.setEndAreaSubName(order.getEndAreaSubName()); data.setEndAreaSubName(order.getEndAreaSubName());
data.setEndReason(order.getEndReason()); data.setEndReason(order.getEndReason());
data.setActualRidingFee(order.getActualRidingFee());
data.setActualDispatchFee(order.getActualDispatchFee());
data.setActualManageFee(order.getActualManageFee());
return data; return data;
} }

View File

@ -41,14 +41,17 @@ import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.bo.OrderChangeBO; import com.ruoyi.bst.order.domain.bo.OrderChangeBO;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO; import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.bo.OrderEndBO; import com.ruoyi.bst.order.domain.bo.OrderEndBO;
import com.ruoyi.bst.order.domain.bo.OrderRidePaySuccessBO;
import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO; import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCloseDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderCloseDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderCreateDTO; import com.ruoyi.bst.order.domain.dto.OrderCreateDTO;
import com.ruoyi.bst.order.domain.dto.OrderDeductDTO;
import com.ruoyi.bst.order.domain.dto.OrderEndDTO; import com.ruoyi.bst.order.domain.dto.OrderEndDTO;
import com.ruoyi.bst.order.domain.dto.OrderOpenDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderOpenDeviceDTO;
import com.ruoyi.bst.order.domain.dto.OrderRefundDTO; import com.ruoyi.bst.order.domain.dto.OrderRefundDTO;
import com.ruoyi.bst.order.domain.dto.OrderVerifyDTO; import com.ruoyi.bst.order.domain.dto.OrderVerifyDTO;
import com.ruoyi.bst.order.domain.enums.OrderPayType;
import com.ruoyi.bst.order.domain.enums.OrderReturnType; import com.ruoyi.bst.order.domain.enums.OrderReturnType;
import com.ruoyi.bst.order.domain.enums.OrderStatus; import com.ruoyi.bst.order.domain.enums.OrderStatus;
import com.ruoyi.bst.order.domain.vo.OrderEndVO; import com.ruoyi.bst.order.domain.vo.OrderEndVO;
@ -427,15 +430,14 @@ public class OrderServiceImpl implements OrderService {
order.setDuration(Duration.between(order.getStartTime(), order.getEndTime()).getSeconds()); order.setDuration(Duration.between(order.getStartTime(), order.getEndTime()).getSeconds());
order.setDistance(LocationLogUtil.calcDistance(bo.getPositionList())); order.setDistance(LocationLogUtil.calcDistance(bo.getPositionList()));
order.setEndReason(dto.getEndReason()); order.setEndReason(dto.getEndReason());
// 还车类型
order.setReturnType(returnType); order.setReturnType(returnType);
// 订单状态 order.setStatus(OrderStatus.RIDE_WAIT_PAY.getCode());
this.setOrderStatus(order, returnType);
// 订单费用 // 订单费用
boolean needDispatchFee = dto.getNeedDispatchFee() != null && dto.getNeedDispatchFee(); boolean needDispatchFee = dto.getNeedDispatchFee() != null && dto.getNeedDispatchFee();
this.setOrderFee(order, area, inParkingVO, needDispatchFee); this.setOrderFee(order, area, inParkingVO, needDispatchFee);
Integer result = transactionTemplate.execute(status -> { transactionTemplate.execute(status -> {
// 更新订单数据 // 更新订单数据
Order data = orderConverter.toPOByEnd(order); Order data = orderConverter.toPOByEnd(order);
OrderQuery query = new OrderQuery(); OrderQuery query = new OrderQuery();
@ -445,16 +447,6 @@ public class OrderServiceImpl implements OrderService {
ServiceUtil.assertion(rows != 1, "ID为%s的订单更新失败", order.getId()); ServiceUtil.assertion(rows != 1, "ID为%s的订单更新失败", order.getId());
vo.setDb(rows); vo.setDb(rows);
// 当订单状态为已完成时处理订单完成操作
if (OrderStatus.FINISHED.getCode().equals(order.getStatus())) {
// 预分成
boolean bonus = this.prepayBonus(order.getId());
ServiceUtil.assertion(!bonus, "ID为%s的订单预分成失败", order.getId());
} else if (OrderStatus.WAIT_VERIFY.getCode().equals(order.getStatus())) {
// 发送还车审核通知
smsService.sendOrderWaitVerifyMsg(order);
}
// 结束订单设备 // 结束订单设备
int finish = orderDeviceService.finish(orderDevice, dto.getPicture(), inParkingVO); int finish = orderDeviceService.finish(orderDevice, dto.getPicture(), inParkingVO);
ServiceUtil.assertion(finish != 1, "结束ID为%s的订单设备失败", orderDevice.getId()); ServiceUtil.assertion(finish != 1, "结束ID为%s的订单设备失败", orderDevice.getId());
@ -468,14 +460,26 @@ public class OrderServiceImpl implements OrderService {
return rows; return rows;
}); });
boolean isSuccess = result != null && result == 1; if (order.getSuitDepositDeduction() != null && order.getSuitDepositDeduction()) {
if (isSuccess && OrderStatus.FINISHED.getCode().equals(order.getStatus())) { this.autoDeduct(order);
this.handleFinished(order);
} }
return vo; return vo;
} }
/**
* 自动押金抵扣
* @param order
*/
private int autoDeduct(OrderVO order) {
OrderDeductDTO dto = new OrderDeductDTO();
dto.setId(order.getId());
dto.setAmount(order.getTotalFee());
dto.setNeedVerify(true);
dto.setReduceAmountWhenNotEnough(true);
return this.deduct(dto);
}
/** /**
* 设置日志参数 * 设置日志参数
* @param lon 手机定位经度 * @param lon 手机定位经度
@ -501,8 +505,8 @@ public class OrderServiceImpl implements OrderService {
device.setLocationType(DeviceLocationType.PHONE.getCode()); device.setLocationType(DeviceLocationType.PHONE.getCode());
device.setLastLocationTime(LocalDateTime.now()); device.setLastLocationTime(LocalDateTime.now());
// 若卫星信号弱则更新设备定位 // 若卫星信号弱或者车辆离线则更新设备定位
if (DeviceUtil.isLowSatelliteSignal(device)) { if (DeviceUtil.isLowSatelliteSignal(device) || DeviceUtil.isOffline(device)) {
Device data = new Device(); Device data = new Device();
data.setMac(device.getMac()); data.setMac(device.getMac());
data.setLongitude(device.getLongitude()); data.setLongitude(device.getLongitude());
@ -530,23 +534,13 @@ public class OrderServiceImpl implements OrderService {
// 处理订单完成 // 处理订单完成
private void handleFinished(OrderVO order) { private void handleFinished(OrderVO order) {
scheduledExecutorService.schedule(() -> { // 预分成
int refund = this.refundRemainAmount(order); boolean bonus = this.prepayBonus(order.getId());
ServiceUtil.assertion(refund != 1, "ID为%s的订单退还剩余金额失败", order.getId()); ServiceUtil.assertion(!bonus, "ID为%s的订单预分成失败", order.getId());
}, 60, TimeUnit.SECONDS);
}
// 设置订单状态 // 剩余金额退款
private void setOrderStatus(OrderVO order, String returnType) { int refund = this.refundRemainAmount(order);
// 根据是否需要审核设置订单状态 ServiceUtil.assertion(refund != 1, "ID为%s的订单退还剩余金额失败", order.getId());
if (order.getAreaReturnVerify() != null && order.getAreaReturnVerify()
&& OrderReturnType.needVerify().contains(returnType)) {
// 更新订单状态为待审核
order.setStatus(OrderStatus.WAIT_VERIFY.getCode());
} else {
// 更新订单状态为已完成
order.setStatus(OrderStatus.FINISHED.getCode());
}
} }
// 设置订单费用 // 设置订单费用
@ -556,9 +550,12 @@ public class OrderServiceImpl implements OrderService {
order.setDispatchFee(orderFee.getDispatchFee()); order.setDispatchFee(orderFee.getDispatchFee());
order.setRidingFee(orderFee.getRidingFee()); order.setRidingFee(orderFee.getRidingFee());
order.setTotalFee(orderFee.getTotalFee()); order.setTotalFee(orderFee.getTotalFee());
}
// 设置订单实收金额
private void setActualAmount(Order order, BigDecimal payAmount) {
// 实收金额 // 实收金额
BigDecimal remain = order.getPayedAmount(); BigDecimal remain = payAmount;
// 骑行费实收 // 骑行费实收
order.setActualRidingFee(MathUtils.min(remain, order.getRidingFee())); order.setActualRidingFee(MathUtils.min(remain, order.getRidingFee()));
remain = MathUtils.subtractDecimal(remain, order.getActualRidingFee()); remain = MathUtils.subtractDecimal(remain, order.getActualRidingFee());
@ -574,8 +571,8 @@ public class OrderServiceImpl implements OrderService {
return 0; return 0;
} }
// 订单剩余金额退款 // 订单剩余金额退款
// 退款金额 = 实付金额 - 实收金额 // 退款金额 = 押金支付金额 - 押金抵扣金额 - 车损费
BigDecimal refund = MathUtils.subtractDecimal(order.getPayedAmount(), order.getTotalFee()); BigDecimal refund = MathUtils.subtractDecimal(order.getPayedAmount(), order.getDepositDeductionAmount(), order.getDeductionFee());
if (refund != null && refund.compareTo(BigDecimal.ZERO) > 0) { if (refund != null && refund.compareTo(BigDecimal.ZERO) > 0) {
return this.refund(order, refund, null, null, "系统", RefundType.AUTO.getCode()); return this.refund(order, refund, null, null, "系统", RefundType.AUTO.getCode());
} }
@ -584,13 +581,14 @@ public class OrderServiceImpl implements OrderService {
} }
/** /**
* 退款 * 押金退款
* *
* @param order 订单 * @param order 订单
* @param amount 退款金额 * @param amount 退款金额
* @param reason 退款原因 * @param reason 退款原因
* @param userId 操作人ID * @param userId 操作人ID
* @param userName 操作人名称 * @param userName 操作人名称
* @param type 退款类型
* @return 结果 * @return 结果
*/ */
private int refund(OrderVO order, BigDecimal amount, String reason, Long userId, String userName, String type) { private int refund(OrderVO order, BigDecimal amount, String reason, Long userId, String userName, String type) {
@ -617,9 +615,11 @@ public class OrderServiceImpl implements OrderService {
Integer result = transactionTemplate.execute(status -> { Integer result = transactionTemplate.execute(status -> {
// 分成退款 // 分成退款
boolean bonusRefund = bonusService.refundByBst(BonusBstType.ORDER, order.getId(), amount, if (RefundType.ADMIN.getCode().equals(type)) {
order.getPayAmount(), finalRefundReason); boolean bonusRefund = bonusService.refundByBst(BonusBstType.ORDER, order.getId(), amount,
ServiceUtil.assertion(!bonusRefund, "ID为%s的订单分成退款失败", order.getId()); order.getPayAmount(), finalRefundReason);
ServiceUtil.assertion(!bonusRefund, "ID为%s的订单分成退款失败", order.getId());
}
// 支付退款 // 支付退款
PayRefundDTO dto = new PayRefundDTO(); PayRefundDTO dto = new PayRefundDTO();
@ -832,7 +832,7 @@ public class OrderServiceImpl implements OrderService {
Integer result = transactionTemplate.execute(status -> { Integer result = transactionTemplate.execute(status -> {
// 更新订单状态 // 更新订单状态
Order data = new Order(); Order data = new Order();
data.setStatus(pass ? OrderStatus.FINISHED.getCode() : OrderStatus.REJECTED.getCode()); data.setStatus(OrderStatus.FINISHED.getCode());
data.setVerifyRemark(dto.getRemark()); data.setVerifyRemark(dto.getRemark());
OrderQuery query = new OrderQuery(); OrderQuery query = new OrderQuery();
query.setId(dto.getId()); query.setId(dto.getId());
@ -842,24 +842,21 @@ public class OrderServiceImpl implements OrderService {
// 更新车损费 // 更新车损费
if (dto.getDeductionFee() != null && dto.getDeductionFee().compareTo(BigDecimal.ZERO) > 0) { if (dto.getDeductionFee() != null && dto.getDeductionFee().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal max = MathUtils.subtractDecimal(order.getDepositFee(), order.getTotalFee()); BigDecimal max = OrderUtil.calcRemainCanDeductDeposit(order);
ServiceUtil.assertion(MathUtils.biggerThan(dto.getDeductionFee(), max), "ID为%s的订单车损费不允许超过%s", ServiceUtil.assertion(MathUtils.biggerThan(dto.getDeductionFee(), max), "ID为%s的订单车损费不允许超过%s",
dto.getId(), max); dto.getId(), max);
int add = orderMapper.addDeductionFee(order.getId(), dto.getDeductionFee()); int add = orderMapper.addDeductionFee(order.getId(), dto.getDeductionFee());
ServiceUtil.assertion(add != 1, "ID为%s的订单增加车损费失败请刷新后重试", order.getId()); ServiceUtil.assertion(add != 1, "ID为%s的订单增加车损费失败请刷新后重试", order.getId());
} }
// 预分成 // 订单结束操作
boolean bonus = this.prepayBonus(dto.getId()); if (pass) {
ServiceUtil.assertion(!bonus, "ID为%s的订单预分成失败", order.getId()); this.handleFinished(order);
}
return rows; return rows;
}); });
// 若审核通过则退还剩余金额
if (pass && result != null && result > 0) {
this.handleFinished(order);
}
return result == null ? 0 : result; return result == null ? 0 : result;
} }
@ -888,4 +885,90 @@ public class OrderServiceImpl implements OrderService {
return orderMapper.updateOrder(data); return orderMapper.updateOrder(data);
} }
@Override
public int deduct(OrderDeductDTO dto) {
// 查询订单
OrderVO order = this.selectOrderById(dto.getId());
ServiceUtil.assertion(order == null, "ID为%s的订单不存在", dto.getId());
// 判断是否可以抵扣
ServiceUtil.assertion(!OrderStatus.canDeduct().contains(order.getStatus()), "ID为%s的订单当前状态不允许抵扣", dto.getId());
BigDecimal canDeductAmount = OrderUtil.calcRemainCanDeductDeposit(order);
// 获取实际抵扣金额
BigDecimal deductAmount;
if (MathUtils.biggerThan(dto.getAmount(), canDeductAmount)) {
// 抵扣金额不足时
ServiceUtil.assertion(!dto.getReduceAmountWhenNotEnough(), "ID为%s的订单可抵扣押金不足当前可抵扣金额为%s", dto.getId(), canDeductAmount);
deductAmount = canDeductAmount;
} else {
// 抵扣金额充足时
deductAmount = dto.getAmount();
}
// 关闭订单的支付ID
boolean closePay = payService.closeByBstId(PayBstType.ORDER_RIDE.getType(), order.getId());
ServiceUtil.assertion(!closePay, "ID为%s的订单骑行费支付关闭失败", dto.getId());
// 更新订单数据
Integer result = transactionTemplate.execute(status -> {
// 押金抵扣
int deduct = orderMapper.deductDeposit(order.getId(), deductAmount);
ServiceUtil.assertion(deduct != 1, "ID为%s的订单押金抵扣失败剩余可抵扣押金不足", dto.getId());
OrderRidePaySuccessBO bo = new OrderRidePaySuccessBO();
bo.setOrder(order);
bo.setPayType(OrderPayType.DEPOSIT_DEDUCTION);
bo.setPayAmount(deductAmount);
bo.setNeedVerify(dto.getNeedVerify());
return this.handleRidePaySuccess(bo);
});
return result == null ? 0 : result;
}
private int handleRidePaySuccess(OrderRidePaySuccessBO bo) {
OrderVO order = bo.getOrder();
OrderPayType payType = bo.getPayType();
// 更新订单数据
Order data = new Order();
data.setId(order.getId());
data.setPayType(payType.getCode());
// 计算实收金额
this.setActualAmount(data, bo.getPayAmount());
// 根据是否需要审核设置订单状态
if (order.getAreaReturnVerify() != null && order.getAreaReturnVerify() && bo.getNeedVerify()) {
data.setStatus(OrderStatus.WAIT_VERIFY.getCode());
} else {
data.setStatus(OrderStatus.FINISHED.getCode());
}
OrderQuery query = new OrderQuery();
query.setId(order.getId());
query.setStatusList(OrderStatus.canRidePaySuccess());
query.setIsNullPayType(true);
Integer result = transactionTemplate.execute(status -> {
int rows = orderMapper.updateByQuery(data, query);
ServiceUtil.assertion(rows != 1, "ID为%s的骑行支付更新失败", order.getId());
// 当订单状态为待审核时发送还车审核通知
if (OrderStatus.WAIT_VERIFY.getCode().equals(data.getStatus())) {
smsService.sendOrderWaitVerifyMsg(order);
}
// 订单结束操作
if (OrderStatus.FINISHED.getCode().equals(data.getStatus())) {
this.handleFinished(order);
}
return rows;
});
return result == null ? 0 : result;
}
} }

View File

@ -198,4 +198,16 @@ public class OrderUtil {
return query; return query;
} }
/**
* 剩余可抵扣押金
* @param order
* @return
*/
public static BigDecimal calcRemainCanDeductDeposit(OrderVO order) {
if (order == null) {
return BigDecimal.ZERO;
}
return MathUtils.dv(order.getDepositDeductRemain());
}
} }

View File

@ -17,7 +17,8 @@ import lombok.Getter;
@Getter @Getter
@AllArgsConstructor @AllArgsConstructor
public enum PayBstType { public enum PayBstType {
ORDER("1", "订单", OrderPayHandlerImpl.class); ORDER("1", "订单押金", OrderPayHandlerImpl.class),
ORDER_RIDE("2", "订单骑行费", null);
private final String type; private final String type;
private final String msg; private final String msg;

View File

@ -103,6 +103,11 @@ public class Suit extends BaseEntity implements LogBizParam
@ApiModelProperty("可用时长(秒)") @ApiModelProperty("可用时长(秒)")
private Long seconds; private Long seconds;
@Excel(name = "自动押金抵扣")
@ApiModelProperty("自动押金抵扣")
@NotNull(message = "自动押金抵扣不能为空", groups = {ValidGroup.Create.class})
private Boolean depositDeduction;
@Override @Override
public Object logBizId() { public Object logBizId() {
return id; return id;

View File

@ -27,6 +27,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bs.deposit_amount, bs.deposit_amount,
bs.type, bs.type,
bs.seconds, bs.seconds,
bs.deposit_deduction,
su.nick_name as user_name su.nick_name as user_name
<include refid="searchTables"/> <include refid="searchTables"/>
</sql> </sql>
@ -98,6 +99,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="depositAmount != null">deposit_amount,</if> <if test="depositAmount != null">deposit_amount,</if>
<if test="seconds != null">seconds,</if> <if test="seconds != null">seconds,</if>
<if test="type != null and type != ''">type,</if> <if test="type != null and type != ''">type,</if>
<if test="depositDeduction != null">deposit_deduction,</if>
</trim> </trim>
<trim prefix="values (" suffix=")" suffixOverrides=","> <trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if> <if test="userId != null">#{userId},</if>
@ -115,6 +117,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="depositAmount != null">#{depositAmount},</if> <if test="depositAmount != null">#{depositAmount},</if>
<if test="seconds != null">#{seconds},</if> <if test="seconds != null">#{seconds},</if>
<if test="type != null and type != ''">#{type},</if> <if test="type != null and type != ''">#{type},</if>
<if test="depositDeduction != null">#{depositDeduction},</if>
</trim> </trim>
</insert> </insert>
@ -142,6 +145,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.depositAmount != null">deposit_amount = #{data.depositAmount},</if> <if test="data.depositAmount != null">deposit_amount = #{data.depositAmount},</if>
<if test="data.type != null and data.type != ''">type = #{data.type},</if> <if test="data.type != null and data.type != ''">type = #{data.type},</if>
<if test="data.seconds != null">seconds = #{data.seconds},</if> <if test="data.seconds != null">seconds = #{data.seconds},</if>
<if test="data.depositDeduction != null">deposit_deduction = #{data.depositDeduction},</if>
</sql> </sql>
<delete id="deleteSuitById" parameterType="Long"> <delete id="deleteSuitById" parameterType="Long">

View File

@ -20,6 +20,7 @@ import org.springframework.web.bind.annotation.RestController;
import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission; import com.ruoyi.bst.areaJoin.domain.enums.AreaJoinPermission;
import com.ruoyi.bst.order.domain.OrderQuery; import com.ruoyi.bst.order.domain.OrderQuery;
import com.ruoyi.bst.order.domain.OrderVO; import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.dto.OrderDeductDTO;
import com.ruoyi.bst.order.domain.dto.OrderEndDTO; import com.ruoyi.bst.order.domain.dto.OrderEndDTO;
import com.ruoyi.bst.order.domain.dto.OrderRefundDTO; import com.ruoyi.bst.order.domain.dto.OrderRefundDTO;
import com.ruoyi.bst.order.domain.dto.OrderVerifyDTO; import com.ruoyi.bst.order.domain.dto.OrderVerifyDTO;
@ -154,4 +155,19 @@ public class OrderController extends BaseController
return toAjax(orderService.verify(dto)); return toAjax(orderService.verify(dto));
} }
/**
* 押金抵扣
*/
@Log(title = "押金抵扣", businessType = BusinessType.UPDATE, bizIdName = "arg0", bizType = LogBizType.ORDER)
@PreAuthorize("@ss.hasPermi('bst:order:deduct')")
@PutMapping("/deduct")
public AjaxResult deduct(@RequestBody @Validated OrderDeductDTO dto) {
if (!orderValidator.canOperate(dto.getId())) {
return error("您无权押金抵扣ID为" + dto.getId() + "的订单");
}
dto.setNeedVerify(false);
dto.setReduceAmountWhenNotEnough(false);
return toAjax(orderService.deduct(dto));
}
} }