临时提交

This commit is contained in:
磷叶 2025-01-20 18:00:56 +08:00
parent 7255481fc6
commit 92b327658a
14 changed files with 188 additions and 80 deletions

View File

@ -1,5 +1,14 @@
package com.ruoyi.ss.transactionBill.domain;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonView;
import com.ruoyi.common.annotation.Excel;
@ -8,24 +17,16 @@ import com.ruoyi.common.core.domain.BaseEntity;
import com.ruoyi.common.core.domain.JsonViewProfile;
import com.ruoyi.common.core.domain.ValidGroup;
import com.ruoyi.system.valid.DictValid;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;
import lombok.EqualsAndHashCode;
/**
* 充值记录对象 sm_transaction_bill
*
* @author 邱贞招
* @date 2024-02-21
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class TransactionBill extends BaseEntity
{
private static final long serialVersionUID = 1L;
@ -334,4 +335,24 @@ public class TransactionBill extends BaseEntity
@Excel(name = "渠道类型", dictType = DictTypeConstants.RECHARGE_CHANNEL_TYPE)
@ApiModelProperty("渠道类型")
private String channelType;
@Excel(name = "使用vip的ID")
@ApiModelProperty("使用vip的ID")
private Long vipId;
@Excel(name = "会员折扣")
@ApiModelProperty("会员折扣")
private BigDecimal vipDiscount;
@Excel(name = "会员名称")
@ApiModelProperty("会员名称")
private String vipName;
@Excel(name = "优惠金额")
@ApiModelProperty("优惠金额")
private BigDecimal discountAmount;
@Excel(name = "优惠金额退款")
@ApiModelProperty("优惠金额退款")
private BigDecimal discountRefundAmount;
}

View File

@ -6,6 +6,7 @@ import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.suit.domain.SuitVO;
import com.ruoyi.ss.transactionBill.domain.TransactionBill;
import com.ruoyi.ss.transactionBill.domain.dto.RechargeDTO;
import com.ruoyi.ss.transactionBill.domain.vo.PrepayPriceVO;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.vip.domain.VipVO;
@ -48,4 +49,7 @@ public class RechargeBO {
// VIP信息
private VipVO vip;
// 预付款金额数据
private PrepayPriceVO price;
}

View File

@ -83,6 +83,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
stb.mch_show_mobile_price,
stb.mch_show_mobile_status,
stb.channel_type,
stb.vip_id,
stb.vip_discount,
stb.vip_name,
stb.discount_amount,
stb.discount_refund_amount,
</sql>
<sql id="selectSmTransactionBillVo">
@ -213,7 +218,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="query.suitVoidResult != null and query.suitVoidResult != ''"> and stb.suit_void_result = #{query.suitVoidResult}</if>
<if test="query.openMsg != null and query.openMsg != ''"> and stb.open_msg like concat('%', #{query.openMsg}, '%')</if>
<if test="query.mchShowMobileStatus != null and query.mchShowMobileStatus != ''"> and stb.mch_show_mobile_status = #{query.mchShowMobileStatus}</if>
<if test="query.channelType != null and query.channelType != ''"> and channel_type = #{query.channelType}</if>
<if test="query.channelType != null and query.channelType != ''"> and stb.channel_type = #{query.channelType}</if>
<if test="query.vipId != null "> and stb.vip_id = #{query.vipId}</if>
<if test="query.vipName != null and query.vipName != ''"> and vip_name like concat('%', #{query.vipName}, '%')</if>
<if test="query.isNullSuitEndTime != null">
and stb.suit_end_time is
<if test="!query.isNullSuitEndTime">
@ -607,6 +614,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="mchShowMobilePrice != null">mch_show_mobile_price,</if>
<if test="mchShowMobileStatus != null and mchShowMobileStatus != ''">mch_show_mobile_status,</if>
<if test="channelType != null and channelType != ''">channel_type,</if>
<if test="vipId != null">vip_id,</if>
<if test="vipDiscount != null">vip_discount,</if>
<if test="vipName != null">vip_name,</if>
<if test="discountAmount != null">discount_amount,</if>
<if test="discountRefundAmount != null">discount_refund_amount,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="billNo != null">#{billNo},</if>
@ -678,6 +690,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="mchShowMobilePrice != null">#{mchShowMobilePrice},</if>
<if test="mchShowMobileStatus != null and mchShowMobileStatus != ''">#{mchShowMobileStatus},</if>
<if test="channelType != null and channelType != ''">#{channelType},</if>
<if test="vipId != null">#{vipId},</if>
<if test="vipDiscount != null">#{vipDiscount},</if>
<if test="vipName != null">#{vipName},</if>
<if test="discountAmount != null">#{discountAmount},</if>
<if test="discountRefundAmount != null">#{discountRefundAmount},</if>
</trim>
</insert>
@ -765,6 +782,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="data.mchShowMobilePrice != null">stb.mch_show_mobile_price = #{data.mchShowMobilePrice},</if>
<if test="data.mchShowMobileStatus != null and data.mchShowMobileStatus != ''">stb.mch_show_mobile_status = #{data.mchShowMobileStatus},</if>
<if test="data.channelType != null and data.channelType != ''">stb.channel_type = #{data.channelType},</if>
<if test="data.vipId != null">stb.vip_id = #{data.vipId},</if>
<if test="data.vipDiscount != null">stb.vip_discount = #{data.vipDiscount},</if>
<if test="data.vipName != null">stb.vip_name = #{data.vipName},</if>
<if test="data.discountAmount != null">stb.discount_amount = #{data.discountAmount},</if>
<if test="data.discountRefundAmount != null">stb.discount_refund_amount = #{data.discountRefundAmount},</if>
</sql>
<update id="updateByQuery">

View File

@ -3,6 +3,7 @@ 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;
@ -122,6 +123,10 @@ public class TransactionBillConverterImpl implements TransactionBillConverter {
bo.setPlatform(deptService.selectDeptById(Constants.ROOT_DEPT));
bo.setAgent(userService.selectSmUserByUserId(device.getAgentId()));
bo.setVip(vipService.selectVipById(dto.getVipId()));
if (bo.getVip() != null && bo.getSuit() != null) {
bo.setPrice(RechargeUtils.calcRechargePrePayPrice(bo.getVip(), bo.getSuit()));
}
return bo;
}

View File

@ -102,6 +102,7 @@ import com.ruoyi.ss.transactionBill.service.TransactionAssembler;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.ss.transactionBill.service.TransactionBillValidator;
import com.ruoyi.ss.transactionBill.service.WithdrawValidator;
import com.ruoyi.ss.transactionBill.utils.RechargeUtils;
import com.ruoyi.ss.transfer.domain.TransferVO;
import com.ruoyi.ss.transfer.interfaces.AfterTransfer;
import com.ruoyi.ss.transfer.service.TransferConverter;
@ -195,6 +196,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
@Autowired
private VipService vipService;
private VipVO vip;
/**
* 查询充值记录
*
@ -298,6 +301,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
int monthFee = receiveBillService.genBillByMonthAndPay(bo.getMch(), bo.getDevice());
ServiceUtil.assertion(monthFee != 1, "月费收取失败,请联系商户处理");
// TODO 扣减会员次数,记录会员使用记录
// 新增订单
int insert = this.insertSmTransactionBill(order);
ServiceUtil.assertion(insert != 1, "下单失败");
@ -407,6 +412,8 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
SuitVO suit = bo.getSuit();
SmUserVO user = bo.getUser();
StoreVo store = bo.getStore();
PrepayPriceVO price = bo.getPrice();
VipVO vip = bo.getVip();
// 创建订单
TransactionBill order = new TransactionBill();
@ -419,15 +426,15 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
order.setCloseStatus(RechargeCloseStatus.SUCCESS.getStatus());
order.setCloseResult("预计成功");
order.setStatus(TransactionBillStatus.UNPAID.getStatus()); // 状态未支付
// 订单金额
order.setMoney(price.getPayPrice());
order.setDiscountAmount(price.getDiscountAmount());
// 订单金额若为智能收费则收取押金否则收取本金
if (SuitFeeMode.SMART.getMode().equals(suit.getFeeMode())) {
// 智能收费收取押金
order.setMoney(dto.getSuitDeposit());
order.setSuitDeposit(dto.getSuitDeposit());
} else {
// 单次收费收取本金
order.setMoney(dto.getMoney());
// 会员信息
if (vip != null) {
order.setVipId(vip.getId());
order.setVipDiscount(vip.getDiscount());
order.setVipName(vip.getVipLevelName());
}
// 用户信息
@ -454,6 +461,7 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
order.setSuitLowPower(suit.getLowPower());
order.setSuitEnabledVoid(suit.getEnabledVoice());
order.setSuitVoidMinute(suit.getVoiceMinutes());
order.setSuitDeposit(suit.getDeposit());
// 设备信息
order.setDeviceNo(device.getDeviceNo());
@ -1254,15 +1262,13 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
);
ServiceUtil.assertion(!allowCancel.contains(bill.getStatus()), "当前订单状态不允许取消");
// 若是未支付的分时订单则不允许取消
// ServiceUtil.assertion(TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus())
// && SuitFeeType.timingList().contains(bill.getSuitFeeType()), "未支付的分时段订单不允许取消");
Integer result = transactionTemplate.execute(s -> {
// 执行取消订单
// TODO 执行取消订单改为updateByQuery的形式
int cancel = transactionBillMapper.cancelRecharge(billNo, status.getStatus());
ServiceUtil.assertion(cancel != 1, "取消订单失败:状态已发生变化");
// TODO 恢复会员次数
// 取消支付订单
boolean cancelPay = false;
if (TransactionBillStatus.UNPAID.getStatus().equals(bill.getStatus())) {
@ -1822,27 +1828,6 @@ public class TransactionBillServiceImpl implements TransactionBillService, After
SuitVO suit = suitService.selectSuitBySuitId(suitId);
// 获取会员
VipVO vip = vipService.selectVipById(vipId);
return this.calcRechargePrePayPrice(vip, suit);
}
private PrepayPriceVO calcRechargePrePayPrice(VipVO vip, SuitVO suit) {
ServiceUtil.assertion(suit == null, "套餐不存在");
// 结果
PrepayPriceVO vo = new PrepayPriceVO();
// 原价
vo.setOriginalPrice(suit.getDeposit());
// 优惠价
if (vip != null && vip.getDiscount() != null) {
// 优惠金额
BigDecimal discountAmount = vip.getDiscount()
.multiply(suit.getDeposit())
.divide(BigDecimal.valueOf(10), 2, RoundingMode.HALF_UP);
vo.setDiscountAmount(discountAmount);
} else {
vo.setDiscountAmount(BigDecimal.ZERO);
}
// 应付金额
vo.setPayPrice(vo.getOriginalPrice().subtract(vo.getDiscountAmount()));
return vo;
return RechargeUtils.calcRechargePrePayPrice(vip, suit);
}
}

View File

@ -15,6 +15,7 @@ import com.ruoyi.common.core.domain.ValidateResult;
import com.ruoyi.common.enums.ServiceCode;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.MathUtils;
import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.collection.CollectionUtils;
@ -41,6 +42,7 @@ import com.ruoyi.ss.transactionBill.domain.dto.RechargeDTO;
import com.ruoyi.ss.transactionBill.domain.dto.RechargePayBO;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillStatus;
import com.ruoyi.ss.transactionBill.domain.enums.TransactionBillType;
import com.ruoyi.ss.transactionBill.domain.vo.PrepayPriceVO;
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.transactionBill.service.TransactionBillService;
import com.ruoyi.ss.transactionBill.service.TransactionBillValidator;
@ -193,29 +195,33 @@ public class TransactionBillValidatorImpl extends BaseValidator implements Trans
return error("套餐收费方式已发生变化,请重新下单");
}
// 会员检查
if (dto.getVipId() != null) {
VipVO vip = bo.getVip();
// 判断会员是否有效
// 判断会员是否可以用在当前店铺
if (dto.getVipId() != null && vip != null) {
if (!vipValidator.canUse(vip, store)) {
if (vip == null) {
return error(String.format("ID为%s的会员不存在", dto.getVipId()));
}
if (!vipValidator.isStore(vip, store)) {
return error(String.format("ID为%s的会员不能用在当前店铺请重新选择会员", dto.getVipId()));
}
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()));
}
}
// 价格检查
PrepayPriceVO price = bo.getPrice();
if (price == null) {
return error("价格计算失败");
}
if (!MathUtils.equals(price.getPayPrice(), dto.getMoney())) {
return error(String.format("实付金额发生变化,请刷新后重试。传入值:%s当前值%s", dto.getMoney(), price.getPayPrice()));
}
if (!MathUtils.equals(price.getDiscountAmount(), dto.getDiscountAmount())) {
return error(String.format("优惠金额发生变化,请刷新后重试。传入值:%s当前值%s", dto.getDiscountAmount(), price.getDiscountAmount()));
}
// if (SuitFeeMode.SMART.getMode().equals(suit.getFeeMode())) {
// if (device.getExpireTime() != null && device.getExpireTime().isAfter(LocalDateTime.now())) {
// return error("当前设备还有剩余时长,无法选择智能收费方式下单");
// }
// if (DeviceOnlineStatus.ONLINE.getStatus().equals(device.getOnlineStatus())
// && device.getSurplusEle() != null
// && device.getSurplusEle().compareTo(BigDecimal.ZERO) > 0) {
// return error("当前设备还有剩余电量,无法选择智能收费方式下单");
// }
// }
return success();
}

View File

@ -1,17 +1,21 @@
package com.ruoyi.ss.transactionBill.utils;
import com.ruoyi.common.utils.collection.CollectionUtils;
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.vo.TransactionBillVO;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.suit.domain.SuitVO;
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.vo.PrepayPriceVO;
import com.ruoyi.ss.transactionBill.domain.vo.TransactionBillVO;
import com.ruoyi.ss.vip.domain.VipVO;
import com.ruoyi.ss.vip.utils.VipUtil;
/**
* @author wjh
* 2024/12/24
@ -159,4 +163,34 @@ public class RechargeUtils {
return bill.getSuitGearAmount().get(bill.getSuitGearTime().get(hour));
}
public static PrepayPriceVO calcRechargePrePayPrice(VipVO vip, SuitVO suit) {
if (suit == null) {
return null;
}
// 结果
PrepayPriceVO vo = new PrepayPriceVO();
// 原价
if (SuitFeeMode.SMART.getMode().equals(suit.getFeeMode())) {
// 智能收费收取押金
vo.setOriginalPrice(suit.getDeposit());
} else {
// 单次收费收取本金
vo.setOriginalPrice(suit.getPrice());
}
vo.setPayPrice(vo.getOriginalPrice());
// 计算优惠后的金额
if (vip != null) {
BigDecimal payPrice = VipUtil.calcDiscountAmount(vip.getDiscount(), vo.getPayPrice());
vo.setPayPrice(payPrice);
}
// 计算已优惠金额
BigDecimal discountAmount = vo.getOriginalPrice().subtract(vo.getPayPrice());
vo.setDiscountAmount(discountAmount);
return vo;
}
}

View File

@ -31,6 +31,6 @@ public class VipVO extends Vip{
@ApiModelProperty("是否在有效期内")
private Boolean inValid;
@ApiModelProperty("是否被限制")
@ApiModelProperty("是否被限制")
private Boolean inLimit;
}

View File

@ -11,10 +11,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
(sv.start_time &lt;= now() and sv.end_time &gt;= now())
</sql>
<!-- 是否在限制次数内 -->
<!-- 是否被限制 -->
<sql id="inLimit">
(
sv.limit_type != '1' and sv.limit_count > sv.round_count
sv.limit_type != '1' and sv.round_count >= sv.limit_count
)
</sql>

View File

@ -3,6 +3,7 @@ package com.ruoyi.ss.vip.service;
import java.util.List;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.vip.domain.Vip;
import com.ruoyi.ss.vip.domain.VipVO;
@ -35,7 +36,12 @@ public interface VipValidator {
/**
* 是否允许使用在当前店铺
*/
boolean canUse(VipVO vip, StoreVo store);
boolean isStore(VipVO vip, StoreVo store);
/**
* 是否允许使用在当前用户
*/
boolean isUser(VipVO vip, SmUserVO user);
/**
* 是否在有效期内

View File

@ -11,6 +11,7 @@ import com.ruoyi.common.utils.ServiceUtil;
import com.ruoyi.common.utils.collection.CollectionUtils;
import com.ruoyi.ss.store.domain.StoreVo;
import com.ruoyi.ss.store.service.StoreValidator;
import com.ruoyi.ss.user.domain.SmUserVO;
import com.ruoyi.ss.user.service.UserValidator;
import com.ruoyi.ss.vip.domain.Vip;
import com.ruoyi.ss.vip.domain.VipQuery;
@ -91,10 +92,21 @@ public class VipValidatorImpl implements VipValidator {
}
@Override
public boolean canUse(VipVO vip, StoreVo store) {
public boolean isStore(VipVO vip, StoreVo store) {
return vip != null && store != null && Objects.equals(vip.getStoreId(), store.getStoreId());
}
/**
* 是否允许使用在当前用户
*
* @param vip
* @param user
*/
@Override
public boolean isUser(VipVO vip, SmUserVO user) {
return vip != null && user != null && Objects.equals(vip.getUserId(), user.getUserId());
}
@Override
public boolean isInValid(VipVO vip) {
LocalDateTime now = LocalDateTime.now();

View File

@ -1,10 +1,12 @@
package com.ruoyi.ss.vip.utils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.ss.vipLevel.domain.enums.VipLevelLimitType;
import java.time.LocalDateTime;
/**
* @author wjh
* 2025/1/18
@ -28,4 +30,15 @@ public class VipUtil {
return null;
}
/**
* 计算折扣后的价格
*/
public static BigDecimal calcDiscountAmount(BigDecimal discount, BigDecimal originalPrice) {
if (discount == null || originalPrice == null) {
return originalPrice;
}
return originalPrice.multiply(discount).divide(BigDecimal.valueOf(10), 2, RoundingMode.HALF_DOWN);
}
}

