修改套餐、订单,待测试

This commit is contained in:
墨大叔 2024-10-09 18:03:01 +08:00
parent 518a48ac97
commit 1ae280ad0d
12 changed files with 197 additions and 120 deletions

View File

@ -142,7 +142,7 @@ public class PayBillConverterImpl implements PayBillConverter {
PayBill po = new PayBill();
po.setBstType(PayBillBstType.RECHARGE_ORDER_DEPOSIT.getType());
po.setBstId(order.getBillId());
po.setAmount(order.getSuitPrice());
po.setAmount(order.getSuitDeposit());
po.setChannelId(channel.getChannelId());
po.setStatus(PayBillStatus.WAIT_PAY.getStatus());
po.setDescription("充值订单押金:" + order.getBillNo());

View File

@ -61,6 +61,7 @@ public class Suit extends BaseEntity
@Excel(name = "押金", readConverterExp = "元=")
@ApiModelProperty("押金")
@Min(value = 0, message = "押金不允许小于0元")
@JsonView(DeviceView.SuitList.class)
private BigDecimal deposit;
/** 详细说明 */

View File

@ -37,6 +37,7 @@ public class SuitBO extends Suit {
bo.setLowPower(getLowPower());
bo.setEnabledVoice(getEnabledVoice());
bo.setVoiceMinutes(getVoiceMinutes());
bo.setDeposit(getDeposit());
return bo;
}
@ -61,6 +62,7 @@ public class SuitBO extends Suit {
bo.setLowPower(getLowPower());
bo.setEnabledVoice(getEnabledVoice());
bo.setVoiceMinutes(getVoiceMinutes());
bo.setDeposit(getDeposit());
return bo;
}
}

View File

