卡券功能

This commit is contained in:
磷叶 2025-05-27 14:08:25 +08:00
parent ecfbd97e88
commit d9b769ce52
20 changed files with 224 additions and 89 deletions

View File

@ -177,7 +177,7 @@ public class BonusConverterImpl implements BonusConverter {
String reason = "卡券订单收入:" + order.getNo(); String reason = "卡券订单收入:" + order.getNo();
for (Bonus bonus : result) { for (Bonus bonus : result) {
bonus.setReason(reason); bonus.setReason(reason);
bonus.setBstType(BonusBstType.ORDER.getType()); bonus.setBstType(BonusBstType.VIP_ORDER.getType());
bonus.setBstId(order.getId()); bonus.setBstId(order.getId());
bonus.setStatus(BonusStatus.INVALID.getStatus()); bonus.setStatus(BonusStatus.INVALID.getStatus());
bonus.setToBalance(true); bonus.setToBalance(true);

View File

@ -39,8 +39,8 @@ public class DeviceUtil {
} }
// 转换经纬度坐标系并赋值 // 转换经纬度坐标系并赋值
// 只有定位是正常的才认为是有获取到定位 // 只有定位是正常的 && 卫星信号大于2颗 才认为是有获取到定位
if (DeviceUtil.validLocation(sys.getLon(), sys.getLat())) { if (DeviceUtil.validLocation(sys.getLon(), sys.getLat()) && sys.getS() != null && sys.getS() > 2) {
List<BigDecimal> coordinates = null; List<BigDecimal> coordinates = null;
// 转换坐标 // 转换坐标

View File

@ -281,4 +281,8 @@ public class Order extends BaseEntity {
@Excel(name = "卡券优惠值") @Excel(name = "卡券优惠值")
@ApiModelProperty("卡券优惠值") @ApiModelProperty("卡券优惠值")
private BigDecimal vipDiscountValue; private BigDecimal vipDiscountValue;
@Excel(name = "卡券名称")
@ApiModelProperty("卡券名称")
private String vipName;
} }

View File

@ -65,5 +65,8 @@ public class OrderQuery extends OrderVO {
@ApiModelProperty("骑行费支付日期范围") @ApiModelProperty("骑行费支付日期范围")
@DateTimeFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd")
private List<LocalDate> ridePayDateRange; private List<LocalDate> ridePayDateRange;
@ApiModelProperty("VIP ID 为空")
private Boolean isNullVipUserId;
} }

View File

@ -134,4 +134,9 @@ public class OrderVO extends Order implements IotDevice {
public BigDecimal getActualReceivedAmount() { public BigDecimal getActualReceivedAmount() {
return MathUtils.subtractDecimal(this.getActualAmount(), this.getAdminRefundAmount()); return MathUtils.subtractDecimal(this.getActualAmount(), this.getAdminRefundAmount());
} }
// 总优惠金额
public BigDecimal getTotalDiscountAmount() {
return this.getRidingDiscount();
}
} }

View File

@ -1,24 +1,27 @@
package com.ruoyi.bst.order.domain.bo; package com.ruoyi.bst.order.domain.dto;
import java.math.BigDecimal;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.ruoyi.bst.order.domain.OrderVO;
import com.ruoyi.bst.order.domain.enums.OrderPayType; import com.ruoyi.bst.order.domain.enums.OrderPayType;
import lombok.Data; import lombok.Data;
@Data @Data
public class OrderRidePaySuccessBO { public class OrderRidePaySuccessDTO {
// 订单数据 // 订单数据
private OrderVO order; private Long orderId;
// 支付方式 // 支付方式
private OrderPayType payType; private OrderPayType payType;
// 是否忽略审核 // 是否忽略审核
private Boolean ignoreVerify; private Boolean ignoreVerify;
// 支付金额
private BigDecimal payAmount;
// 支付ID // 支付ID
private Long payId; private Long payId;

View File

@ -91,4 +91,9 @@ public enum OrderStatus {
public static List<String> canUpdatePrice() { public static List<String> canUpdatePrice() {
return CollectionUtils.map(OrderStatus::getCode, RIDE_WAIT_PAY); return CollectionUtils.map(OrderStatus::getCode, RIDE_WAIT_PAY);
} }
// 允许恢复VIP优惠次数的订单状态
public static List<String> canRecoverVipCount() {
return CollectionUtils.map(OrderStatus::getCode, RIDE_WAIT_PAY);
}
} }

View File

@ -206,7 +206,7 @@ public interface OrderMapper {
* 押金抵扣 * 押金抵扣
* @param data * @param data
*/ */
int deductDeposit(@Param("data") Order data); int deductDeposit(@Param("id") Long id, @Param("depositDeductionAmount") BigDecimal depositDeductionAmount);
/** /**
* 更新退款信息 * 更新退款信息
@ -216,4 +216,12 @@ public interface OrderMapper {
*/ */
int updateRefund(@Param("data") Order data, @Param("query") OrderQuery query); int updateRefund(@Param("data") Order data, @Param("query") OrderQuery query);
/**
* 清空订单的VIP信息
* @param orderId
* @param vipUserId
* @return
*/
int clearVipInfo(@Param("orderId") Long orderId, @Param("vipUserId") Long vipUserId);
} }

View File

@ -75,6 +75,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
bo.riding_discount, bo.riding_discount,
bo.vip_type, bo.vip_type,
bo.vip_discount_value, bo.vip_discount_value,
bo.vip_name,
<include refid="depositCanDeductRemain"/> as deposit_deduct_remain, <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,
@ -167,6 +168,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.adminRefundCount != null "> and bo.admin_refund_count = #{query.adminRefundCount}</if> <if test="query.adminRefundCount != null "> and bo.admin_refund_count = #{query.adminRefundCount}</if>
<if test="query.adminRefundAmount != null "> and bo.admin_refund_amount = #{query.adminRefundAmount}</if> <if test="query.adminRefundAmount != null "> and bo.admin_refund_amount = #{query.adminRefundAmount}</if>
<if test="query.priceChanged != null "> and bo.price_changed = #{query.priceChanged}</if> <if test="query.priceChanged != null "> and bo.price_changed = #{query.priceChanged}</if>
<if test="query.isNullVipUserId != null"> and bo.vip_user_id is <if test="!query.isNullVipUserId">not</if> null</if>
<if test="query.vipName != null and query.vipName != ''"> and bo.vip_name like concat('%', #{query.vipName}, '%')</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
@ -299,6 +302,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="ridingDiscount != null">riding_discount,</if> <if test="ridingDiscount != null">riding_discount,</if>
<if test="vipType != null">vip_type,</if> <if test="vipType != null">vip_type,</if>
<if test="vipDiscountValue != null">vip_discount_value,</if> <if test="vipDiscountValue != null">vip_discount_value,</if>
<if test="vipName != null">vip_name,</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>
@ -364,6 +368,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="ridingDiscount != null">#{ridingDiscount},</if> <if test="ridingDiscount != null">#{ridingDiscount},</if>
<if test="vipType != null">#{vipType},</if> <if test="vipType != null">#{vipType},</if>
<if test="vipDiscountValue != null">#{vipDiscountValue},</if> <if test="vipDiscountValue != null">#{vipDiscountValue},</if>
<if test="vipName != null">#{vipName},</if>
</trim> </trim>
</insert> </insert>
@ -439,6 +444,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.ridingDiscount != null">riding_discount = #{data.ridingDiscount},</if> <if test="data.ridingDiscount != null">riding_discount = #{data.ridingDiscount},</if>
<if test="data.vipType != null">vip_type = #{data.vipType},</if> <if test="data.vipType != null">vip_type = #{data.vipType},</if>
<if test="data.vipDiscountValue != null">vip_discount_value = #{data.vipDiscountValue},</if> <if test="data.vipDiscountValue != null">vip_discount_value = #{data.vipDiscountValue},</if>
<if test="data.vipName != null">vip_name = #{data.vipName},</if>
</sql> </sql>
<delete id="deleteOrderById" parameterType="Long"> <delete id="deleteOrderById" parameterType="Long">
@ -635,14 +641,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<update id="deductDeposit"> <update id="deductDeposit">
update bst_order bo update bst_order bo
left join bst_pay bp on bp.id = bo.pay_id left join bst_pay bp on bp.id = bo.pay_id
set bo.deposit_deduction_amount = bo.deposit_deduction_amount + #{data.depositDeductionAmount}, set bo.deposit_deduction_amount = bo.deposit_deduction_amount + #{depositDeductionAmount}
bo.actual_amount = #{data.actualAmount}, where bo.id = #{id}
bo.actual_riding_fee = #{data.actualRidingFee}, and <include refid="depositCanDeductRemain"/> &gt;= #{depositDeductionAmount}
bo.actual_dispatch_fee = #{data.actualDispatchFee},
bo.actual_manage_fee = #{data.actualManageFee},
bo.actual_deduction_fee = #{data.actualDeductionFee}
where bo.id = #{data.id}
and <include refid="depositCanDeductRemain"/> &gt;= #{data.depositDeductionAmount}
</update> </update>
<!-- 剩余可抵扣押金 = 已支付押金 - 退款中 - 已退款 - 已抵扣 --> <!-- 剩余可抵扣押金 = 已支付押金 - 退款中 - 已退款 - 已抵扣 -->
@ -701,5 +702,17 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</update> </update>
<!-- clearVipInfo -->
<update id="clearVipInfo">
update bst_order bo
set bo.vip_user_id = null,
bo.vip_type = null,
bo.vip_discount_value = null,
bo.vip_name = null,
bo.riding_discount = 0
where bo.id = #{orderId}
and bo.vip_user_id = #{vipUserId}
and bo.status = 'RIDE_WAIT_PAY'
</update>
</mapper> </mapper>

View File

@ -7,7 +7,7 @@ import com.ruoyi.bst.device.domain.vo.DeviceIotVO;
import com.ruoyi.bst.order.domain.Order; import com.ruoyi.bst.order.domain.Order;
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.bo.OrderRidePaySuccessBO; import com.ruoyi.bst.order.domain.dto.OrderRidePaySuccessDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO; import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcRideFeeDTO; import com.ruoyi.bst.order.domain.dto.OrderCalcRideFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO;
@ -246,10 +246,9 @@ public interface OrderService {
/** /**
* 处理骑行费支付成功 * 处理骑行费支付成功
* @param bo * @param dto @return
* @return
*/ */
public int handleRidePaySuccess(OrderRidePaySuccessBO bo); public int handleRidePaySuccess(OrderRidePaySuccessDTO dto);
/** /**
* 改价 * 改价

View File

@ -3,35 +3,30 @@ package com.ruoyi.bst.order.service.impl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import com.ruoyi.bst.order.domain.OrderVO; import com.ruoyi.bst.order.domain.dto.OrderRidePaySuccessDTO;
import com.ruoyi.bst.order.domain.bo.OrderRidePaySuccessBO;
import com.ruoyi.bst.order.domain.enums.OrderPayType; import com.ruoyi.bst.order.domain.enums.OrderPayType;
import com.ruoyi.bst.order.service.OrderService; import com.ruoyi.bst.order.service.OrderService;
import com.ruoyi.bst.pay.domain.PayVO; import com.ruoyi.bst.pay.domain.PayVO;
import com.ruoyi.bst.pay.interfaces.PayHandler; import com.ruoyi.bst.pay.interfaces.PayHandler;
import com.ruoyi.common.utils.ServiceUtil;
@Service @Service
public class OrderRidePayHandlerImpl implements PayHandler { public class OrderRidePayHandlerImpl implements PayHandler {
@Autowired @Autowired
private OrderService orderService; private OrderService orderService;
@Override @Override
public boolean onPaySuccess(PayVO pay) { public boolean onPaySuccess(PayVO pay) {
// 查询订单
OrderVO order = orderService.selectOrderById(pay.getBstId());
ServiceUtil.assertion(order == null, "订单不存在");
// 处理骑行费支付成功 // 处理骑行费支付成功
OrderRidePaySuccessBO bo = new OrderRidePaySuccessBO(); OrderRidePaySuccessDTO dto = new OrderRidePaySuccessDTO();
bo.setOrder(order); dto.setOrderId(pay.getBstId());
bo.setPayType(OrderPayType.USER_PAY); dto.setPayType(OrderPayType.USER_PAY);
bo.setPayAmount(pay.getAmount()); dto.setPayAmount(pay.getAmount());
bo.setIgnoreVerify(false); dto.setIgnoreVerify(false);
bo.setPayId(pay.getId()); dto.setPayId(pay.getId());
bo.setPayTime(pay.getPayTime()); dto.setPayTime(pay.getPayTime());
int rows = orderService.handleRidePaySuccess(bo); int rows = orderService.handleRidePaySuccess(dto);
return rows == 1; return rows == 1;
} }

View File

@ -47,7 +47,7 @@ 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.OrderPayRideFeeBO; import com.ruoyi.bst.order.domain.bo.OrderPayRideFeeBO;
import com.ruoyi.bst.order.domain.bo.OrderRidePaySuccessBO; import com.ruoyi.bst.order.domain.dto.OrderRidePaySuccessDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO; import com.ruoyi.bst.order.domain.dto.OrderCalcFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderCalcRideFeeDTO; import com.ruoyi.bst.order.domain.dto.OrderCalcRideFeeDTO;
import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO; import com.ruoyi.bst.order.domain.dto.OrderChangeDeviceDTO;
@ -592,7 +592,8 @@ public class OrderServiceImpl implements OrderService {
BigDecimal remain = payAmount; BigDecimal remain = payAmount;
data.setActualAmount(payAmount); data.setActualAmount(payAmount);
// 骑行费实收 // 骑行费实收
data.setActualRidingFee(MathUtils.min(remain, old.getRidingFee())); BigDecimal ridingFee = MathUtils.subtractDecimal(old.getRidingFee(), old.getRidingDiscount());
data.setActualRidingFee(MathUtils.min(remain, ridingFee));
remain = MathUtils.subtractDecimal(remain, data.getActualRidingFee()); remain = MathUtils.subtractDecimal(remain, data.getActualRidingFee());
// 调度费实收 // 调度费实收
data.setActualDispatchFee(MathUtils.min(remain, old.getDispatchFee())); data.setActualDispatchFee(MathUtils.min(remain, old.getDispatchFee()));
@ -1048,22 +1049,22 @@ public class OrderServiceImpl implements OrderService {
// 更新订单数据 // 更新订单数据
Integer result = transactionTemplate.execute(status -> { Integer result = transactionTemplate.execute(status -> {
// 恢复旧优惠券次数清空订单优惠券信息
this.recoverVipCount(order);
// 押金抵扣计算实收金额 // 押金抵扣计算实收金额
Order data = new Order(); int deduct = orderMapper.deductDeposit(order.getId(), deductAmount);
data.setId(order.getId());
data.setDepositDeductionAmount(deductAmount);
this.setActualAmount(data, order, deductAmount);
int deduct = orderMapper.deductDeposit(data);
ServiceUtil.assertion(deduct != 1, "ID为%s的订单押金抵扣失败剩余可抵扣押金不足%s元", order.getId(), deductAmount); ServiceUtil.assertion(deduct != 1, "ID为%s的订单押金抵扣失败剩余可抵扣押金不足%s元", order.getId(), deductAmount);
// 处理抵扣成功 // 处理抵扣成功
OrderRidePaySuccessBO bo = new OrderRidePaySuccessBO(); OrderRidePaySuccessDTO dto = new OrderRidePaySuccessDTO();
bo.setOrder(order); dto.setOrderId(order.getId());
bo.setPayType(OrderPayType.DEPOSIT_DEDUCTION); dto.setPayType(OrderPayType.DEPOSIT_DEDUCTION);
bo.setIgnoreVerify(ignoreVerify); dto.setIgnoreVerify(ignoreVerify);
bo.setPayId(null); dto.setPayAmount(deductAmount);
bo.setPayTime(LocalDateTime.now()); dto.setPayId(null);
return this.handleRidePaySuccess(bo); dto.setPayTime(LocalDateTime.now());
return this.handleRidePaySuccess(dto);
}); });
return result == null ? 0 : result; return result == null ? 0 : result;
@ -1076,18 +1077,21 @@ public class OrderServiceImpl implements OrderService {
* 处理骑行费支付成功 * 处理骑行费支付成功
*/ */
@Override @Override
public int handleRidePaySuccess(OrderRidePaySuccessBO bo) { public int handleRidePaySuccess(OrderRidePaySuccessDTO dto) {
OrderVO order = bo.getOrder(); OrderVO order = selectOrderById(dto.getOrderId());
OrderPayType payType = bo.getPayType(); ServiceUtil.assertion(order == null, "ID为%s的订单不存在", dto.getOrderId());
OrderPayType payType = dto.getPayType();
// 更新订单数据 // 更新订单数据
Order data = new Order(); Order data = new Order();
// 支付信息 // 支付信息
data.setPayType(payType.getCode()); data.setPayType(payType.getCode());
data.setRidePayId(bo.getPayId()); data.setRidePayId(dto.getPayId());
data.setRidePayTime(bo.getPayTime()); data.setRidePayTime(dto.getPayTime());
this.setActualAmount(data, order, dto.getPayAmount());
// 设置订单状态 // 设置订单状态
boolean ignoreVerify = bo.getIgnoreVerify() != null && bo.getIgnoreVerify(); boolean ignoreVerify = dto.getIgnoreVerify() != null && dto.getIgnoreVerify();
if (order.getAreaReturnVerify() != null && order.getAreaReturnVerify() && !ignoreVerify) { if (order.getAreaReturnVerify() != null && order.getAreaReturnVerify() && !ignoreVerify) {
data.setStatus(OrderStatus.WAIT_VERIFY.getCode()); data.setStatus(OrderStatus.WAIT_VERIFY.getCode());
} else { } else {
@ -1137,32 +1141,35 @@ public class OrderServiceImpl implements OrderService {
OrderVO order = bo.getOrder(); OrderVO order = bo.getOrder();
VipUserVO vipUser = bo.getVipUser(); VipUserVO vipUser = bo.getVipUser();
OrderCalcRideFeeVO fee = bo.getFee(); OrderCalcRideFeeVO fee = bo.getFee();
return transactionTemplate.execute(status -> {
// TODO 恢复旧优惠券次数清空订单优惠券信息
if (order.getVipUserId() != null) {
} return transactionTemplate.execute(status -> {
// 恢复旧优惠券次数清空订单优惠券信息
this.recoverVipCount(order);
// 更新订单信息 // 更新订单信息
Order data = new Order(); Order data = new Order();
if (vipUser != null) { if (vipUser != null) {
data.setVipUserId(vipUser.getId()); data.setVipUserId(vipUser.getId());
data.setVipDiscountValue(vipUser.getDiscount()); data.setVipDiscountValue(vipUser.getDiscount());
data.setVipType(vipUser.getType()); data.setVipType(vipUser.getType());
// TODO 卡名称 data.setVipName(vipUser.getName());
} }
data.setRidingDiscount(fee.getRideFeeDiscount()); data.setRidingDiscount(fee.getRideFeeDiscount());
OrderQuery query = new OrderQuery(); OrderQuery query = new OrderQuery();
query.setId(order.getId()); query.setId(order.getId());
query.setVipUserIdIsNull(true); query.setIsNullVipUserId(true);
orderMapper.updateByQuery(data, query); int rows = orderMapper.updateByQuery(data, query);
ServiceUtil.assertion(rows != 1, "ID为%s的订单更新失败", order.getId());
// 若有使用优惠券则扣减优惠券次数
if (vipUser != null) {
int deduct = vipUserService.deductVipCount(vipUser.getId(), 1);
ServiceUtil.assertion(deduct != 1, "优惠券可用次数不足");
}
// TODO 若有使用优惠券则扣减优惠券次数
// 转为支付单 // 转为支付单
Pay pay = payConverter.toPayPO(bo); Pay pay = payConverter.toPayPO(bo);
// 创建并调起支付 // 创建并调起支付
return payService.createPayBill(pay); return payService.createPayBill(pay);
@ -1173,6 +1180,32 @@ public class OrderServiceImpl implements OrderService {
} }
/**
* 恢复VIP优惠次数
*/
private int recoverVipCount(OrderVO order) {
ServiceUtil.assertion(order == null, "订单不存在");
if (order.getVipUserId() == null) {
return 1;
}
ServiceUtil.assertion(!OrderStatus.canRecoverVipCount().contains(order.getStatus()), "ID为%s的订单当前状态不允许恢复VIP优惠次数", order.getId());
Integer result = transactionTemplate.execute(status -> {
// 清空订单的VIP信息
int clear = orderMapper.clearVipInfo(order.getId(), order.getVipUserId());
ServiceUtil.assertion(clear != 1, "ID为%s的订单清空VIP信息失败", order.getId());
// 尝试恢复VIP优惠次数
try {
vipUserService.recoverVipCount(order.getVipUserId(), 1);
} catch (Exception e) {
log.error("ID为{}的订单恢复VIP优惠次数失败", order.getId(), e);
}
return clear;
});
return result == null ? 0 : result;
}
@Override @Override
public int updatePrice(OrderUpdatePriceDTO dto) { public int updatePrice(OrderUpdatePriceDTO dto) {
// 查询订单 // 查询订单
@ -1235,7 +1268,7 @@ public class OrderServiceImpl implements OrderService {
public OrderCalcRideFeeVO calcRideFee(OrderVO order, VipUserVO vipUser) { public OrderCalcRideFeeVO calcRideFee(OrderVO order, VipUserVO vipUser) {
ServiceUtil.assertion(order == null, "订单不存在"); ServiceUtil.assertion(order == null, "订单不存在");
// 计算应付费用 // 计算应付费用
OrderCalcRideFeeVO vo = new OrderCalcRideFeeVO(); OrderCalcRideFeeVO vo = new OrderCalcRideFeeVO();
vo.setOriginalAmount(order.getRidingFee()); vo.setOriginalAmount(order.getRidingFee());
vo.setRideFeeDiscount(VipUserUtil.calcDiscountAmount(vo.getOriginalAmount(), vipUser)); // 骑行费优惠 vo.setRideFeeDiscount(VipUserUtil.calcDiscountAmount(vo.getOriginalAmount(), vipUser)); // 骑行费优惠
@ -1243,7 +1276,7 @@ public class OrderServiceImpl implements OrderService {
vo.setDispatchFee(order.getDispatchFee()); // 调度费 vo.setDispatchFee(order.getDispatchFee()); // 调度费
vo.setManageFee(order.getManageFee()); // 管理费 vo.setManageFee(order.getManageFee()); // 管理费
vo.setDeductionFee(order.getDeductionFee()); // 车损费 vo.setDeductionFee(order.getDeductionFee()); // 车损费
return vo; return vo;
} }
} }

View File

@ -82,7 +82,6 @@ public class OrderValidatorImpl implements OrderValidator{
ModelVO model = bo.getModel(); ModelVO model = bo.getModel();
ServiceUtil.assertion(model == null, "ID为%s的车型不存在", device.getModelId()); ServiceUtil.assertion(model == null, "ID为%s的车型不存在", device.getModelId());
ServiceUtil.assertion(CollectionUtils.isEmptyElement(model.getSuitIds()) || !model.getSuitIds().contains(suit.getId()), "ID为%s的套餐不可在ID为%s的车辆使用", dto.getSuitId(), dto.getDeviceId()); ServiceUtil.assertion(CollectionUtils.isEmptyElement(model.getSuitIds()) || !model.getSuitIds().contains(suit.getId()), "ID为%s的套餐不可在ID为%s的车辆使用", dto.getSuitId(), dto.getDeviceId());
ServiceUtil.assertion(suit.getSeconds() == null || suit.getSeconds() <= 0, "ID为%s的套餐时长不能为0或小于0", dto.getSuitId());
// 用户 // 用户
UserVO user = bo.getUser(); UserVO user = bo.getUser();
@ -262,7 +261,12 @@ public class OrderValidatorImpl implements OrderValidator{
OrderCalcRideFeeVO fee = bo.getFee(); OrderCalcRideFeeVO fee = bo.getFee();
ServiceUtil.assertion(fee == null, "ID为%s的订单当前费用不存在", dto.getOrderId()); ServiceUtil.assertion(fee == null, "ID为%s的订单当前费用不存在", dto.getOrderId());
ServiceUtil.assertion(!OrderUtil.isEquals(fee, dto.getFee()), "ID为%s的订单当前费用已发生变化请刷新后重新支付", dto.getOrderId()); ServiceUtil.assertion(!OrderUtil.isEquals(fee, dto.getFee()), "ID为%s的订单当前费用已发生变化请刷新后重新支付", dto.getOrderId());
// 用户
UserVO user = bo.getUser();
ServiceUtil.assertion(user == null, "ID为%s的用户不存在", dto.getUserId());
ServiceUtil.assertion(!Objects.equals(order.getUserId(), user.getUserId()), "ID为%s的用户无权限支付ID为%s的订单", dto.getUserId(), dto.getOrderId());
// 优惠券 // 优惠券
VipUserVO vipUser = bo.getVipUser(); VipUserVO vipUser = bo.getVipUser();
if (vipUser != null) { if (vipUser != null) {
@ -274,15 +278,12 @@ public class OrderValidatorImpl implements OrderValidator{
int limitCount = MathUtils.dv(vipUser.getLimitCount()); int limitCount = MathUtils.dv(vipUser.getLimitCount());
ServiceUtil.assertion(roundCount >= limitCount, "ID为%s的卡券已达到本周期使用次数上限", vipUser.getId()); ServiceUtil.assertion(roundCount >= limitCount, "ID为%s的卡券已达到本周期使用次数上限", vipUser.getId());
} }
ServiceUtil.assertion(!Objects.equals(order.getAreaId(), vipUser.getAreaId()), "ID为%s的卡券不可在ID为%s的运营区使用", vipUser.getId(), order.getAreaId());
ServiceUtil.assertion(!Objects.equals(vipUser.getUserId(), user.getUserId()), "ID为%s的用户无权使用ID为%s的卡券", user.getUserId(), vipUser.getId());
} }
// 用户
UserVO user = bo.getUser();
ServiceUtil.assertion(user == null, "ID为%s的用户不存在", dto.getUserId());
ServiceUtil.assertion(!Objects.equals(order.getUserId(), user.getUserId()), "ID为%s的用户无权限支付ID为%s的订单", dto.getUserId(), dto.getOrderId());
// 支付校验 // 支付校验
payValidator.checkPay(bo.getApp(), bo.getChannel(), bo.getUserApp()); payValidator.checkPay(bo.getApp(), bo.getChannel(), bo.getUserApp());
} }
} }

View File

@ -9,6 +9,7 @@ import com.ruoyi.bst.channel.domain.ChannelVO;
import com.ruoyi.bst.order.domain.Order; import com.ruoyi.bst.order.domain.Order;
import com.ruoyi.bst.order.domain.bo.OrderCreateBO; import com.ruoyi.bst.order.domain.bo.OrderCreateBO;
import com.ruoyi.bst.order.domain.bo.OrderPayRideFeeBO; import com.ruoyi.bst.order.domain.bo.OrderPayRideFeeBO;
import com.ruoyi.bst.order.domain.vo.OrderCalcRideFeeVO;
import com.ruoyi.bst.pay.domain.Pay; import com.ruoyi.bst.pay.domain.Pay;
import com.ruoyi.bst.pay.domain.enums.PayBstType; import com.ruoyi.bst.pay.domain.enums.PayBstType;
import com.ruoyi.bst.pay.domain.enums.PayStatus; import com.ruoyi.bst.pay.domain.enums.PayStatus;
@ -90,7 +91,8 @@ public class PayConverterImpl implements PayConverter {
ChannelVO channel = bo.getChannel(); ChannelVO channel = bo.getChannel();
UserAppVO userApp = bo.getUserApp(); UserAppVO userApp = bo.getUserApp();
AppVO app = bo.getApp(); AppVO app = bo.getApp();
if (order == null || user == null || channel == null || userApp == null || app == null) { OrderCalcRideFeeVO fee = bo.getFee();
if (order == null || user == null || channel == null || userApp == null || app == null || fee == null) {
return null; return null;
} }
@ -99,7 +101,7 @@ public class PayConverterImpl implements PayConverter {
// 订单信息 // 订单信息
pay.setBstType(PayBstType.ORDER_RIDE.getType()); pay.setBstType(PayBstType.ORDER_RIDE.getType());
pay.setBstId(order.getId()); pay.setBstId(order.getId());
pay.setAmount(order.getTotalFee()); pay.setAmount(fee.getPayAmount());
pay.setAreaId(order.getAreaId()); pay.setAreaId(order.getAreaId());
pay.setDescription("骑行费:" + order.getNo()); pay.setDescription("骑行费:" + order.getNo());

View File

@ -96,4 +96,20 @@ public interface VipUserMapper
* @return * @return
*/ */
int logicDel(@Param("ids") List<Long> ids); int logicDel(@Param("ids") List<Long> ids);
/**
* 恢复VIP优惠次数
* @param vipUserId
* @param count
* @return
*/
int recoverVipCount(@Param("vipUserId") Long vipUserId, @Param("count") int count);
/**
* 扣减VIP优惠次数
* @param id
* @param count
* @return
*/
int deductVipCount(@Param("id") Long id, @Param("count") int count);
} }

View File

@ -193,4 +193,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and bvu.deleted = false and bvu.deleted = false
</update> </update>
<!-- recoverVipCount -->
<update id="recoverVipCount">
update bst_vip_user bvu
set bvu.round_count = if (bvu.round_count &gt; 0, bvu.round_count - #{count}, 0),
bvu.surplus_total = bvu.surplus_total + #{count}
where bvu.id = #{vipUserId}
</update>
<!-- deductVipCount -->
<update id="deductVipCount">
update bst_vip_user bvu
set bvu.round_count = bvu.round_count + #{count},
bvu.surplus_total = bvu.surplus_total - #{count}
where bvu.id = #{id}
and bvu.surplus_total &gt;= #{count}
and bvu.round_count + #{count} &lt;= bvu.limit_count
</update>
</mapper> </mapper>

View File

@ -74,4 +74,19 @@ public interface VipUserService
*/ */
public int logicDel(List<Long> ids); public int logicDel(List<Long> ids);
/**
* 恢复VIP优惠次数
* @param vipUserId
* @return
*/
public int recoverVipCount(Long vipUserId, int count);
/**
* 扣减VIP优惠次数
* @param id
* @param count
* @return
*/
public int deductVipCount(Long id, int count);
} }

View File

@ -121,4 +121,19 @@ public class VipUserServiceImpl implements VipUserService
return vipUserMapper.logicDel(ids); return vipUserMapper.logicDel(ids);
} }
@Override
public int recoverVipCount(Long vipUserId, int count) {
if (vipUserId == null || count <= 0) {
return 0;
}
return vipUserMapper.recoverVipCount(vipUserId, count);
}
@Override
public int deductVipCount(Long id, int count) {
if (id == null || count <= 0) {
return 0;
}
return vipUserMapper.deductVipCount(id, count);
}
} }

View File

@ -11,7 +11,7 @@ import com.ruoyi.bst.vipUser.domain.VipUserVO;
import com.ruoyi.common.utils.MathUtils; import com.ruoyi.common.utils.MathUtils;
public class VipUserUtil { public class VipUserUtil {
public static LocalDateTime getNextResetTime(VipUser vip) { public static LocalDateTime getNextResetTime(VipUser vip) {
if (vip == null) { if (vip == null) {
return null; return null;
@ -32,9 +32,11 @@ public class VipUserUtil {
if (vipUser == null || originalAmount == null) { if (vipUser == null || originalAmount == null) {
return BigDecimal.ZERO; return BigDecimal.ZERO;
} }
if (VipType.DISCOUNT.getCode().equals(vipUser.getType())) { if (VipType.DISCOUNT.getCode().equals(vipUser.getType())) {
return MathUtils.mulDecimal(originalAmount, vipUser.getDiscount()).divide(BigDecimal.valueOf(10), 2, RoundingMode.HALF_UP); BigDecimal decimal10 = BigDecimal.valueOf(10);
return MathUtils.mulDecimal(originalAmount, MathUtils.subtractDecimal(decimal10, vipUser.getDiscount()))
.divide(decimal10, 2, RoundingMode.DOWN);
} else if (VipType.DEDUCT.getCode().equals(vipUser.getType())) { } else if (VipType.DEDUCT.getCode().equals(vipUser.getType())) {
return MathUtils.min(originalAmount, vipUser.getDiscount()); return MathUtils.min(originalAmount, vipUser.getDiscount());
} else if (VipType.BALANCE.getCode().equals(vipUser.getType())) { } else if (VipType.BALANCE.getCode().equals(vipUser.getType())) {

View File

@ -149,7 +149,7 @@ public class AppOrderController extends BaseController {
} }
return error("关闭订单失败"); return error("关闭订单失败");
} }
@ApiOperation("计算订单费用") @ApiOperation("计算订单费用")
@PostMapping("/calcFee") @PostMapping("/calcFee")
@Log(title = "计算订单费用", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER) @Log(title = "计算订单费用", businessType = BusinessType.OTHER, bizIdName = "arg0", bizType = LogBizType.ORDER)
@ -174,7 +174,6 @@ public class AppOrderController extends BaseController {
OrderVO order = orderService.selectOrderById(dto.getOrderId()); OrderVO order = orderService.selectOrderById(dto.getOrderId());
ServiceUtil.assertion(order == null, "订单不存在"); ServiceUtil.assertion(order == null, "订单不存在");
ServiceUtil.assertion(!orderValidator.canOpenDevice(order, getUserId()), "您无权操作ID为%s的订单设备开启", order.getId()); ServiceUtil.assertion(!orderValidator.canOpenDevice(order, getUserId()), "您无权操作ID为%s的订单设备开启", order.getId());
ServiceUtil.assertion(orderValidator.isTimeout(order), "预存款已完用,请还车后再扫码骑行,或联系客服人员处理!");
if (dto.getRequiredIot() == null) { if (dto.getRequiredIot() == null) {
dto.setRequiredIot(true); dto.setRequiredIot(true);
} }
@ -194,7 +193,6 @@ public class AppOrderController extends BaseController {
OrderVO order = orderService.selectOrderById(dto.getOrderId()); OrderVO order = orderService.selectOrderById(dto.getOrderId());
ServiceUtil.assertion(order == null, "订单不存在"); ServiceUtil.assertion(order == null, "订单不存在");
ServiceUtil.assertion(!orderValidator.canCloseDevice(order, getUserId()), "您无权操作ID为%s的订单设备关闭", order.getId()); ServiceUtil.assertion(!orderValidator.canCloseDevice(order, getUserId()), "您无权操作ID为%s的订单设备关闭", order.getId());
ServiceUtil.assertion(orderValidator.isTimeout(order), "预存款已完用,请还车后再扫码骑行,或联系客服人员处理!");
if (dto.getRequiredIot() == null) { if (dto.getRequiredIot() == null) {
dto.setRequiredIot(true); dto.setRequiredIot(true);
} }
@ -213,7 +211,6 @@ public class AppOrderController extends BaseController {
OrderVO order = orderService.selectOrderById(dto.getOrderId()); OrderVO order = orderService.selectOrderById(dto.getOrderId());
ServiceUtil.assertion(!orderValidator.canChangeDevice(order, getUserId()), "您无权操作ID为%s的订单换车", order.getId()); ServiceUtil.assertion(!orderValidator.canChangeDevice(order, getUserId()), "您无权操作ID为%s的订单换车", order.getId());
ServiceUtil.assertion(orderValidator.isTimeout(order), "预存款已完用,请还车后再扫码骑行,或联系客服人员处理!");
dto.setUserId(getUserId()); dto.setUserId(getUserId());
dto.setUserName(getNickName()); dto.setUserName(getNickName());
return toAjax(orderService.changeDevice(dto)); return toAjax(orderService.changeDevice(dto));
@ -226,7 +223,7 @@ public class AppOrderController extends BaseController {
// 设置日志参数 // 设置日志参数
LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon()); LogParamHolder.set(LogParamHolder.PARAM_LON, dto.getLon());
LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat()); LogParamHolder.set(LogParamHolder.PARAM_LAT, dto.getLat());
OrderVO order = orderService.selectOrderById(dto.getOrderId()); OrderVO order = orderService.selectOrderById(dto.getOrderId());
ServiceUtil.assertion(!orderValidator.isUser(order, getUserId()), "您无权操作ID为%s的订单打开坐垫锁", order.getId()); ServiceUtil.assertion(!orderValidator.isUser(order, getUserId()), "您无权操作ID为%s的订单打开坐垫锁", order.getId());
if (dto.getRequiredIot() == null) { if (dto.getRequiredIot() == null) {