View File

@ -60,10 +60,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
where svl.id = #{id}
</select>
<insert id="insertVipLevelSku" parameterType="VipLevel">
<insert id="insertVipLevelSku" parameterType="VipLevel" useGeneratedKeys="true" keyProperty="id">
insert into ss_vip_level
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="price != null">price,</if>
<if test="time != null">`time`,</if>
<if test="createTime != null">create_time,</if>
@ -81,7 +80,6 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="deleted != null">deleted,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="price != null">#{price},</if>
<if test="time != null">#{time},</if>
<if test="createTime != null">#{createTime},</if>

View File

@ -7,6 +7,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Objects;
import com.ruoyi.ss.transactionBill.domain.bo.RechargeBO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.validation.annotation.Validated;
@ -218,7 +219,8 @@ public class AppTransactionBillController extends BaseController
@ApiOperation("创建订单")
@PostMapping("/recharge")
public AjaxResult addRecharge(@RequestBody @Validated RechargeDTO dto) {
return AjaxResult.success("操作成功", transactionBillService.addOrder(transactionBillConverter.toRechargeBO(dto)));
RechargeBO bo = transactionBillConverter.toRechargeBO(dto);
return AjaxResult.success("操作成功", transactionBillService.addOrder(bo));
}
@ApiOperation("下单前获取订单预存金额")