@ -2,6 +2,7 @@ package com.ruoyi.ss.suit.service.impl;
import com.ruoyi.common.core.domain.BaseValidator;
import com.ruoyi.common.core.domain.ValidateResult;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
@ -148,45 +149,6 @@ public class SuitValidatorImpl extends BaseValidator implements SuitValidator {
}
SuitService suitService = SpringUtils.getBean(SuitService.class);
SuitVO old = suitService.selectSuitBySuitId(suit.getSuitId());
boolean create = old == null; // 是否新建
// 创建或者修改其中一个时
if (create || suit.getFeeType() != null || suit.getFeeMode() != null) {
String feeMode = suit.getFeeMode() == null && old != null ? old.getFeeMode() : suit.getFeeMode();
String feeType = suit.getFeeType() == null && old != null ? old.getFeeType() : suit.getFeeType();
List<BigDecimal> gearAmount = suit.getGearAmount() == null && old != null ? old.getGearAmount() : suit.getGearAmount();
List<Integer> gearTime = suit.getGearTime() == null && old != null ? old.getGearTime() : suit.getGearTime();
Integer value = suit.getValue() == null && old != null ? old.getValue() : suit.getValue();
String timeUnit = suit.getTimeUnit() == null && old != null ? old.getTimeUnit() : suit.getTimeUnit();
// 判断收费方式是否被允许
if (SuitFeeMode.SINGLE.getMode().equals(feeMode) && !SuitFeeType.allowSingle().contains(feeType)) {
return error("该收费模式下,收费方式不被允许");
}
// 判断分时电价是否设置
if (SuitFeeType.timingList().contains(feeType) && (CollectionUtils.isEmptyElement(gearAmount) || CollectionUtils.isEmptyElement(gearTime))) {
return error("需要配置分时电价");
}
// 判断单次购买时长/度是否设置
if (SuitFeeType.singleList().contains(feeType) && value == null) {
return error("需要配置单次购买时长/度");
}
// 判断时长单位是否设置
if (SuitFeeType.needConfigTimeUnit().contains(feeType) && timeUnit == null) {
return error("需要配置时长单位");
}
}
if (create || suit.getPrice() != null) {
BigDecimal minService = sysConfigService.getBigDecimal(ConfigKey.RECHARGE_MIN_SERVICE);
if (suit.getPrice().compareTo(minService) < 0) {
return error("价格/押金不允许低于" + minService + "");
}
}
// 判断设备是否支持电量
if (SuitFeeType.rechargeCountList().contains(suit.getFeeType()) && CollectionUtils.isNotEmptyElement(suit.getDeviceIds())) {
@ -207,8 +169,64 @@ public class SuitValidatorImpl extends BaseValidator implements SuitValidator {
@Override
public void afterCheck(SuitVO vo, SuitVO old) {
if (vo == null){
return;
}
ServiceUtil.assertion (vo.getEnabledLowPowerClose() != null && vo.getEnabledLowPowerClose() && vo.getLowPower() == null, "最低功率阈值不允许为空");
String feeMode = vo.getFeeMode();
String feeType = vo.getFeeType();
List<BigDecimal> gearAmount = vo.getGearAmount();
List<Integer> gearTime = vo.getGearTime();
Integer value = vo.getValue() ;
String timeUnit = vo.getTimeUnit();
// 判断收费方式是否被允许
if (SuitFeeMode.SINGLE.getMode().equals(feeMode) && !SuitFeeType.allowSingle().contains(feeType)) {
throw new ServiceException("该收费模式下,收费方式不被允许");
}
// 判断分时电价是否设置
if (SuitFeeType.timingList().contains(feeType) && (CollectionUtils.isEmptyElement(gearAmount) || CollectionUtils.isEmptyElement(gearTime))) {
throw new ServiceException("需要配置分时电价");
}
// 判断单次购买时长/度是否设置
if (SuitFeeType.singleList().contains(feeType) && value == null) {
throw new ServiceException("需要配置单次购买时长/度");
}
// 判断时长单位是否设置
if (SuitFeeType.needConfigTimeUnit().contains(feeType) && timeUnit == null) {
throw new ServiceException("需要配置时长单位");
}
// 若开启最低功率阈值则需设置最低功率
if (SuitFeeType.rechargeCountList().contains(feeType)) {
ServiceUtil.assertion (vo.getEnabledLowPowerClose() != null && vo.getEnabledLowPowerClose() && vo.getLowPower() == null, "最低功率阈值不允许为空");
}
// 若开启语音播报则需要设置播报时机
if (SuitFeeType.TIME.getType().equals(feeType)) {
ServiceUtil.assertion(vo.getEnabledVoice() != null && vo.getEnabledVoice() && vo.getVoiceMinutes() == null, "语音播报时机不允许为空");
}
// 一次性充值的套餐价格不允许低于最低服务费
if (SuitFeeType.singleList().contains(feeType)) {
ServiceUtil.assertion(vo.getPrice() == null, "价格不允许为空");
BigDecimal minService = sysConfigService.getBigDecimal(ConfigKey.RECHARGE_MIN_SERVICE);
if (vo.getPrice().compareTo(minService) < 0) {
throw new ServiceException("价格不允许低于" + minService + "");
}
// 若为智能计时智能按量则押金不允许低于价格且必须为价格的整数倍
if (SuitFeeMode.SMART.getMode().equals(feeMode)) {
ServiceUtil.assertion(vo.getDeposit() == null, "押金不允许为空");
ServiceUtil.assertion(vo.getDeposit().compareTo(vo.getPrice()) < 0, "押金不允许低于价格");
// 取模判断余数
BigDecimal remainder = vo.getDeposit().remainder(vo.getPrice());
ServiceUtil.assertion(remainder.compareTo(BigDecimal.ZERO) != 0, "押金必须为价格的整数倍");
}
}
}
}

View File

@ -306,6 +306,10 @@ public class TransactionBill extends BaseEntity implements Payable
@ApiModelProperty("套餐语音播报设置结果描述")
private String suitVoidMsg;
@Excel(name = "套餐押金", readConverterExp = "元=")
@ApiModelProperty("套餐押金")
private BigDecimal suitDeposit;
/**
* 获取价格
*/

View File

@ -55,11 +55,16 @@ public class RechargeDTO {
@DictValid(type = DictTypeConstants.SUIT_FEE_TYPE, message = "非法的套餐收费类型")
private String suitFeeType;
@ApiModelProperty("套餐金额/押金")
@NotNull(message = "套餐金额/押金不允许为空")
@Min(value = 0, message = "套餐金额/押金不允许小于0")
@ApiModelProperty("套餐金额")
@NotNull(message = "套餐金额不允许为空")
@Min(value = 0, message = "套餐金额不允许小于0")
private BigDecimal suitPrice;
@ApiModelProperty("套餐押金")
@NotNull(message = "套餐押金不允许为空")
@Min(value = 0, message = "套餐押金不允许小于0")
private BigDecimal suitDeposit;
@ApiModelProperty("套餐档位金额列表")
@AllowSize(sizes = {0, 4}, message = "套餐档位金额列表大小仅允许为0或者4")
private List<BigDecimal> suitGearAmount;

View File

@ -5,6 +5,8 @@ import com.ruoyi.common.core.domain.JsonViewProfile;
import com.ruoyi.ss.bonus.domain.Bonus;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.iot.interfaces.IotDevice;
import com.ruoyi.ss.suit.domain.enums.SuitFeeMode;
import com.ruoyi.ss.suit.domain.enums.SuitFeeType;
import com.ruoyi.ss.suit.domain.enums.SuitTimeUnit;
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
import io.swagger.annotations.ApiModel;
@ -12,6 +14,7 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List;
/**
@ -72,7 +75,15 @@ public class TransactionBillVO extends TransactionBill implements IotDevice {
unit = SuitTimeUnit.MINUTE;
}
long time = this.getSuitTime() == null ? 0 : this.getSuitTime();
return time * unit.getConversion();
// 计算周期数
int round = 1;
// 若为智能模式则计算出押金最大支持多少次向上取整
if (SuitFeeMode.SMART.getMode().equals(this.getSuitFeeMode())) {
round = this.getSuitDeposit().divide(this.getSuitPrice(), 0, RoundingMode.UP).intValue();
}
return time * unit.getConversion() * round;
}
@Override

View File

@ -76,6 +76,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
stb.suit_void_minute,
stb.suit_void_result,
stb.suit_void_msg,
stb.suit_deposit,
</sql>
<sql id="selectSmTransactionBillVo">
@ -480,6 +481,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="suitVoidMinute != null">suit_void_minute,</if>
<if test="suitVoidResult != null">suit_void_result,</if>
<if test="suitVoidMsg != null">suit_void_msg,</if>
<if test="suitDeposit != null">suit_deposit,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="billNo != null">#{billNo},</if>
@ -544,6 +546,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="suitVoidMinute != null">#{suitVoidMinute},</if>
<if test="suitVoidResult != null">#{suitVoidResult},</if>
<if test="suitVoidMsg != null">#{suitVoidMsg},</if>
<if test="suitDeposit != null">#{suitDeposit},</if>
</trim>
</insert>
@ -626,6 +629,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.suitVoidMinute != null">stb.suit_void_minute = #{data.suitVoidMinute},</if>
<if test="data.suitVoidResult != null">stb.suit_void_result = #{data.suitVoidResult},</if>
<if test="data.suitVoidMsg != null">stb.suit_void_msg = #{data.suitVoidMsg},</if>
<if test="data.suitDeposit != null">suit_deposit = #{data.suitDeposit},</if>
</sql>
<update id="updateByQuery">

View File

@ -192,6 +192,18 @@ public interface TransactionBillService
*/
int updateByQuery(TransactionBill data, TransactionBillQuery query);
/**
* 尝试充值设备
* @param billId 订单ID
* @param tryCount 尝试次数
*/
void tryRechargeDevice(Long billId, int tryCount);
/**
* 尝试设置语音
*/
void trySetVoice(TransactionBillVO bill, int tryCount);
/**
* 刷新支付结果
*/
@ -249,11 +261,6 @@ public interface TransactionBillService
*/
int selectSimpleCount(TransactionBillQuery query);
/**
* 充值成功后处理
*/
int rechargeSuccess(PaySuccessBO bo);
/**
* 支付押金
*/

View File

@ -2,6 +2,8 @@ package com.ruoyi.ss.transactionBill.service.impl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.ss.bonus.domain.BonusVO;
import com.ruoyi.ss.bonus.domain.enums.BonusArrivalType;
import com.ruoyi.ss.bonus.service.BonusService;
import com.ruoyi.ss.payBill.domain.PayBillVO;
import com.ruoyi.ss.payBill.domain.bo.RefundSuccessBO;
@ -14,6 +16,7 @@ import com.ruoyi.ss.transactionBill.domain.TransactionBill;
import com.ruoyi.ss.transactionBill.domain.TransactionBillQuery;
import com.ruoyi.ss.transactionBill.domain.bo.PaySuccessBO;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.transactionBill.service.TransactionAssembler;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
@ -23,6 +26,9 @@ import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionTemplate;
import java.math.BigDecimal;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* 充值成功处理类
@ -43,7 +49,7 @@ public class RechargePayHandler implements AfterPay, AfterRefund {
private TransactionTemplate transactionTemplate;
@Autowired
private ISmUserService userService;
private ScheduledExecutorService scheduledExecutorService;
@Autowired
private BonusService bonusService;
@ -65,12 +71,66 @@ public class RechargePayHandler implements AfterPay, AfterRefund {
transactionAssembler.assembleBonusList(bill);
if (SuitFeeType.singleList().contains(bill.getSuitFeeType())) {
return transactionBillService.rechargeSuccess(bo);
return this.handleSinglePaySuccess(bo);
} else {
return this.handleTimingPaySuccess(bo);
}
}
/**
* 处理单次订单支付成功
*/
private int handleSinglePaySuccess(PaySuccessBO bo) {
TransactionBillVO bill = bo.getOrder();
PayBillVO pay = bo.getPayBill();
ServiceUtil.assertion(bill == null || !TransactionBillType.RECHARGE.getType().equals(bill.getType()), "充值订单不存在");
ServiceUtil.assertion(!TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus()), "当前订单状态非未支付");
List<BonusVO> bonusList = bill.getBonusList();
Integer result = transactionTemplate.execute(status -> {
// 处理分成将金额分好
bonusService.partBonus(bonusList, bill.getMoney());
BonusVO platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null);
BonusVO mch = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.MCH.getType())).findFirst().orElse(null);
// 修改订单信息
TransactionBill data = new TransactionBill();
data.setStatus(TransactionBillStatus.SUCCESS.getStatus());
data.setPayTime(DateUtils.toDate(pay.getPayTime()));
data.setChannelId(pay.getChannelId());
data.setChannelCost(pay.getChannelCost());
data.setPayId(pay.getPayId());
if (platform != null) {
data.setServiceCharge(platform.getAmount());
}
if (mch != null) {
data.setArrivalAmount(mch.getAmount());
}
TransactionBillQuery query = new TransactionBillQuery();
query.setBillId(bill.getBillId());
query.setStatus(TransactionBillStatus.UNPAID.getStatus());
query.setType(TransactionBillType.RECHARGE.getType());
int updateCount = transactionBillService.updateByQuery(data, query);
ServiceUtil.assertion(updateCount != 1, "订单状态已发生变化,请刷新后重试");
return updateCount;
});
// 操作成功
if (result != null && result == 1) {
// 充值设备尝试1次
transactionBillService.tryRechargeDevice(bill.getBillId(), 1);
// 设备设置语音播报
scheduledExecutorService.schedule(() -> {
transactionBillService.trySetVoice(bill, 3);
}, 0, TimeUnit.SECONDS);
}
return result == null ? 0 : result;
}
/**
* 处理分时段订单支付成功
*/

