diff --git a/smart-switch-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java index db182b55..1cc0b56c 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/iot/service/impl/IotServiceImpl.java @@ -63,15 +63,9 @@ public class IotServiceImpl implements IotService { @Value(value = "${sm.daysToExpire}") private Long daysToExpire; - @Value(value = "${debug}") - private Boolean debug; - @Autowired private IotConverter iotConverter; - @Autowired - private ScheduledExecutorService scheduledExecutorService; - @Autowired private ICommandLogService commandLogService; @@ -100,7 +94,7 @@ public class IotServiceImpl implements IotService { if (StringUtils.isBlank(status)) { // log.info("进入command"); // 发送命令 - CommandResponse res = uploadData(deviceName, productId, "获取在线状态", false); + CommandResponse res = uploadData(deviceName, productId, "获取在线状态"); // 若是离线,则直接返回离线 if (res != null && res.isNotOnline()) { @@ -280,19 +274,19 @@ public class IotServiceImpl implements IotService { ServiceUtil.assertion(device == null || StringUtils.isBlank(device.getProductId()), "设备为空"); CommandResponse res = null; if (StringUtils.hasText(device.iotMac2())) { - res = this.uploadData(device.iotMac2(), device.getProductId(), reason, recordLog); + res = this.uploadData(device.iotMac2(), device.getProductId(), reason); } if ((res == null || !res.isSuccess()) && StringUtils.hasText(device.iotMac1())) { - res = this.uploadData(device.iotMac1(), device.getProductId(), reason, recordLog); + res = this.uploadData(device.iotMac1(), device.getProductId(), reason); } return res; } - private CommandResponse uploadData(String deviceName, String productId, String reason, boolean recordLog) { + private CommandResponse uploadData(String deviceName, String productId, String reason) { if (StringUtils.isBlank(deviceName) || StringUtils.isBlank(productId)) { return null; } - return sendCommand(deviceName, IotConstants.COMMAND_UPLOAD_DATA, productId, reason, recordLog); + return sendCommand(deviceName, IotConstants.COMMAND_UPLOAD_DATA, productId, reason); } private CommandResponse setVxs(String deviceName, BigDecimal vxs, String productId, String reason) { @@ -397,7 +391,7 @@ public class IotServiceImpl implements IotService { if (seconds < 0) { throw new ServiceException("设置剩余时长参数错误:读数不允许小于0"); } - return sendCommand(deviceName, IotConstants.COMMAND_RECHARGE + seconds + IotConstants.COMMAND_SEPARATOR, 5, productId, reason, true); + return sendCommand(deviceName, IotConstants.COMMAND_RECHARGE + seconds + IotConstants.COMMAND_SEPARATOR, 5, productId, reason); } @Override @@ -423,7 +417,7 @@ public class IotServiceImpl implements IotService { // throw new ServiceException("充值电量错误:充值电量不允许小于0"); return null; } - return sendCommand(deviceName, IotConstants.COMMAND_ADD_ELE + ele.multiply(BigDecimal.valueOf(1000)) + IotConstants.COMMAND_SEPARATOR, 5, productId, reason, true); + return sendCommand(deviceName, IotConstants.COMMAND_ADD_ELE + ele.multiply(BigDecimal.valueOf(1000)) + IotConstants.COMMAND_SEPARATOR, 5, productId, reason); } @Override @@ -449,7 +443,7 @@ public class IotServiceImpl implements IotService { // throw new ServiceException("设置电量错误:电量不允许为空"); return null; } - return sendCommand(deviceName, IotConstants.COMMAND_SET_ELE + ele.multiply(BigDecimal.valueOf(1000)) + IotConstants.COMMAND_SEPARATOR, 5, productId, reason, true); + return sendCommand(deviceName, IotConstants.COMMAND_SET_ELE + ele.multiply(BigDecimal.valueOf(1000)) + IotConstants.COMMAND_SEPARATOR, 5, productId, reason); } @Override @@ -537,16 +531,11 @@ public class IotServiceImpl implements IotService { } private CommandResponse sendCommand(String deviceName, String command, String productId, String reason) { - return sendCommand(deviceName, command, null, productId, reason, true); - } - - - private CommandResponse sendCommand(String deviceName, String command, String productId, String reason, boolean recordLog) { - return sendCommand(deviceName, command, null, productId, reason, recordLog); + return sendCommand(deviceName, command, null, productId, reason); } // 发送MQTT命令 - private CommandResponse sendCommand(String deviceName, String command, Integer timeout, String productId, String reason, boolean recordLog) { + private CommandResponse sendCommand(String deviceName, String command, Integer timeout, String productId, String reason) { if (timeout == null) { timeout = this.timeout; } @@ -561,9 +550,7 @@ public class IotServiceImpl implements IotService { CommandResponse res = JSON.parseObject(result, CommandResponse.class); // 记录成功日志 - if (recordLog) { - this.addCommandLog(deviceName, command, res.getMsg(), reason, res.getCode()); - } + this.addCommandLog(deviceName, command, res.getMsg(), reason, res.getCode()); // 若返回数据为离线或者超时,则判断设备是否在线 // if (IotHttpStatus.checkOnlineList().contains(res.getCode())) { diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusConverterImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusConverterImpl.java index 2b3ac67d..5c35cb36 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusConverterImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusConverterImpl.java @@ -18,6 +18,7 @@ import com.ruoyi.ss.storeStaff.domain.StoreStaffVO; import com.ruoyi.ss.storeStaff.service.StoreStaffService; import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO; import com.ruoyi.ss.user.domain.SmUserVO; +import com.ruoyi.ss.user.service.UserAssembler; import com.ruoyi.ss.user.service.UserService; import com.ruoyi.ss.vipOrder.domain.VipOrderVO; import com.ruoyi.system.service.ISysDeptService; @@ -55,6 +56,9 @@ public class BonusConverterImpl implements BonusConverter { @Autowired private UserService userService; + @Autowired + private UserAssembler userAssembler; + @Override public List toPoList(SysDept platform, DeviceVO device, List staffList) { @@ -140,6 +144,7 @@ public class BonusConverterImpl implements BonusConverter { return Collections.emptyList(); } SmUserVO mch = userService.selectSmUserByUserId(order.getMchId()); + userAssembler.assembleRealServiceRate(Collections.singletonList(mch)); SysDept platform = deptService.selectDeptById(Constants.ROOT_DEPT); if (mch == null || platform == null) { return Collections.emptyList(); @@ -152,8 +157,9 @@ public class BonusConverterImpl implements BonusConverter { // 是否平台收取 boolean isPlatform = ChannelType.PLATFORM.getType().equals(channelType); // 平台收取 - if (isPlatform && mch.getServiceRate() != null) { - this.addBonus(result, toPo(platform, mch.getRealServiceRate())); + if (isPlatform) { + BigDecimal serviceRate = mch.getRealServiceRate() == null ? BigDecimal.ZERO : mch.getRealServiceRate(); + result.add(toPo(platform, serviceRate)); } // 员工收取 diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusServiceImpl.java index 4d5f7c0e..4df49c1f 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/service/impl/BonusServiceImpl.java @@ -22,6 +22,7 @@ import com.ruoyi.ss.bonus.domain.vo.BonusMonthAmountVO; import com.ruoyi.ss.bonus.domain.vo.ProvideBonusVO; import com.ruoyi.ss.bonus.mapper.BonusMapper; import com.ruoyi.ss.bonus.service.BonusService; +import com.ruoyi.ss.bonus.utils.BonusUtil; import com.ruoyi.ss.recordBalance.domain.enums.RecordBalanceBstType; import com.ruoyi.ss.user.domain.SmUserVO; import com.ruoyi.ss.user.service.UserAssembler; @@ -199,8 +200,42 @@ public class BonusServiceImpl implements BonusService } BigDecimal decimal100 = new BigDecimal(100); - BigDecimal dividedAmount = BigDecimal.ZERO; // 已分配金额 + // 获取平台的分成 + Bonus platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null); + // 平台存在分成的情况,需要处理最低分成 + if (platform != null) { + BigDecimal minService = sysConfigService.getBigDecimal(ConfigKey.RECHARGE_MIN_SERVICE); // 平台最低需要的分成金额 + BigDecimal platformAmount = money.multiply(platform.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP); // 平台预计分成金额 + // 若总金额 < 最低分成,则平台全收 + if (money.compareTo(minService) < 0) { + BonusUtil.partBonusAllPlatform(platform, money); + } + // 若平台分成 < 最低金额,则收取最低金额,其他金额给其他分成方分成 + else if (platformAmount.compareTo(minService) < 0) { + BonusUtil.partBonusMinService(bonusList, money, platform, minService); + } + // 其余正常收取 + else { + BonusUtil.partBonusNormal(bonusList, money); + } + } + // 其余情况,正常收取费用 + else { + BonusUtil.partBonusNormal(bonusList, money); + } + + // 误差处理,将误差值交给可以处理的分成方处理 + BigDecimal dividedAmount = CollectionUtils.sumDecimal(bonusList, Bonus::getAmount); + if (dividedAmount.compareTo(money) != 0) { + BigDecimal diff = money.subtract(dividedAmount); + BonusUtil.handlePartDiff(bonusList, diff); + } + dividedAmount = CollectionUtils.sumDecimal(bonusList, Bonus::getAmount); + ServiceUtil.assertion(dividedAmount.compareTo(money) != 0, "分成金额分配出错"); + + // 设置预计分成时间等数据 + LocalDateTime now = LocalDateTime.now(); // 获取用户列表 List userList = userService.selectByUserIds(bonusList.stream() .filter(item -> BonusArrivalType.userList().contains(item.getArrivalType())) @@ -209,60 +244,6 @@ public class BonusServiceImpl implements BonusService ); // 拼接用户实际到账延迟 userAssembler.assembleRealArrivalDelay(userList); - - LocalDateTime now = LocalDateTime.now(); - - // 获取平台的分成 - Bonus platform = bonusList.stream().filter(bonus -> bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())).findFirst().orElse(null); - ServiceUtil.assertion(platform == null, "平台不存在"); - BigDecimal platformAmount = money.multiply(platform.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP); // 平台预计分成金额 - - // 处理最低分成 - BigDecimal minService = sysConfigService.getBigDecimal(ConfigKey.RECHARGE_MIN_SERVICE); - // 若总金额 < 最低分成,则平台全收 - if (money.compareTo(minService) < 0) { - this.setAmount(platform, money); - dividedAmount = platform.getAmount(); - } - // 若平台分成 < 最低金额,则收取最低金额,其他金额给其他分成方分成 - else if (platformAmount.compareTo(minService) < 0) { - // 平台设置分成为最低金额 - this.setAmount(platform, minService); - dividedAmount = platform.getAmount(); - - // 计算其他分成方分成金额 - BigDecimal remain = money.subtract(platform.getAmount()); // 剩余可分成金额 - BigDecimal remainPoint = decimal100.subtract(platform.getPoint()); // 剩余总百分比 - for (Bonus bonus : bonusList) { - if (bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())) { - continue; - } - // 计算分成比例占剩余可分成金额的比例 - BigDecimal bonusPoint = bonus.getPoint().multiply(decimal100).divide(remainPoint, 2, RoundingMode.HALF_UP); - BigDecimal amount = remain.multiply(bonusPoint).divide(decimal100, 2, RoundingMode.HALF_UP); - this.setAmount(bonus, amount); - // 增加已分成金额 - dividedAmount = dividedAmount.add(amount); - } - } - // 其余情况,正常收取费用 - else { - // 循环遍历,构造分成金额 - for (Bonus bonus : bonusList) { - BigDecimal amount = money.multiply(bonus.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP); - this.setAmount(bonus, amount); - // 增加已分成金额 - dividedAmount = dividedAmount.add(amount); - } - } - - // 若存在误差,则平台吃掉误差 - if (dividedAmount.compareTo(money) != 0) { - BigDecimal subtract = money.subtract(dividedAmount); // 误差值 - this.setAmount(platform, platform.getAmount().add(subtract)); - } - - // 设置预计分成时间等数据 for (Bonus bonus : bonusList) { bonus.setStatus(BonusStatus.WAIT_DIVIDE.getStatus()); bonus.setWaitAmount(bonus.getAmount()); @@ -279,10 +260,6 @@ public class BonusServiceImpl implements BonusService } } - private void setAmount(Bonus bonus, BigDecimal amount) { - ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "分成金额不允许小于0"); - bonus.setAmount(amount); - } @Override public List> selectCountByArrival(BonusQuery query) { diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/utils/BonusUtil.java b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/utils/BonusUtil.java new file mode 100644 index 00000000..c577c198 --- /dev/null +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/bonus/utils/BonusUtil.java @@ -0,0 +1,166 @@ +package com.ruoyi.ss.bonus.utils; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +import com.ruoyi.common.exception.ServiceException; +import com.ruoyi.common.utils.ServiceUtil; +import com.ruoyi.ss.bonus.domain.Bonus; +import com.ruoyi.ss.bonus.domain.BonusVO; +import com.ruoyi.ss.bonus.domain.enums.BonusArrivalType; + +/** + * @author wjh + * 2025/1/21 + */ +public class BonusUtil { + + /** + * 平台全收服务费分成处理 + */ + public static void partBonusAllPlatform(Bonus platform, BigDecimal money) { + setAmount(platform, money); + } + + /** + * 最低服务费分成处理 + */ + public static void partBonusMinService(List bonusList, BigDecimal money, Bonus platform, BigDecimal minService) { + // 平台设置分成为最低金额 + setAmount(platform, minService); + BigDecimal decimal100 = new BigDecimal("100"); + // 计算其他分成方分成金额 + BigDecimal remain = money.subtract(platform.getAmount()); // 剩余可分成金额 + BigDecimal remainPoint = decimal100.subtract(platform.getPoint()); // 剩余总百分比 + for (Bonus bonus : bonusList) { + if (bonus.getArrivalType().equals(BonusArrivalType.PLATFORM.getType())) { + continue; + } + // 计算分成比例占剩余可分成金额的比例 + BigDecimal bonusPoint = bonus.getPoint().multiply(decimal100).divide(remainPoint, 2, RoundingMode.HALF_UP); + BigDecimal amount = remain.multiply(bonusPoint).divide(decimal100, 2, RoundingMode.HALF_UP); + setAmount(bonus, amount); + } + } + + /** + * 基础处理分成 + */ + public static void partBonusNormal(List bonusList, BigDecimal money) { + BigDecimal decimal100 = new BigDecimal("100"); + // 循环遍历,构造分成金额 + for (Bonus bonus : bonusList) { + BigDecimal amount = money.multiply(bonus.getPoint()).divide(decimal100, 2, RoundingMode.HALF_UP); + setAmount(bonus, amount); + } + } + + + private static void setAmount(Bonus bonus, BigDecimal amount) { + ServiceUtil.assertion(amount.compareTo(BigDecimal.ZERO) < 0, "分成金额不允许小于0"); + bonus.setAmount(amount); + } + + /** + * 处理分成的误差 + */ + public static void handlePartDiff(List bonusList, BigDecimal diff) { + // 若误差金额为正数或者0,则交给第一个分成方处理 + if (diff.compareTo(BigDecimal.ZERO) >= 0) { + Bonus bonus = bonusList.get(0); + setAmount(bonus, bonus.getAmount().add(diff)); + } + // 否则,若为负数,则多个分成方处理 + else { + // 多个分成方共同承担误差 + BigDecimal remainingDiff = diff; + for (Bonus bonus : bonusList) { + if (bonus == null || bonus.getAmount().compareTo(BigDecimal.ZERO) <= 0) { + continue; + } + + // 计算当前分成方可以承担的误差金额(确保不会导致金额为负) + BigDecimal maxDeductible = bonus.getAmount(); // 最大可扣除金额 + BigDecimal adjustAmount = maxDeductible.min(remainingDiff.abs()); + + // 直接调整金额 + setAmount(bonus, bonus.getAmount().subtract(adjustAmount)); + remainingDiff = remainingDiff.add(adjustAmount); + + // 如果误差已经分配完毕,则退出 + if (remainingDiff.compareTo(BigDecimal.ZERO) == 0) { + return; + } + } + + // 如果所有分成方都处理完了还有剩余误差,抛出异常 + if (remainingDiff.compareTo(BigDecimal.ZERO) != 0) { + throw new ServiceException("分成金额误差处理失败:无法完全分配误差金额,剩余误差:" + remainingDiff); + } + } + } + + + /** + * 处理退款金额分配的误差 + * @param refundList 退款列表 + * @param bonusList 原始分成列表 + * @param dividedAmount 已分配金额 + * @param refundAmount 需要退款总金额 + */ + public static void handleRefundAmountDiff(List refundList, List bonusList, BigDecimal dividedAmount, BigDecimal refundAmount) { + if (dividedAmount.compareTo(refundAmount) == 0) { + return; + } + // 多个分成方共同承担误差 + BigDecimal remainingDiff = refundAmount.subtract(dividedAmount); + for (Bonus refundBonus : refundList) { + // 获取原始分成记录 + BonusVO originalBonus = bonusList.stream() + .filter(b -> b.getId().equals(refundBonus.getId())) + .findFirst() + .orElse(null); + + if (originalBonus == null) { + continue; + } + + if (remainingDiff.compareTo(BigDecimal.ZERO) > 0) { + // 处理正数误差(少退了):需要增加退款金额 + // 计算当前分成方的剩余可退金额 + BigDecimal availableRefund = originalBonus.getAmount() + .subtract(originalBonus.getRefundAmount() == null ? BigDecimal.ZERO : originalBonus.getRefundAmount()) + .subtract(refundBonus.getRefundAmount()); + + // 如果剩余可退金额足够承担误差 + if (availableRefund.compareTo(BigDecimal.ZERO) > 0) { + // 取可用退款金额与剩余误差中的较小值 + BigDecimal adjustAmount = availableRefund.min(remainingDiff); + refundBonus.setRefundAmount(refundBonus.getRefundAmount().add(adjustAmount)); + remainingDiff = remainingDiff.subtract(adjustAmount); + } + } else { + // 处理负数误差(多退了):需要减少退款金额 + // 计算当前退款金额 + BigDecimal currentRefund = refundBonus.getRefundAmount(); + if (currentRefund.compareTo(BigDecimal.ZERO) > 0) { + // 取当前退款金额与剩余需要减少金额的较小值 + BigDecimal adjustAmount = currentRefund.min(remainingDiff.abs()); + refundBonus.setRefundAmount(currentRefund.subtract(adjustAmount)); + remainingDiff = remainingDiff.add(adjustAmount); + } + } + + // 如果误差已经分配完毕,则退出 + if (remainingDiff.compareTo(BigDecimal.ZERO) == 0) { + return; + } + } + + // 如果仍然无法完全分配误差,抛出异常 + if (remainingDiff.compareTo(BigDecimal.ZERO) != 0) { + throw new ServiceException("退款金额分配出错:无法处理误差金额,剩余误差:" + remainingDiff); + } + } +} diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java index f71e96ec..ce830576 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/domain/TransactionBill.java @@ -338,21 +338,26 @@ public class TransactionBill extends BaseEntity @Excel(name = "使用vip的ID") @ApiModelProperty("使用vip的ID") + @JsonView(JsonViewProfile.App.class) private Long vipId; @Excel(name = "会员折扣") @ApiModelProperty("会员折扣") + @JsonView(JsonViewProfile.App.class) private BigDecimal vipDiscount; @Excel(name = "会员名称") @ApiModelProperty("会员名称") + @JsonView(JsonViewProfile.App.class) private String vipName; @Excel(name = "优惠金额") @ApiModelProperty("优惠金额") + @JsonView(JsonViewProfile.App.class) private BigDecimal discountAmount; @Excel(name = "优惠金额退款") @ApiModelProperty("优惠金额退款") + @JsonView(JsonViewProfile.App.class) private BigDecimal discountRefundAmount; } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java index 722c7023..74922fff 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillConverterImpl.java @@ -2,8 +2,6 @@ package com.ruoyi.ss.transactionBill.service.impl; import java.util.Collections; -import com.ruoyi.ss.app.service.AppService; -import com.ruoyi.ss.transactionBill.utils.RechargeUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -14,6 +12,7 @@ import com.ruoyi.common.utils.StringUtils; import com.ruoyi.ss.account.domain.AccountQuery; import com.ruoyi.ss.account.domain.AccountVO; import com.ruoyi.ss.account.service.AccountService; +import com.ruoyi.ss.app.service.AppService; import com.ruoyi.ss.channel.service.ChannelService; import com.ruoyi.ss.channelWithdraw.domain.ChannelWithdrawVO; import com.ruoyi.ss.channelWithdraw.service.ChannelWithdrawService; @@ -36,6 +35,7 @@ import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO; import com.ruoyi.ss.transactionBill.domain.dto.WithdrawDTO; import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO; import com.ruoyi.ss.transactionBill.service.TransactionBillConverter; +import com.ruoyi.ss.transactionBill.utils.RechargeUtils; import com.ruoyi.ss.user.domain.SmUserVO; import com.ruoyi.ss.user.service.UserService; import com.ruoyi.ss.vip.service.VipService; diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java index 44e07350..8c53d305 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillServiceImpl.java @@ -18,6 +18,7 @@ import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.stream.Collectors; +import com.ruoyi.ss.bonus.utils.BonusUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -111,6 +112,7 @@ import com.ruoyi.ss.user.domain.SmUserVO; import com.ruoyi.ss.user.service.UserService; import com.ruoyi.ss.vip.domain.VipVO; import com.ruoyi.ss.vip.service.VipService; +import com.ruoyi.ss.vip.utils.VipUtil; import com.ruoyi.system.domain.enums.config.ConfigKey; import com.ruoyi.system.service.ISysConfigService; import com.ruoyi.task.bill.BillDelayedManager; @@ -196,8 +198,6 @@ public class TransactionBillServiceImpl implements TransactionBillService, After @Autowired private VipService vipService; - private VipVO vip; - /** * 查询充值记录 * @@ -301,7 +301,12 @@ public class TransactionBillServiceImpl implements TransactionBillService, After int monthFee = receiveBillService.genBillByMonthAndPay(bo.getMch(), bo.getDevice()); ServiceUtil.assertion(monthFee != 1, "月费收取失败,请联系商户处理"); - // TODO 扣减会员次数,记录会员使用记录 + // 扣减会员次数 + VipVO vip = bo.getVip(); + if (vip != null) { + int use = vipService.useVip(vip); + ServiceUtil.assertion(use != 1, "ID为%s的会员使用失败", vip.getId()); + } // 新增订单 int insert = this.insertSmTransactionBill(order); @@ -1072,20 +1077,36 @@ public class TransactionBillServiceImpl implements TransactionBillService, After totalEle = this.calcTotalEle(device, totalEle); } + BigDecimal result = BigDecimal.ZERO; + // 智能计费 if (SuitFeeMode.SMART.getMode().equals(bill.getSuitFeeMode())) { if (SuitFeeType.timingList().contains(bill.getSuitFeeType())) { // 分时段计费 - return this.calcTimingAmount(bill, endTime, totalEle); + result = this.calcTimingAmount(bill, endTime, totalEle); } else { // 非分时段计费 - return this.calcSmartAmount(bill, endTime, totalEle); + result = this.calcSmartAmount(bill, endTime, totalEle); } } // 非智能计费 else { - return bill.getMoney(); + result = bill.getMoney(); } + + // 计算会员折扣 + result = VipUtil.calcDiscountAmount(bill.getVipDiscount(), result); + + // 若需要金额 > 订单金额,则返回订单金额 + if (result.compareTo(bill.getMoney()) > 0) { + result = bill.getMoney(); + } + // 若需要的金额 < 0,则返回0 + if (result.compareTo(BigDecimal.ZERO) < 0) { + result = BigDecimal.ZERO; + } + + return result; } @Override @@ -1252,7 +1273,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After // 查询数据 TransactionBill bill = transactionBillMapper.selectSmTransactionBillByBillNo(billNo); - ServiceUtil.assertion(bill == null, "订单不存在"); + ServiceUtil.assertion(bill == null || bill.getBillId() == null, "订单不存在"); ServiceUtil.assertion(!TransactionBillType.RECHARGE.getType().equals(bill.getType()), "非充值订单,不允许取消"); // 允许取消的状态 @@ -1263,11 +1284,20 @@ public class TransactionBillServiceImpl implements TransactionBillService, After ServiceUtil.assertion(!allowCancel.contains(bill.getStatus()), "当前订单状态不允许取消"); Integer result = transactionTemplate.execute(s -> { - // TODO 执行取消订单,改为updateByQuery的形式 - int cancel = transactionBillMapper.cancelRecharge(billNo, status.getStatus()); + // 执行取消订单 + TransactionBill data = new TransactionBill(); + data.setStatus(status.getStatus()); + TransactionBillQuery query = new TransactionBillQuery(); + query.setBillId(bill.getBillId()); + query.setStatusList(allowCancel); + query.setType(TransactionBillType.RECHARGE.getType()); + int cancel = this.updateByQuery(data, query); ServiceUtil.assertion(cancel != 1, "取消订单失败:状态已发生变化"); - // TODO 恢复会员次数 + // 恢复会员次数 + if (bill.getVipId() != null) { + vipService.unUseVip(bill.getVipId()); + } // 取消支付订单 boolean cancelPay = false; @@ -1487,7 +1517,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After } // 处理误差 - handleRefundAmountDiff(refundList, bonusList, dividedAmount, refundAmount); + BonusUtil.handleRefundAmountDiff(refundList, bonusList, dividedAmount, refundAmount); // 校验金额 for (Bonus bonus : refundList) { @@ -1500,58 +1530,6 @@ public class TransactionBillServiceImpl implements TransactionBillService, After return executeRefund(refundList); } - /** - * 处理退款金额分配的误差 - * @param refundList 退款列表 - * @param bonusList 原始分成列表 - * @param dividedAmount 已分配金额 - * @param refundAmount 需要退款总金额 - */ - private void handleRefundAmountDiff(List refundList, List bonusList, BigDecimal dividedAmount, BigDecimal refundAmount) { - if (dividedAmount.compareTo(refundAmount) == 0) { - return; - } - // 误差值 - BigDecimal diff = refundAmount.subtract(dividedAmount); - - // 多个分成方共同承担误差 - BigDecimal remainingDiff = diff; - for (Bonus refundBonus : refundList) { - // 获取原始分成记录 - BonusVO originalBonus = bonusList.stream() - .filter(b -> b.getId().equals(refundBonus.getId())) - .findFirst() - .orElse(null); - - if (originalBonus == null) { - continue; - } - - // 计算当前分成方的剩余可退金额 - BigDecimal availableRefund = originalBonus.getAmount() - .subtract(originalBonus.getRefundAmount() == null ? BigDecimal.ZERO : originalBonus.getRefundAmount()) - .subtract(refundBonus.getRefundAmount()); - - // 如果剩余可退金额足够承担误差 - if (availableRefund.compareTo(BigDecimal.ZERO) > 0) { - // 取可用退款金额与剩余误差中的较小值 - BigDecimal adjustAmount = availableRefund.min(remainingDiff); - refundBonus.setRefundAmount(refundBonus.getRefundAmount().add(adjustAmount)); - remainingDiff = remainingDiff.subtract(adjustAmount); - - // 如果误差已经分配完毕,则退出 - if (remainingDiff.compareTo(BigDecimal.ZERO) == 0) { - return; - } - } - } - - // 如果仍然无法完全分配误差,抛出异常 - if (remainingDiff.compareTo(BigDecimal.ZERO) > 0) { - throw new ServiceException("退款金额分配出错:无法处理误差金额"); - } - } - // 执行分成退款 private int executeRefund(List refundList) { Integer result = transactionTemplate.execute(status -> { diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillValidatorImpl.java b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillValidatorImpl.java index 3cc16b7b..0af94204 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillValidatorImpl.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/transactionBill/service/impl/TransactionBillValidatorImpl.java @@ -203,11 +203,20 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans if (!vipValidator.isStore(vip, store)) { return error(String.format("ID为%s的会员不能用在当前店铺,请重新选择会员", dto.getVipId())); } + if (!vipValidator.isUser(vip, user)) { + return error(String.format("ID为%s的会员不是ID为%s的用户,请重新选择会员", dto.getVipId(), user.getUserId())); + } + // 有效期 if (vipValidator.isInValid(vip)) { return error(String.format("ID为%s的会员已过期,请重新选择会员", dto.getVipId())); } - if (!vipValidator.isUser(vip, user)) { - return error(String.format("ID为%s的会员不是ID为%s的用户,请重新选择会员", dto.getVipId(), user.getUserId())); + // 剩余次数 + if (!vipValidator.hasSurplusCount(vip)) { + return error(String.format("ID为%s的会员已达到最大使用次数,请重新选择会员", dto.getVipId())); + } + // 周期剩余次数 + if (!vipValidator.hasSurplusRoundCount(vip)) { + return error(String.format("ID为%s的会员已达到本周期最大使用次数,请重新选择会员", dto.getVipId())); } } // 价格检查 diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/domain/VipQuery.java b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/domain/VipQuery.java index 3f182148..9371dfd8 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/domain/VipQuery.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/domain/VipQuery.java @@ -1,6 +1,7 @@ package com.ruoyi.ss.vip.domain; import java.math.BigDecimal; +import java.time.LocalDate; import java.util.List; import io.swagger.annotations.ApiModelProperty; @@ -33,4 +34,10 @@ public class VipQuery extends VipVO{ @ApiModelProperty("剩余次数最大值") private BigDecimal surplusCountEnd; + + @ApiModelProperty("需要刷新的日期") + private LocalDate nextResetDate; + + @ApiModelProperty("限制类型列表") + private List limitTypes; } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.java b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.java index ac167268..cb2ad142 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.java +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.java @@ -1,11 +1,12 @@ package com.ruoyi.ss.vip.mapper; +import java.util.List; + +import org.apache.ibatis.annotations.Param; + import com.ruoyi.ss.vip.domain.Vip; import com.ruoyi.ss.vip.domain.VipQuery; import com.ruoyi.ss.vip.domain.VipVO; -import org.apache.ibatis.annotations.Param; - -import java.util.List; /** * 会员Mapper接口 @@ -77,4 +78,14 @@ public interface VipMapper * 逻辑删除 */ int logicDel(@Param("ids") List ids); + + /** + * 使用会员 + */ + int useVip(@Param("id") Long id); + + /** + * 恢复会员次数 + */ + int unUseVip(@Param("id") Long id); } diff --git a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.xml b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.xml index f15c6e86..690ba382 100644 --- a/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.xml +++ b/smart-switch-service/src/main/java/com/ruoyi/ss/vip/mapper/VipMapper.xml @@ -71,6 +71,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" and sv.discount <= #{query.discountEnd} and sv.surplus_count >= #{query.surplusCountStart} and sv.surplus_count <= #{query.surplusCountEnd} + and date(sv.next_reset_time) = #{query.nextResetDate} and @@ -97,6 +98,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" #{item} + + and sv.limit_type in + + #{item} + + +