View File

@ -425,12 +425,20 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
order.setCloseResult("预计成功");
if (SuitFeeType.timingList().contains(suit.getFeeType())) {
// 当为分时段收费则收取押金
order.setSuitPrice(dto.getSuitPrice());
// 当为分时段收费状态为未支付押金
order.setSuitPrice(dto.getSuitDeposit());
order.setSuitDeposit(dto.getSuitDeposit());
order.setStatus(TransactionBillStatus.UNPAID_DEPOSIT.getStatus()); // 未支付押金
} else {
// 否则收取金额
order.setMoney(dto.getMoney());
// 否则为未支付
if (SuitFeeMode.SMART.getMode().equals(suit.getFeeMode())) {
// 智能收费收取押金
order.setMoney(dto.getSuitDeposit());
order.setSuitDeposit(dto.getSuitDeposit());
} else {
// 单次收费收取本金
order.setMoney(dto.getMoney());
}
order.setStatus(TransactionBillStatus.UNPAID.getStatus()); // 状态未支付
}
@ -930,64 +938,11 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
return transactionBillMapper.updateSmTransactionBill(data);
}
@Override
public int rechargeSuccess(PaySuccessBO bo) {
TransactionBillVO bill = bo.getOrder();
PayBillVO pay = bo.getPayBill();
ServiceUtil.assertion(bill == null || !TransactionBillType.RECHARGE.getType().equals(bill.getType()), "充值订单不存在");
ServiceUtil.assertion(!TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus()), "当前订单状态非未支付");
List<BonusVO> bonusList = bill.getBonusList();
Integer result = transactionTemplate.execute(status -> {
// 处理分成将金额分好
bonusService.partBonus(bonusList, bill.getMoney());
BonusVO platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null);
BonusVO mch = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.MCH.getType())).findFirst().orElse(null);
// 修改订单信息
TransactionBill data = new TransactionBill();
data.setStatus(TransactionBillStatus.SUCCESS.getStatus());
data.setPayTime(DateUtils.toDate(pay.getPayTime()));
data.setChannelId(pay.getChannelId());
data.setChannelCost(pay.getChannelCost());
data.setPayId(pay.getPayId());
if (platform != null) {
data.setServiceCharge(platform.getAmount());
}
if (mch != null) {
data.setArrivalAmount(mch.getAmount());
}
TransactionBillQuery query = new TransactionBillQuery();
query.setBillId(bill.getBillId());
query.setStatus(TransactionBillStatus.UNPAID.getStatus());
query.setType(TransactionBillType.RECHARGE.getType());
int updateCount = this.updateByQuery(data, query);
ServiceUtil.assertion(updateCount != 1, "订单状态已发生变化,请刷新后重试");
return updateCount;
});
// 操作成功
if (result != null && result == 1) {
// 充值设备尝试1次
this.tryRechargeDevice(bill.getBillId(), 1);
// 设备设置语音播报
scheduledExecutorService.schedule(() -> {
this.trySetVoice(bill, 3);
}, 0, TimeUnit.SECONDS);
}
return result == null ? 0 : result;
}
/**
* 尝试设置语音播报
*/
private void trySetVoice(TransactionBillVO bill, int tryCount) {
@Override
public void trySetVoice(TransactionBillVO bill, int tryCount) {
if (tryCount <= 0) {
return;
}
@ -1033,7 +988,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
* @param billId
* @param tryCount
*/
private void tryRechargeDevice(Long billId, int tryCount) {
@Override
public void tryRechargeDevice(Long billId, int tryCount) {
if (tryCount <= 0) {
return;
}
@ -1374,12 +1330,15 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
BigDecimal startEle = order.getSuitStartEle();
BigDecimal usedEle = totalEle.subtract(startEle); // 已使用的电量
BigDecimal suitTotalEle = BigDecimal.valueOf(order.getSuitTime()); // 套餐总电量
BigDecimal noUsedEle = suitTotalEle.subtract(usedEle); // 未使用的电量
// 退款金额 = 订单金额 * 未使用的电量 / 套餐总电量
// 计算总使用的周期数不满足一个周期也算一个周期
BigDecimal totalRound = usedEle.divide(suitTotalEle, 0, RoundingMode.UP);
// 退款金额 = 订单金额 - 使用周期数 * 单价
return order.getMoney()
.multiply(noUsedEle)
.divide(suitTotalEle, 2, RoundingMode.HALF_UP);
.subtract(
totalRound.multiply(order.getSuitPrice())
);
}
// 计算智能时长收费需要退款的金额
@ -1391,12 +1350,15 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
ServiceUtil.assertion(unit == null, "订单套餐单位无效");
long totalSeconds = unit.getConversion() * order.getSuitTime(); // 总时长
long noUseSeconds = totalSeconds - between.getSeconds(); // 未使用的时长
// 退款金额 = 订单金额 * 未使用的时长 / 总时长
// 计算总使用的周期数不满足一个周期也算一个周期
long totalRound = totalSeconds / order.getSuitTime() + (totalSeconds % order.getSuitTime() != 0 ? 1 : 0);
// 退款金额 = 订单金额 - 使用周期数 * 单价
return order.getMoney()
.multiply(BigDecimal.valueOf(noUseSeconds))
.divide(BigDecimal.valueOf(totalSeconds), 2, RoundingMode.HALF_UP);
.subtract(
BigDecimal.valueOf(totalRound).multiply(order.getSuitPrice())
);
}
// 计算分时段计量金额

View File

@ -127,6 +127,9 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
if (suit.getPrice().compareTo(dto.getSuitPrice()) != 0) {
return error("当前套餐价格已发生变化,请重新下单");
}
if (suit.getDeposit().compareTo(dto.getSuitDeposit()) != 0) {
return error("当前套餐押金已发生变化,请重新下单");
}
if (!suit.getFeeMode().equals(dto.getSuitFeeMode())) {
return error("当前套餐收费模式发生变化,请重新下单");
}